From 8c987ea0b424bb6117fa4abdb193bc7219eaecfe Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Tue, 20 Aug 2024 14:19:19 +1000 Subject: [PATCH 1/5] Add the ability to extend Docker configuration This adds the ability for modules to extend the docker-compose.yml configuration with sidecar containers (or override existing configuration). --- inc/composer/class-command.php | 47 ++++++++++++++++++- inc/composer/class-compose-extension.php | 31 ++++++++++++ .../class-docker-compose-generator.php | 35 ++++++++++---- 3 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 inc/composer/class-compose-extension.php diff --git a/inc/composer/class-command.php b/inc/composer/class-command.php index ff687d5c..e9e4b144 100644 --- a/inc/composer/class-command.php +++ b/inc/composer/class-command.php @@ -29,6 +29,10 @@ * @package altis/local-server */ class Command extends BaseCommand { + /** + * Package-specific configuration. + */ + protected array $package_config; /** * Command configuration. @@ -116,6 +120,40 @@ private function get_base_command_prefix() : string { ); } + /** + * 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'] ) ) { + 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.) + if ( ! empty( $config ) ) { + require $this->getApplication()->getComposer()->getConfig()->get( 'vendor-dir' ) . '/autoload.php'; + } + + return $config; + } + /** * Execute the given command. * @@ -996,7 +1034,14 @@ protected function check_host_entries( InputInterface $input, OutputInterface $o * @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', diff --git a/inc/composer/class-compose-extension.php b/inc/composer/class-compose-extension.php new file mode 100644 index 00000000..57c6b79a --- /dev/null +++ b/inc/composer/class-compose-extension.php @@ -0,0 +1,31 @@ +project_name = $project_name; $this->bucket_name = "s3-{$this->project_name}"; $this->config_dir = dirname( __DIR__, 2 ) . '/docker'; @@ -89,6 +95,7 @@ public function __construct( string $root_dir, string $project_name, string $tld $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 @@ public function get_array() : array { } } + // Initialize plugins and run them. + if ( ! empty( $this->extra ) ) { + foreach ( $this->extra as $package_spec ) { + /** @var Compose_Extension */ + $handler = new $package_spec['compose-extension'](); + $handler->set_config( $this, $this->args ); + $config = $handler->filter_compose( $config ); + } + } + return $config; } From b3eb74e3f0091b705c679c815b60a294061ae5d2 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Tue, 20 Aug 2024 15:35:47 +1000 Subject: [PATCH 2/5] Add documentation for specifying containers --- docs/extra-containers.md | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/extra-containers.md diff --git a/docs/extra-containers.md b/docs/extra-containers.md new file mode 100644 index 00000000..05fe963d --- /dev/null +++ b/docs/extra-containers.md @@ -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'] = [ + '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" + } + } + } +} +``` From 6ddad37ce242b1a293f3f2da17fd7742d9bdd552 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 17 Oct 2024 11:34:53 +0100 Subject: [PATCH 3/5] Wrap the docs to 132 chars --- docs/extra-containers.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/extra-containers.md b/docs/extra-containers.md index 05fe963d..2b84dc8e 100644 --- a/docs/extra-containers.md +++ b/docs/extra-containers.md @@ -3,27 +3,37 @@ 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. +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.) +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. +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/). +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. +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. +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. +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. +**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`: From 1c68efbbf15826566806fec2eee67be93a266174 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 17 Oct 2024 11:35:32 +0100 Subject: [PATCH 4/5] Change negative checks to positive for readability --- inc/composer/class-command.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/composer/class-command.php b/inc/composer/class-command.php index e9e4b144..5ec3e78d 100644 --- a/inc/composer/class-command.php +++ b/inc/composer/class-command.php @@ -137,7 +137,7 @@ protected function get_package_configs() : array { $config = []; foreach ( $packages as $name => $package ) { $extra = $package->getExtra(); - if ( ! isset( $extra['altis'] ) || ! isset( $extra['altis']['local-server'] ) ) { + if ( empty( $extra['altis'] ) || empty( $extra['altis']['local-server'] ) ) { continue; } From e0ee43d4defe21b9e090fb6d2c83248f4ef41cd2 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Mon, 2 Dec 2024 23:39:05 +0200 Subject: [PATCH 5/5] Update docs/extra-containers.md Co-authored-by: Konstantin Kovshenin --- docs/extra-containers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extra-containers.md b/docs/extra-containers.md index 2b84dc8e..466a7eb0 100644 --- a/docs/extra-containers.md +++ b/docs/extra-containers.md @@ -50,7 +50,7 @@ class Local_Server_Extension implements Compose_Extension { } public function filter_compose( array $config ) : array { - $config['services']'foo'] = [ + $config['services']['foo'] = [ 'container_name' => "{$this->generator->project_name}-foo", 'image' => 'hello-world', ];