-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to modify docker-compose configuration #722
base: master
Are you sure you want to change the base?
Changes from 2 commits
8c987ea
b3eb74e
6ddad37
1c68efb
e0ee43d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
--- | ||
order: 100 | ||
--- | ||
# Extra Containers | ||
|
||
Local Server provides a series of Docker containers for the base Altis behaviour. Modules and other packages may add additional containers (typically called "sidecar" containers) or alter the container behaviour through the extension system. | ||
|
||
|
||
## How Local Server works | ||
|
||
Local Server internally uses Docker Compose to create and manage containers for the Altis environment. When a user runs the `composer server start` command, Local Server dynamically provisions a `docker-compose.yml` file based on the user's preferences. (This file is only regenerated when starting Local Server to avoid conflicts or surprising behaviour for users.) | ||
|
||
Other commands such as `composer server logs` are passthrough-style commands, which primarily wrap `docker-compose` commands, and which work with Docker Compose server names. | ||
|
||
Domain routing is internally handled using [Traefik Proxy](https://doc.traefik.io/traefik/) and each service can register user-accessible routes using the [label configuration system](https://doc.traefik.io/traefik/providers/docker/). | ||
|
||
Extensions register a class which has the ability to filter the `docker-compose.yml` data before it is written out to a file, allowing the extensions to register additional services and containers, or modify the configuration in other ways. | ||
|
||
|
||
## Writing an extension | ||
|
||
Any Composer package (including the root package, i.e. the project) can specify an extension class, which is dynamically loaded by Local Server and which receives the `docker-compose.yml` data to filter. | ||
|
||
Packages should implement the [`Altis\Local_Server\Composer\Compose_Extension` interface](https://github.com/humanmade/altis-local-server/blob/master/inc/composer/class-compose-extension.php) with the relevant methods. | ||
|
||
**Note:** This class will be loaded in the Composer context, not in WordPress, and neither hooks nor the full codebase will be loaded. Notably, functions like `Altis\get_config()` will not return default values as a result. | ||
|
||
For example, to add a sidecar service called `foo`: | ||
|
||
```php | ||
namespace MyModule; | ||
|
||
use Altis\Local_Server\Composer\{Compose_Extension, Docker_Compose_Generator}; | ||
|
||
class Local_Server_Extension implements Compose_Extension { | ||
protected Docker_Compose_Generator $generator; | ||
|
||
public function set_config( Docker_Compose_Generator $generator, array $args ) : void { | ||
$this->generator = $generator; | ||
} | ||
|
||
public function filter_compose( array $config ) : array { | ||
$config['services']'foo'] = [ | ||
joehoyle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
'container_name' => "{$this->generator->project_name}-foo", | ||
'image' => 'hello-world', | ||
]; | ||
|
||
return $config; | ||
} | ||
``` | ||
|
||
This class should then be specified in the package's `composer.json` as `extra.altis.local-server.compose-extension`: | ||
|
||
```json | ||
{ | ||
"extra": { | ||
"altis": { | ||
"local-server": { | ||
"compose-extension": "Altis\\Enhanced_Search\\Local_Server_Extension" | ||
} | ||
} | ||
} | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -29,6 +29,10 @@ | |||||||||
* @package altis/local-server | ||||||||||
*/ | ||||||||||
class Command extends BaseCommand { | ||||||||||
/** | ||||||||||
* Package-specific configuration. | ||||||||||
*/ | ||||||||||
Check failure on line 34 in inc/composer/class-command.php HM Linter / hmlinterinc/composer/class-command.php#L34
Raw output
|
||||||||||
protected array $package_config; | ||||||||||
Check failure on line 35 in inc/composer/class-command.php HM Linter / hmlinterinc/composer/class-command.php#L35
Raw output
|
||||||||||
|
||||||||||
/** | ||||||||||
* Command configuration. | ||||||||||
|
@@ -116,6 +120,40 @@ | |||||||||
); | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Get all extra configs added by all the packages. | ||||||||||
* | ||||||||||
* @return array Extra configurations added by packages (e.g. handlers). | ||||||||||
*/ | ||||||||||
protected function get_package_configs() : array { | ||||||||||
// Fetch all the packages. | ||||||||||
$repo = $this->getApplication()->getComposer()->getLocker()->getLockedRepository( true ); | ||||||||||
$packages = []; | ||||||||||
foreach ( $repo->getPackages() as $package ) { | ||||||||||
$packages[ $package->getName() ] = $package; | ||||||||||
} | ||||||||||
|
||||||||||
// Check which are publishing containers. | ||||||||||
$config = []; | ||||||||||
foreach ( $packages as $name => $package ) { | ||||||||||
$extra = $package->getExtra(); | ||||||||||
if ( ! isset( $extra['altis'] ) || ! isset( $extra['altis']['local-server'] ) ) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be clearer as a positive test:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd agree if it were a straight There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe |
||||||||||
continue; | ||||||||||
} | ||||||||||
|
||||||||||
$config[ $name ] = $extra['altis']['local-server']; | ||||||||||
} | ||||||||||
|
||||||||||
// If we have any packages, ensure we have all the autoloaders loaded. | ||||||||||
// (By default, Composer only loads composer-plugin packages and their | ||||||||||
// dependencies.) | ||||||||||
Check failure on line 149 in inc/composer/class-command.php HM Linter / hmlinterinc/composer/class-command.php#L149
Raw output
|
||||||||||
if ( ! empty( $config ) ) { | ||||||||||
require $this->getApplication()->getComposer()->getConfig()->get( 'vendor-dir' ) . '/autoload.php'; | ||||||||||
} | ||||||||||
|
||||||||||
return $config; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Execute the given command. | ||||||||||
* | ||||||||||
|
@@ -996,7 +1034,14 @@ | |||||||||
* @return void | ||||||||||
*/ | ||||||||||
protected function generate_docker_compose( array $args = [] ) : void { | ||||||||||
$docker_compose = new Docker_Compose_Generator( getcwd(), $this->get_project_subdomain(), $this->get_project_tld(), $this->get_project_url(), $args ); | ||||||||||
$docker_compose = new Docker_Compose_Generator( | ||||||||||
getcwd(), | ||||||||||
$this->get_project_subdomain(), | ||||||||||
$this->get_project_tld(), | ||||||||||
$this->get_project_url(), | ||||||||||
$args, | ||||||||||
$this->get_package_configs() | ||||||||||
); | ||||||||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents | ||||||||||
file_put_contents( | ||||||||||
getcwd() . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'docker-compose.yml', | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
Check failure on line 1 in inc/composer/class-compose-extension.php HM Linter / hmlinterinc/composer/class-compose-extension.php#L1
Raw output
|
||
|
||
namespace Altis\Local_Server\Composer; | ||
|
||
/** | ||
* Local Server docker-compose extension. | ||
* | ||
* This interface allows modules to alter the Local Server configuration for | ||
* Docker Compose, such as adding additional sidecar containers. | ||
*/ | ||
interface Compose_Extension { | ||
/** | ||
* Configure the extension. | ||
* | ||
* @param Docker_Compose_Generator $generator The root generator. | ||
* @param array $args An optional array of arguments to modify the behaviour of the generator. | ||
*/ | ||
public function set_config( Docker_Compose_Generator $generator, array $args ) : void; | ||
|
||
/** | ||
* Filter the docker-compose.yml config. | ||
* | ||
* This method is supplied with the full configuration for docker-compose | ||
* before it is saved to a file. Handlers can filter this value and return | ||
* an updated config, such as adding additional services. | ||
* | ||
* @param array $config Full docker-compose.yml configuration. | ||
* @return array Altered docker-compose.yml configuration. | ||
*/ | ||
public function filter_compose( array $config ) : array; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,56 +20,61 @@ | |
* | ||
* @var string | ||
*/ | ||
protected $project_name; | ||
public $project_name; | ||
|
||
/** | ||
* The S3 bucket name. | ||
* | ||
* @var string | ||
*/ | ||
protected $bucket_name; | ||
public $bucket_name; | ||
|
||
/** | ||
* The Altis project root directory. | ||
* | ||
* @var string | ||
*/ | ||
protected $root_dir; | ||
public $root_dir; | ||
|
||
/** | ||
* The docker-compose.yml directory. | ||
* | ||
* @var string | ||
*/ | ||
protected $config_dir; | ||
public $config_dir; | ||
|
||
/** | ||
* The primary top level domain for the server. | ||
* | ||
* @var string | ||
*/ | ||
protected $tld; | ||
public $tld; | ||
|
||
/** | ||
* The primary domain name for the project. | ||
* | ||
* @var string | ||
*/ | ||
protected $hostname; | ||
public $hostname; | ||
|
||
/** | ||
* The client facing domain name for the project. | ||
* | ||
* @var string | ||
*/ | ||
protected $url; | ||
public $url; | ||
|
||
/** | ||
* An array of data passed to | ||
* | ||
* @var array | ||
*/ | ||
protected $args; | ||
public $args; | ||
|
||
/** | ||
* Extra configuration from packages. | ||
*/ | ||
Check failure on line 76 in inc/composer/class-docker-compose-generator.php HM Linter / hmlinterinc/composer/class-docker-compose-generator.php#L76
Raw output
|
||
protected $extra; | ||
|
||
/** | ||
* Create and configure the generator. | ||
|
@@ -79,8 +84,9 @@ | |
* @param string $tld The primary top level domain for the server. | ||
* @param string $url The client facing URL. | ||
* @param array $args An optional array of arguments to modify the behaviour of the generator. | ||
* @param array $extra Extra configuration from packages. | ||
*/ | ||
public function __construct( string $root_dir, string $project_name, string $tld, string $url, array $args = [] ) { | ||
public function __construct( string $root_dir, string $project_name, string $tld, string $url, array $args = [], array $extra = [] ) { | ||
$this->project_name = $project_name; | ||
$this->bucket_name = "s3-{$this->project_name}"; | ||
$this->config_dir = dirname( __DIR__, 2 ) . '/docker'; | ||
|
@@ -89,6 +95,7 @@ | |
$this->hostname = $this->tld ? $this->project_name . '.' . $this->tld : $this->project_name; | ||
$this->url = $url; | ||
$this->args = $args; | ||
$this->extra = $extra; | ||
} | ||
|
||
/** | ||
|
@@ -923,6 +930,16 @@ | |
} | ||
} | ||
|
||
// Initialize plugins and run them. | ||
if ( ! empty( $this->extra ) ) { | ||
foreach ( $this->extra as $package_spec ) { | ||
/** @var Compose_Extension */ | ||
Check failure on line 936 in inc/composer/class-docker-compose-generator.php HM Linter / hmlinterinc/composer/class-docker-compose-generator.php#L936
Raw output
|
||
$handler = new $package_spec['compose-extension'](); | ||
$handler->set_config( $this, $this->args ); | ||
$config = $handler->filter_compose( $config ); | ||
} | ||
} | ||
|
||
return $config; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you also wrap lines at 132 chars please? As per our MD linting standards.