diff --git a/composer.json b/composer.json index 0fb17f2b..001d2422 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/magento-cloud-docker", "description": "Magento Cloud Docker", "type": "magento2-component", - "version": "1.4.4", + "version": "1.4.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/config/services.xml b/config/services.xml index 319f8350..b3eea275 100644 --- a/config/services.xml +++ b/config/services.xml @@ -38,6 +38,7 @@ + diff --git a/images/mailhog/1.0/Dockerfile b/images/mailhog/1.0/Dockerfile index f7d70d66..87b64951 100644 --- a/images/mailhog/1.0/Dockerfile +++ b/images/mailhog/1.0/Dockerfile @@ -2,17 +2,13 @@ # MailHog Dockerfile # -FROM golang:1.17.9-alpine +FROM golang:1.24-alpine -# Install MailHog: -RUN apk --no-cache add --virtual build-dependencies \ - git \ - && mkdir -p /root/gocode \ - && export GOPATH=/root/gocode \ - && go get github.com/mailhog/MailHog@v1.0.1 \ - && mv /root/gocode/bin/MailHog /usr/local/bin \ - && rm -rf /root/gocode \ - && apk del --purge build-dependencies +# Install MailHog +RUN apk --no-cache add git \ + && go install github.com/mailhog/MailHog@v1.0.1 \ + && mv /go/bin/MailHog /usr/local/bin/MailHog \ + && apk del git # Add mailhog user/group with uid/gid 1000. # This is a workaround for boot2docker issue #581, see diff --git a/images/php/8.1-cli/Dockerfile b/images/php/8.1-cli/Dockerfile index d810cd61..407263db 100644 --- a/images/php/8.1-cli/Dockerfile +++ b/images/php/8.1-cli/Dockerfile @@ -56,7 +56,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -65,7 +64,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.1-fpm/Dockerfile b/images/php/8.1-fpm/Dockerfile index f9f544dd..4da8e6e2 100644 --- a/images/php/8.1-fpm/Dockerfile +++ b/images/php/8.1-fpm/Dockerfile @@ -37,7 +37,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -46,7 +45,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.2-cli/Dockerfile b/images/php/8.2-cli/Dockerfile index 54e8f1a6..f8f3114c 100644 --- a/images/php/8.2-cli/Dockerfile +++ b/images/php/8.2-cli/Dockerfile @@ -56,7 +56,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -65,7 +64,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.2-fpm/Dockerfile b/images/php/8.2-fpm/Dockerfile index 9a3a83da..9fc37b02 100644 --- a/images/php/8.2-fpm/Dockerfile +++ b/images/php/8.2-fpm/Dockerfile @@ -37,7 +37,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -46,7 +45,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.3-cli/Dockerfile b/images/php/8.3-cli/Dockerfile index d4154a5f..e5d0addc 100644 --- a/images/php/8.3-cli/Dockerfile +++ b/images/php/8.3-cli/Dockerfile @@ -56,7 +56,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -65,7 +64,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.3-fpm/Dockerfile b/images/php/8.3-fpm/Dockerfile index d1342ab1..5872837e 100644 --- a/images/php/8.3-fpm/Dockerfile +++ b/images/php/8.3-fpm/Dockerfile @@ -37,7 +37,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -46,7 +45,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.4-cli/Dockerfile b/images/php/8.4-cli/Dockerfile index d04d9411..5235cc19 100644 --- a/images/php/8.4-cli/Dockerfile +++ b/images/php/8.4-cli/Dockerfile @@ -77,7 +77,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -86,7 +85,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/images/php/8.4-fpm/Dockerfile b/images/php/8.4-fpm/Dockerfile index 1e610bef..e377d59d 100644 --- a/images/php/8.4-fpm/Dockerfile +++ b/images/php/8.4-fpm/Dockerfile @@ -58,7 +58,6 @@ RUN apt-get update \ gnupg2 \ ca-certificates \ lsb-release \ - software-properties-common \ libbz2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -67,7 +66,7 @@ RUN apt-get update \ libgpgme11-dev \ libicu-dev \ libldap2-dev \ - libpcre3-dev \ + libpcre2-dev \ libpspell-dev \ libtidy-dev \ libxslt1-dev \ diff --git a/src/Compose/BuilderInterface.php b/src/Compose/BuilderInterface.php index a31a394b..1e527d9c 100644 --- a/src/Compose/BuilderInterface.php +++ b/src/Compose/BuilderInterface.php @@ -28,6 +28,7 @@ interface BuilderInterface public const SERVICE_SELENIUM = ServiceInterface::SERVICE_SELENIUM; public const SERVICE_TLS = ServiceInterface::SERVICE_TLS; public const SERVICE_RABBITMQ = ServiceInterface::SERVICE_RABBITMQ; + public const SERVICE_ACTIVEMQ_ARTEMIS = ServiceInterface::SERVICE_ACTIVEMQ_ARTEMIS; public const SERVICE_REDIS = ServiceInterface::SERVICE_REDIS; public const SERVICE_VALKEY = ServiceInterface::SERVICE_VALKEY; public const SERVICE_ELASTICSEARCH = ServiceInterface::SERVICE_ELASTICSEARCH; diff --git a/src/Compose/ProductionBuilder/Service/ActiveMqArtemis.php b/src/Compose/ProductionBuilder/Service/ActiveMqArtemis.php new file mode 100644 index 00000000..0126f33b --- /dev/null +++ b/src/Compose/ProductionBuilder/Service/ActiveMqArtemis.php @@ -0,0 +1,78 @@ +serviceFactory = $serviceFactory; + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return BuilderInterface::SERVICE_ACTIVEMQ_ARTEMIS; + } + + /** + * @inheritDoc + */ + public function getServiceName(): string + { + return $this->getName(); + } + + /** + * @inheritDoc + */ + public function getConfig(Config $config): array + { + return $this->serviceFactory->create( + $this->getServiceName(), + $config->getServiceVersion($this->getServiceName()), + [], + $config->getServiceImage($this->getServiceName()), + $config->getCustomRegistry() + ); + } + + /** + * @inheritDoc + */ + public function getNetworks(): array + { + return [BuilderInterface::NETWORK_MAGENTO]; + } + + /** + * @inheritDoc + */ + public function getDependsOn(Config $config): array + { + return []; + } +} diff --git a/src/Config/Relationship.php b/src/Config/Relationship.php index c22e4c3e..eb8ed363 100644 --- a/src/Config/Relationship.php +++ b/src/Config/Relationship.php @@ -81,6 +81,15 @@ class Relationship 'password' => 'guest', ] ], + 'activemq-artemis' => [ + [ + 'host' => 'activemq-artemis', + 'port' => '61616', + 'username' => 'admin', + 'password' => 'admin', + 'web_console_port' => '8161', + ] + ], 'zookeeper' => [ [ 'host' => 'zookeeper', diff --git a/src/Config/Source/CloudSource.php b/src/Config/Source/CloudSource.php index 2e9e3712..9b272026 100644 --- a/src/Config/Source/CloudSource.php +++ b/src/Config/Source/CloudSource.php @@ -25,22 +25,22 @@ class CloudSource implements SourceInterface /** * @var FileList */ - private $fileList; + private FileList $fileList; /** * @var Filesystem */ - private $filesystem; + private Filesystem $filesystem; /** * @var ServiceFactory */ - private $serviceFactory; + private ServiceFactory $serviceFactory; /** * @var array */ - private static $map = [ + private static array $map = [ ServiceInterface::SERVICE_DB => ['db', 'database', 'mysql'], ServiceInterface::SERVICE_DB_QUOTE => ['mysql-quote'], ServiceInterface::SERVICE_DB_SALES => ['mysql-sales'], @@ -48,7 +48,8 @@ class CloudSource implements SourceInterface ServiceInterface::SERVICE_OPENSEARCH => ['opensearch', 'os'], ServiceInterface::SERVICE_REDIS => ['redis'], ServiceInterface::SERVICE_VALKEY => ['cache','valkey'], - ServiceInterface::SERVICE_RABBITMQ => ['rmq', 'rabbitmq'] + ServiceInterface::SERVICE_RABBITMQ => ['rmq', 'rabbitmq'], + ServiceInterface::SERVICE_ACTIVEMQ_ARTEMIS => ['activemq', 'artemis', 'activemq-artemis'] ]; /** @@ -66,9 +67,10 @@ public function __construct( $this->serviceFactory = $serviceFactory; } - /** - * @inheritDoc - */ + /** + * @inheritDoc + * @throws ConfigurationMismatchException + */ public function read(): Repository { $appConfigFile = $this->fileList->getAppConfig(); diff --git a/src/Service/ServiceFactory.php b/src/Service/ServiceFactory.php index 119fcc51..d53d27d7 100644 --- a/src/Service/ServiceFactory.php +++ b/src/Service/ServiceFactory.php @@ -52,7 +52,7 @@ class ServiceFactory /** * @var array */ - private static $config = [ + private static array $config = [ ServiceInterface::SERVICE_PHP_CLI => [ 'image' => 'magento/magento-cloud-docker-php', 'pattern' => '%s:%s-cli-%s', @@ -153,6 +153,21 @@ class ServiceFactory 'image' => 'rabbitmq', 'pattern' => self::PATTERN_STD, ], + ServiceInterface::SERVICE_ACTIVEMQ_ARTEMIS => [ + 'image' => 'apache/activemq-artemis', + 'pattern' => self::PATTERN_STD, + 'config' => [ + 'ports' => [61616, 61613, 8161], + 'environment' => [ + 'ARTEMIS_USER' => 'admin', + 'ARTEMIS_PASSWORD' => 'admin', + ], + 'volumes' => [ + '/var/lib/artemis/data', + '/var/log/artemis', + ] + ], + ], ServiceInterface::SERVICE_NODE => [ 'image' => 'node', 'pattern' => self::PATTERN_STD @@ -192,7 +207,7 @@ class ServiceFactory /** * @var FileList */ - private $fileList; + private FileList $fileList; /** * @var string @@ -208,12 +223,12 @@ public function __construct(FileList $fileList) } /** - * @param string $name - * @param string $version - * @param array $config - * @param string|null $image - * @param string|null $customRegistry - * @param string|null $imagePattern + * @param string $name + * @param string $version + * @param array $config + * @param string|null $image + * @param string|null $customRegistry + * @param string|null $imagePattern * @return array * @throws ConfigurationMismatchException */ @@ -226,10 +241,12 @@ public function create( ?string $imagePattern = null ): array { if (!array_key_exists($name, self::$config)) { - throw new ConfigurationMismatchException(sprintf( - 'Service "%s" is not supported', - $name - )); + throw new ConfigurationMismatchException( + sprintf( + 'Service "%s" is not supported', + $name + ) + ); } $metaConfig = self::$config[$name]; @@ -246,7 +263,7 @@ public function create( } /** - * @param string $name + * @param string $name * @return string * @throws ConfigurationMismatchException */ @@ -256,14 +273,16 @@ public function getDefaultImage(string $name): string return self::$config[$name]['image']; } - throw new ConfigurationMismatchException(sprintf( - 'Default image for %s cannot be resolved', - $name - )); + throw new ConfigurationMismatchException( + sprintf( + 'Default image for %s cannot be resolved', + $name + ) + ); } /** - * @param string $name + * @param string $name * @return string * @throws ConfigurationMismatchException */ @@ -273,10 +292,12 @@ public function getDefaultVersion(string $name): string return self::$config[$name]['version']; } - throw new ConfigurationMismatchException(sprintf( - 'Default version for %s cannot be resolved', - $name - )); + throw new ConfigurationMismatchException( + sprintf( + 'Default version for %s cannot be resolved', + $name + ) + ); } /** diff --git a/src/Service/ServiceInterface.php b/src/Service/ServiceInterface.php index 54936094..7ae08bbd 100644 --- a/src/Service/ServiceInterface.php +++ b/src/Service/ServiceInterface.php @@ -27,6 +27,7 @@ interface ServiceInterface public const SERVICE_ELASTICSEARCH = 'elasticsearch'; public const SERVICE_OPENSEARCH = 'opensearch'; public const SERVICE_RABBITMQ = 'rabbitmq'; + public const SERVICE_ACTIVEMQ_ARTEMIS = 'activemq-artemis'; public const SERVICE_NODE = 'node'; public const SERVICE_VARNISH = 'varnish'; public const SERVICE_SELENIUM = 'selenium'; diff --git a/src/Test/Functional/Acceptance/ActivemqArtemis84Cest.php b/src/Test/Functional/Acceptance/ActivemqArtemis84Cest.php new file mode 100644 index 00000000..f5bf5718 --- /dev/null +++ b/src/Test/Functional/Acceptance/ActivemqArtemis84Cest.php @@ -0,0 +1,21 @@ +generateDockerCompose($this->buildCommand($data)); + $I->replaceImagesWithCustom(); + $I->startEnvironment(); + + // Test that ActiveMQ Artemis container is running and healthy + $I->runDockerComposeCommand('ps'); + $I->seeInOutput('activemq-artemis'); + $I->seeInOutput('(healthy)'); + + // Test network connectivity + $this->testNetworkConnectivity($I); + + // Test ActiveMQ Artemis CLI functionality + $this->testArtemisCLI($I); + + // Test message producer/consumer functionality + $this->testMessageQueuing($I); + + // Test environment variables + $this->testEnvironmentVariables($I); + } + + /** + * Test network connectivity to ActiveMQ Artemis ports + * + * @param CliTester $I + */ + private function testNetworkConnectivity(CliTester $I): void + { + // Test ActiveMQ Artemis web console accessibility (port 8161) using curl instead of nc + $I->runDockerComposeCommand('exec -T fpm curl -f -s http://activemq-artemis.magento2.docker:8161/ > /dev/null'); + + // Test ActiveMQ Artemis broker port accessibility (port 61616) using telnet timeout + $I->runDockerComposeCommand('exec -T fpm timeout 5 bash -c "runDockerComposeCommand('exec -T fpm timeout 5 bash -c "runDockerComposeCommand('exec -T fpm timeout 5 bash -c "runDockerComposeCommand( + 'exec -T activemq-artemis /opt/activemq-artemis/bin/artemis queue stat --user admin --password admin' + ); + $I->seeInOutput('Connection brokerURL'); + + // Test that we can see the default system queues + $I->seeInOutput('DLQ'); + $I->seeInOutput('ExpiryQueue'); + + // Test broker information + $I->runDockerComposeCommand( + 'exec -T activemq-artemis /opt/activemq-artemis/bin/artemis address show --user admin --password admin' + ); + $I->seeInOutput('DLQ'); + } + + /** + * Test message producer/consumer functionality + * + * @param CliTester $I + */ + private function testMessageQueuing(CliTester $I): void + { + // Test sending a message to the default DLQ queue (which always exists) + $I->runDockerComposeCommand( + 'exec -T activemq-artemis /opt/activemq-artemis/bin/artemis producer ' . + '--destination queue://DLQ --message-count 1 --message "Hello ActiveMQ Artemis Test" ' . + '--user admin --password admin' + ); + $I->seeInOutput('Produced: 1 messages'); + + // Test consuming the message from the DLQ queue + $I->runDockerComposeCommand( + 'exec -T activemq-artemis /opt/activemq-artemis/bin/artemis consumer ' . + '--destination queue://DLQ --message-count 1 --user admin --password admin' + ); + $I->seeInOutput('Consumed: 1 messages'); + + // Test broker memory and connection info + $I->runDockerComposeCommand( + 'exec -T activemq-artemis /opt/activemq-artemis/bin/artemis queue stat --user admin --password admin' + ); + $I->seeInOutput('Connection brokerURL'); + } + + /** + * Test environment variables + * + * @param CliTester $I + */ + private function testEnvironmentVariables(CliTester $I): void + { + // Test that environment variables are properly set + $I->runDockerComposeCommand('exec -T activemq-artemis env | grep ARTEMIS'); + $I->seeInOutput('ARTEMIS_USER=admin'); + $I->seeInOutput('ARTEMIS_PASSWORD=admin'); + } + + /** + * Builds build:compose command from given test data + * + * @param Example $data + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + private function buildCommand(Example $data): string + { + $command = sprintf( + '--mode=production', + $data['version'] + ); + + return $command; + } + + /** + * @return array + */ + protected function dataProvider(): array + { + return [ + [ + 'version' => '2.42.0', + ], + ]; + } +} diff --git a/src/Test/Unit/Config/RelationshipTest.php b/src/Test/Unit/Config/RelationshipTest.php index 7def0faf..4ff6387f 100644 --- a/src/Test/Unit/Config/RelationshipTest.php +++ b/src/Test/Unit/Config/RelationshipTest.php @@ -72,6 +72,15 @@ class RelationshipTest extends TestCase 'password' => 'guest', ] ], + 'activemq-artemis' => [ + [ + 'host' => 'activemq-artemis', + 'port' => '61616', + 'username' => 'admin', + 'password' => 'admin', + 'web_console_port' => '8161', + ] + ], 'zookeeper' => [ [ 'host' => 'zookeeper', @@ -100,6 +109,7 @@ public function testGet() $esVersion = '7.7'; $osVersion = '1.1'; $rmqVersion = '3.5'; + $activemqArtemisVersion = '2.17'; $zookeeperVersion = 'latest'; $configWithType = $this->defaultConfigs; $configWithType['database'][0]['type'] = "mysql:$mysqlVersion"; @@ -108,9 +118,10 @@ public function testGet() $configWithType['elasticsearch'][0]['type'] = "elasticsearch:$esVersion"; $configWithType['opensearch'][0]['type'] = "opensearch:$osVersion"; $configWithType['rabbitmq'][0]['type'] = "rabbitmq:$rmqVersion"; + $configWithType['activemq-artemis'][0]['type'] = "activemq-artemis:$activemqArtemisVersion"; $configWithType['zookeeper'][0]['type'] = "zookeeper:$zookeeperVersion"; - $this->configMock->expects($this->exactly(9)) + $this->configMock->expects($this->exactly(10)) ->method('hasServiceEnabled') ->willReturnCallback(function ($service) { static $services = [ @@ -122,6 +133,7 @@ public function testGet() 'elasticsearch', 'opensearch', 'rabbitmq', + 'activemq-artemis', 'zookeeper' ]; @@ -134,6 +146,7 @@ public function testGet() true, true, true, + true, true ]; @@ -152,6 +165,7 @@ public function testGet() 'elasticsearch', 'opensearch', 'rabbitmq', + 'activemq-artemis', 'zookeeper' ]; @@ -162,10 +176,11 @@ public function testGet() $esVersion, $osVersion, $rmqVersion, + $activemqArtemisVersion, $zookeeperVersion ]; - $this->configMock->expects($this->exactly(7)) + $this->configMock->expects($this->exactly(8)) ->method('getServiceVersion') ->willReturnCallback(function ($service) use ( &$services, diff --git a/tests/functional/Codeception/Docker.php b/tests/functional/Codeception/Docker.php index ac65e51d..955cb2c3 100644 --- a/tests/functional/Codeception/Docker.php +++ b/tests/functional/Codeception/Docker.php @@ -108,8 +108,8 @@ public function runDockerComposeCommand(string $command): bool public function resetFilesOwner(): bool { return $this->runDockerComposeCommand( - 'run build bash -c "chown -R $(id -u):$(id -g) . /composer/cache"' - ); + 'run --user root build bash -c "uid=$(stat -c %u . 2>/dev/null || stat -f %u .); gid=$(stat -c %g . 2>/dev/null || stat -f %g .); chown -R $uid:$gid . /composer/cache"' + ); } /** diff --git a/tests/functional/Robo/Tasks/CopyFromDocker.php b/tests/functional/Robo/Tasks/CopyFromDocker.php index 2dd862d4..6c2ef8f0 100644 --- a/tests/functional/Robo/Tasks/CopyFromDocker.php +++ b/tests/functional/Robo/Tasks/CopyFromDocker.php @@ -80,7 +80,10 @@ public function destination(string $destination): self public function getCommand(): string { return sprintf( - 'docker cp "$(docker-compose ps -q %s)":%s %s', + '(docker-compose cp %s:%s %s) || (docker cp $(docker-compose ps -q %s):%s %s)', + $this->container, + $this->source, + $this->destination, $this->container, $this->source, $this->destination @@ -108,4 +111,4 @@ public function run(): Result return $this->executeCommand($this->getCommand()); } -} +} \ No newline at end of file diff --git a/tests/functional/Robo/Tasks/CopyToDocker.php b/tests/functional/Robo/Tasks/CopyToDocker.php index a1d6d8b3..8ccde7d0 100644 --- a/tests/functional/Robo/Tasks/CopyToDocker.php +++ b/tests/functional/Robo/Tasks/CopyToDocker.php @@ -80,7 +80,10 @@ public function destination(string $destination): self public function getCommand(): string { return sprintf( - 'docker cp "$(docker-compose ps -q %s)":%s %s', + '(docker-compose cp %s:%s %s) || (docker cp $(docker-compose ps -q %s):%s %s)', + $this->source, + $this->container, + $this->destination, $this->source, $this->container, $this->destination @@ -102,4 +105,4 @@ public function run(): Result return $this->executeCommand($this->getCommand()); } -} +} \ No newline at end of file