From fa5a312893467954aa7b25636c03c74bc4bbf834 Mon Sep 17 00:00:00 2001 From: Sathish Subramanian Date: Tue, 11 Aug 2020 23:12:29 +0530 Subject: [PATCH 01/17] MCLOUD-6698: Update deploy ES version config validator (#769) --- .../Validator/Deploy/ElasticSearchVersion.php | 16 +++------------- .../Deploy/ElasticSearchVersionTest.php | 8 +++++--- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/Config/Validator/Deploy/ElasticSearchVersion.php b/src/Config/Validator/Deploy/ElasticSearchVersion.php index 941a0a09d6..1630b69ac8 100644 --- a/src/Config/Validator/Deploy/ElasticSearchVersion.php +++ b/src/Config/Validator/Deploy/ElasticSearchVersion.php @@ -78,19 +78,9 @@ class ElasticSearchVersion implements ValidatorInterface 'esVersionRaw' => '6.x', ], [ - 'packageVersion' => '>=7.0 <7.2', - 'esVersion' => '>=7.0 <7.2', - 'esVersionRaw' => '>=7.0 <7.2', - ], - [ - 'packageVersion' => '>=7.2 <7.4', - 'esVersion' => '>=7.2 <7.4', - 'esVersionRaw' => '>=7.2 <7.4', - ], - [ - 'packageVersion' => '>=7.4', - 'esVersion' => '>=7.4', - 'esVersionRaw' => '>=7.4', + 'packageVersion' => '>=7.5', + 'esVersion' => '>=7.5', + 'esVersionRaw' => '>=7.5', ], ]; diff --git a/src/Test/Unit/Config/Validator/Deploy/ElasticSearchVersionTest.php b/src/Test/Unit/Config/Validator/Deploy/ElasticSearchVersionTest.php index 5ff69f9ea7..3f4ce2baa5 100644 --- a/src/Test/Unit/Config/Validator/Deploy/ElasticSearchVersionTest.php +++ b/src/Test/Unit/Config/Validator/Deploy/ElasticSearchVersionTest.php @@ -239,11 +239,13 @@ public function validateDataProvider(): array ['7.1', '7.0', Success::class], ['7.2', '7.3', Success::class], ['7.3', '7.2', Success::class], - ['7.4', '7.6', Success::class], ['7.6', '7.4', Success::class], - ['7.4', '7.5', Success::class], ['7.5', '7.4', Success::class], - ['7.4', '7.2', Error::class], + ['7.4', '7.2', Success::class], + ['7.7', '7.8', Success::class], + ['7.6', '7.7', Success::class], + ['7.4', '7.6', Error::class], + ['7.4', '7.5', Error::class], ['6.1', '2.0', Error::class], [ '6.2', From 50a9518e3a27cca3d64a4c71311da2c4c9a26870 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko Date: Tue, 18 Aug 2020 14:42:44 -0500 Subject: [PATCH 02/17] MCLOUD-6712: Fix error method parameters for DatabaseSplitConnection validator (#772) --- .../Deploy/DatabaseSplitConnection.php | 24 ++++++++++++------- .../Deploy/DatabaseSplitConnectionTest.php | 22 ++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Config/Validator/Deploy/DatabaseSplitConnection.php b/src/Config/Validator/Deploy/DatabaseSplitConnection.php index e7ec38889e..94c75ec2ed 100644 --- a/src/Config/Validator/Deploy/DatabaseSplitConnection.php +++ b/src/Config/Validator/Deploy/DatabaseSplitConnection.php @@ -63,15 +63,21 @@ public function validate(): Validator\ResultInterface return $this->resultFactory->success(); } - return $this->resultFactory->error(sprintf( - 'Split database configuration was detected in the property %s' - . ' of the file .magento.env.yaml:' . PHP_EOL - . '%s' . PHP_EOL - . 'Magento Cloud does not support a custom split database configuration,' - . ' such configurations will be ignored', - DeployInterface::VAR_DATABASE_CONFIGURATION, - implode(PHP_EOL, $messageItem), + return $this->resultFactory->error( + sprintf( + 'Detected split database configuration in the %s property of the file .magento.env.yaml:' . PHP_EOL + . '%s' . PHP_EOL + . 'Magento Cloud does not support custom connections in the split database configuration,' + . ' Custom connections will be ignored', + DeployInterface::VAR_DATABASE_CONFIGURATION, + implode(PHP_EOL, $messageItem) + ), + sprintf( + 'Update the %s variable in the \'.magento.env.yaml\' file to remove custom connections ' + . 'for split databases.', + DeployInterface::VAR_DATABASE_CONFIGURATION + ), Error::WARN_WRONG_SPLIT_DB_CONFIG - )); + ); } } diff --git a/src/Test/Unit/Config/Validator/Deploy/DatabaseSplitConnectionTest.php b/src/Test/Unit/Config/Validator/Deploy/DatabaseSplitConnectionTest.php index 25575488a7..c6965cf47f 100644 --- a/src/Test/Unit/Config/Validator/Deploy/DatabaseSplitConnectionTest.php +++ b/src/Test/Unit/Config/Validator/Deploy/DatabaseSplitConnectionTest.php @@ -7,6 +7,7 @@ namespace Magento\MagentoCloud\Test\Unit\Config\Validator\Deploy; +use Magento\MagentoCloud\App\Error as ApplicationError; use Magento\MagentoCloud\Config\ConfigException; use Magento\MagentoCloud\Config\Stage\DeployInterface; use Magento\MagentoCloud\Config\Validator\Deploy\DatabaseSplitConnection; @@ -78,14 +79,19 @@ public function testMessageValidate() ->willReturn($dbConfiguration); $this->resultFactoryMock->expects($this->once()) ->method('error') - ->with('Split database configuration was detected in the property DATABASE_CONFIGURATION of the' - . ' file .magento.env.yaml:' . PHP_EOL - . '- connection: checkout' . PHP_EOL - . '- connection: sales' . PHP_EOL - . '- slave_connection: checkout' . PHP_EOL - . '- slave_connection: sales' . PHP_EOL - . 'Magento Cloud does not support a custom split database configuration,' - . ' such configurations will be ignored'); + ->with( + 'Detected split database configuration in the DATABASE_CONFIGURATION property of the' + . ' file .magento.env.yaml:' . PHP_EOL + . '- connection: checkout' . PHP_EOL + . '- connection: sales' . PHP_EOL + . '- slave_connection: checkout' . PHP_EOL + . '- slave_connection: sales' . PHP_EOL + . 'Magento Cloud does not support custom connections in the split database configuration,' + . ' Custom connections will be ignored', + 'Update the DATABASE_CONFIGURATION variable in the \'.magento.env.yaml\' file to remove ' + . 'custom connections for split databases.', + ApplicationError::WARN_WRONG_SPLIT_DB_CONFIG + ); $this->assertInstanceOf(Error::class, $this->validator->validate()); } From 2c8d2b7a9bffea730be3a2327ea7e0632ac21ce1 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko Date: Wed, 19 Aug 2020 15:27:39 -0500 Subject: [PATCH 03/17] MCLOUD-6708: Add warning if MAGE_MODE was set as not production (#771) --- config/schema.error.yaml | 6 + dist/error-codes.md | 3 +- scenario/deploy.xml | 1 + src/App/Error.php | 1 + src/Config/EnvironmentData.php | 12 ++ src/Config/EnvironmentDataInterface.php | 7 ++ .../Validator/Deploy/MageModeVariable.php | 59 +++++++++ src/Test/Unit/Config/EnvironmentDataTest.php | 13 ++ .../Validator/Deploy/MageModeVariableTest.php | 113 ++++++++++++++++++ 9 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/Config/Validator/Deploy/MageModeVariable.php create mode 100644 src/Test/Unit/Config/Validator/Deploy/MageModeVariableTest.php diff --git a/config/schema.error.yaml b/config/schema.error.yaml index 137685c180..8d18abf819 100644 --- a/config/schema.error.yaml +++ b/config/schema.error.yaml @@ -577,6 +577,12 @@ suggestion: 'Check the `cloud.log` for more information.' step: 'pre-deploy:restore-writable-dirs' type: warning +!php/const Magento\MagentoCloud\App\Error::WARN_NOT_SUPPORTED_MAGE_MODE: + title: 'Mode value for MAGE_MODE environment variable not supported' + stage: deploy + suggestion: 'Remove MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports only "production" mode.' + step: 'validate-config:mage-mode-variable' + type: warning !php/const Magento\MagentoCloud\App\Error::WARN_DEBUG_LOG_ENABLED: title: 'Debug logging is enabled in Magento' suggestion: 'To save disk space, do not enable debug logging for your production environments.' diff --git a/dist/error-codes.md b/dist/error-codes.md index 46d3fbef4c..ab36d0d0b9 100644 --- a/dist/error-codes.md +++ b/dist/error-codes.md @@ -147,7 +147,8 @@ Warning errors indicate a problem with the Magento Commerce Cloud project config | 2023 | install-update:split-db | Enabling a split database will be skipped. | | | 2024 | install-update:split-db | The SPLIT_DB variable is missing the configuration for split connection types. | | | 2025 | install-update:split-db | Slave connection not set. | | -| 2026 | pre-deploy:restore-writable-dirs | Failed to restore some data generated data during the build phase to the mounted directories. | Check the `cloud.log` for more information. | +| 2026 | pre-deploy:restore-writable-dirs | Failed to restore some data generated during the build phase to the mounted directories | Check the `cloud.log` for more information. | +| 2027 | validate-config:mage-mode-variable | Mode value for MAGE_MODE environment variable not supported | Remove MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports only "production" mode. | ### Post-deploy stage diff --git a/scenario/deploy.xml b/scenario/deploy.xml index 5e9e719762..f4d990db8f 100644 --- a/scenario/deploy.xml +++ b/scenario/deploy.xml @@ -33,6 +33,7 @@ Magento\MagentoCloud\Config\Validator\Deploy\ElasticSuiteIntegrity + Magento\MagentoCloud\Config\Validator\Deploy\MageModeVariable Magento\MagentoCloud\Config\Validator\Deploy\DatabaseSplitConnection Magento\MagentoCloud\Config\Validator\Deploy\ReportDirNestingLevel Magento\MagentoCloud\Config\Validator\Deploy\AdminData diff --git a/src/App/Error.php b/src/App/Error.php index 7b478c942a..80143277f2 100644 --- a/src/App/Error.php +++ b/src/App/Error.php @@ -130,6 +130,7 @@ class Error public const WARN_NOT_ENOUGH_DATA_SPLIT_DB_VAR = 2024; public const WARN_SLAVE_CONNECTION_NOT_SET = 2025; public const WARN_COPY_MOUNTED_DIRS_FAILED = 2026; + public const WARN_NOT_SUPPORTED_MAGE_MODE = 2027; /** * Post-deploy diff --git a/src/Config/EnvironmentData.php b/src/Config/EnvironmentData.php index ec40a4abfb..73f521bf29 100644 --- a/src/Config/EnvironmentData.php +++ b/src/Config/EnvironmentData.php @@ -162,4 +162,16 @@ public function getBranchName(): string return $this->getEnv($envVarName) ? (string) $this->getEnv($envVarName) : ''; } + + /** + * @inheritDoc + */ + public function getMageMode(): ?string + { + if (isset($this->data['mage-mode'])) { + return $this->data['mage-mode']; + } + + return $this->data['mage-mode'] = $this->getEnv('MAGE_MODE') ?: null; + } } diff --git a/src/Config/EnvironmentDataInterface.php b/src/Config/EnvironmentDataInterface.php index a41275c8cd..946656d34d 100644 --- a/src/Config/EnvironmentDataInterface.php +++ b/src/Config/EnvironmentDataInterface.php @@ -56,4 +56,11 @@ public function getApplication(): array; * @return string */ public function getBranchName(): string; + + /** + * Returns MAGE_MODE environment variable + * + * @return string|null + */ + public function getMageMode(): ?string; } diff --git a/src/Config/Validator/Deploy/MageModeVariable.php b/src/Config/Validator/Deploy/MageModeVariable.php new file mode 100644 index 0000000000..69feb6137a --- /dev/null +++ b/src/Config/Validator/Deploy/MageModeVariable.php @@ -0,0 +1,59 @@ +envData = $envData; + $this->resultFactory = $resultFactory; + } + + /** + * @return Validator\ResultInterface + * @throws FileSystemException + */ + public function validate(): Validator\ResultInterface + { + $mageMode = $this->envData->getMageMode(); + if (!$mageMode || $mageMode == self::PRODUCTION_MODE) { + return $this->resultFactory->success(); + } + + return $this->resultFactory->errorByCode(Error::WARN_NOT_SUPPORTED_MAGE_MODE); + } +} diff --git a/src/Test/Unit/Config/EnvironmentDataTest.php b/src/Test/Unit/Config/EnvironmentDataTest.php index 45086999d3..b8e44e48d1 100644 --- a/src/Test/Unit/Config/EnvironmentDataTest.php +++ b/src/Test/Unit/Config/EnvironmentDataTest.php @@ -171,4 +171,17 @@ public function testGetApplicationWithNoSystemVariablesFileNotExists(): void $this->assertEquals([], $this->environmentData->getApplication()); } + + public function testGetMageMode(): void + { + $this->assertNull($this->environmentData->getMageMode()); + + $mode = 'some_mode'; + $_ENV['MAGE_MODE'] = $mode; + $this->assertEquals($mode, $this->environmentData->getMageMode()); + + //check that value was taken from cache + $_ENV['MAGE_MODE'] = 'new value'; + $this->assertEquals($mode, $this->environmentData->getMageMode()); + } } diff --git a/src/Test/Unit/Config/Validator/Deploy/MageModeVariableTest.php b/src/Test/Unit/Config/Validator/Deploy/MageModeVariableTest.php new file mode 100644 index 0000000000..070644a039 --- /dev/null +++ b/src/Test/Unit/Config/Validator/Deploy/MageModeVariableTest.php @@ -0,0 +1,113 @@ +envDataMock = $this->createMock(EnvironmentDataInterface::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->validator = new MageModeVariable( + $this->envDataMock, + $this->resultFactoryMock + ); + } + + /** + * @param $mageMode string|null + * @throws FileSystemException + * @dataProvider validateSuccessDataProvider + */ + public function testValidateSuccess($mageMode) + { + $this->envDataMock->expects($this->once()) + ->method('getMageMode') + ->willReturn($mageMode); + $this->resultFactoryMock->expects($this->once()) + ->method('success'); + $this->resultFactoryMock->expects($this->never()) + ->method('errorByCode'); + + $this->validator->validate(); + } + + /** + * Data provider for testValidateSuccess + * @return array + */ + public function validateSuccessDataProvider() + { + return [ + [null], + [''], + [MageModeVariable::PRODUCTION_MODE], + ]; + } + + /** + * @param $mageMode string + * @throws FileSystemException + * @dataProvider validateErrorDataProvider + */ + public function testValidateError($mageMode) + { + $this->envDataMock->expects($this->once()) + ->method('getMageMode') + ->willReturn($mageMode); + $this->resultFactoryMock->expects($this->never()) + ->method('success'); + $this->resultFactoryMock->expects($this->once()) + ->method('errorByCode'); + + $this->validator->validate(); + } + + /** + * Data provider for testValidateError + * @return array + */ + public function validateErrorDataProvider() + { + return [ + ['developer'], + ['default'], + ['maintenance'], + ]; + } +} From fb778a9c5b996df40675a880420d22fa688500c8 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko Date: Thu, 20 Aug 2020 15:27:46 -0500 Subject: [PATCH 04/17] MCLOUD-5563: Add information about warm-up concurrency into log (#773) --- src/Step/PostDeploy/WarmUp.php | 19 +++++++++++++------ src/Test/Unit/Step/PostDeploy/WarmUpTest.php | 8 ++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Step/PostDeploy/WarmUp.php b/src/Step/PostDeploy/WarmUp.php index 968e9b97c7..a76bcd1620 100644 --- a/src/Step/PostDeploy/WarmUp.php +++ b/src/Step/PostDeploy/WarmUp.php @@ -67,9 +67,21 @@ public function __construct( public function execute() { try { - $this->logger->info('Starting page warming up'); + $this->logger->info('Starting page warmup'); $config = []; + $concurrency = $this->postDeploy->get(PostDeployInterface::VAR_WARM_UP_CONCURRENCY); + if ($concurrency) { + $config['concurrency'] = $concurrency; + $this->logger->info( + sprintf( + 'Warmup concurrency set to %s as specified by the %s configuration', + $concurrency, + PostDeployInterface::VAR_WARM_UP_CONCURRENCY + ) + ); + } + $urls = $this->urls->getAll(); $config['fulfilled'] = function ($response, $index) use ($urls) { @@ -95,11 +107,6 @@ public function execute() $this->logger->error('Warming up failed: ' . $urls[$index], $context); }; - $concurrency = $this->postDeploy->get(PostDeployInterface::VAR_WARM_UP_CONCURRENCY); - if ($concurrency) { - $config['concurrency'] = $concurrency; - } - $pool = $this->poolFactory->create($urls, $config); /** @var PromiseInterface $promise */ diff --git a/src/Test/Unit/Step/PostDeploy/WarmUpTest.php b/src/Test/Unit/Step/PostDeploy/WarmUpTest.php index bd15ecbb2c..b2250529cc 100644 --- a/src/Test/Unit/Step/PostDeploy/WarmUpTest.php +++ b/src/Test/Unit/Step/PostDeploy/WarmUpTest.php @@ -185,6 +185,14 @@ public function testExecuteWithConcurrency() ]; $concurrency = 2; + $this->loggerMock->expects($this->any()) + ->method('info') + ->withConsecutive( + ['Starting page warmup'], + ['Warmup concurrency set to ' . $concurrency . ' as specified by the ' + . PostDeployInterface::VAR_WARM_UP_CONCURRENCY . ' configuration'] + ); + $this->urlsMock->method('getAll') ->willReturn($urls); $this->postDeployMock->method('get') From 46e7fc96f26a27ff6ca135ef280b4be930643622 Mon Sep 17 00:00:00 2001 From: Bohdan Korablov Date: Wed, 9 Sep 2020 11:51:04 -0500 Subject: [PATCH 05/17] MCLOUD-6910: MC-37292: Make 'quality-patches' a hard dependency for ece-tools and soft dependency for magento-cloud-patches (#780) --- .github/workflows/ci.yaml | 1 + .travis.yml | 5 +++++ composer.json | 1 + 3 files changed, 7 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7a7b6968b2..8a568673ff 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,6 +23,7 @@ jobs: composer config --unset repositories.repo.magento.com composer remove --no-update magento/magento-cloud-components composer remove --no-update magento/magento-cloud-docker + composer remove --no-update magento/quality-patches composer remove --no-update magento/magento-cloud-patches - name: Composer Update run: composer update diff --git a/.travis.yml b/.travis.yml index aa9361e777..74b74741d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,11 @@ jobs: - php: '7.4' env: TEST_SUITE=functional-ee +before_install: +# https://github.com/kylekatarnls/update-helper/issues/9 + - if [ -n "${COMPOSER_VERSION}" ]; then travis_retry composer self-update ${COMPOSER_VERSION}; fi; + + install: - composer config http-basic.repo.magento.com ${REPO_USERNAME_CE} ${REPO_PASSWORD_CE} - composer config github-oauth.github.com ${GITHUB_TOKEN} diff --git a/composer.json b/composer.json index fd950ee5f0..06707c1581 100755 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "magento/magento-cloud-components": "^1.0.6", "magento/magento-cloud-docker": "^1.0.0", "magento/magento-cloud-patches": "^1.0.6", + "magento/quality-patches": "^1.0.3", "monolog/monolog": "^1.16", "nesbot/carbon": "^1.0||^2.0", "psr/container": "^1.0", From c143cfe95478a2733165590246ce358bea6d3df4 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Mon, 14 Sep 2020 09:57:22 -0500 Subject: [PATCH 06/17] MCLOUD-6847: Improve functional tests execution speed (#777) --- .travis.yml | 1 + codeception.dist.yml | 1 + .../Functional/Acceptance/AbstractCest.php | 9 ++++++++ tests/travis/prepare_functional_parallel.sh | 22 +++++-------------- travis.php.ini | 1 + 5 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 travis.php.ini diff --git a/.travis.yml b/.travis.yml index 74b74741d4..8aa596c185 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,7 @@ before_install: install: + - phpenv config-add travis.php.ini - composer config http-basic.repo.magento.com ${REPO_USERNAME_CE} ${REPO_PASSWORD_CE} - composer config github-oauth.github.com ${GITHUB_TOKEN} - if [ -n "${MCC_VERSION}" ]; then composer config repositories.mcc git git@github.com:magento/magento-cloud-components.git && composer require "magento/magento-cloud-components:${MCC_VERSION}" --no-update; fi; diff --git a/codeception.dist.yml b/codeception.dist.yml index 2badeb2381..2549c69fb9 100644 --- a/codeception.dist.yml +++ b/codeception.dist.yml @@ -24,6 +24,7 @@ modules: composer_magento_username: "%REPO_USERNAME%" composer_magento_password: "%REPO_PASSWORD%" composer_github_token: "%GITHUB_TOKEN%" + use_cached_workdir: true printOutput: false Magento\CloudDocker\Test\Functional\Codeception\Docker: system_magento_dir: "%Magento.docker.settings.system.magento_dir%" diff --git a/src/Test/Functional/Acceptance/AbstractCest.php b/src/Test/Functional/Acceptance/AbstractCest.php index 2ff196e6be..a78cf452c8 100644 --- a/src/Test/Functional/Acceptance/AbstractCest.php +++ b/src/Test/Functional/Acceptance/AbstractCest.php @@ -61,6 +61,14 @@ protected function convertEnvFromArrayToJson(array $data): string protected function prepareWorkplace(\CliTester $I, string $templateVersion): void { $I->cleanupWorkDir(); + + if ($I->isCacheWorkDirExists($templateVersion)) { + $I->restoreWorkDirFromCache($templateVersion); + $this->removeESIfExists($I, $templateVersion); + + return; + } + $I->cloneTemplateToWorkDir($templateVersion); $I->createAuthJson(); $I->createArtifactsDir(); @@ -90,6 +98,7 @@ protected function prepareWorkplace(\CliTester $I, string $templateVersion): voi if ($this->runComposerUpdate) { $I->composerUpdate(); + $I->cacheWorkDir($templateVersion); } $this->removeESIfExists($I, $templateVersion); diff --git a/tests/travis/prepare_functional_parallel.sh b/tests/travis/prepare_functional_parallel.sh index a99f80e5b2..ba44c3b9e5 100755 --- a/tests/travis/prepare_functional_parallel.sh +++ b/tests/travis/prepare_functional_parallel.sh @@ -8,15 +8,9 @@ trap '>&2 echo Error: Command \`$BASH_COMMAND\` on line $LINENO failed with exit php_version="${TRAVIS_PHP_VERSION//./}" -readarray -t test_set_list <<< "$(grep -Rl php${php_version} --exclude='*AcceptanceCest.php' --exclude='*AcceptanceCeCest.php' --exclude='*AcceptanceCe71Cest.php' --exclude='*AcceptanceCe72Cest.php' --exclude='*AcceptanceCe73Cest.php' --exclude='*AbstractCest.php' src/Test/Functional/Acceptance | sort)" +test_set_list=($(grep -Rl php${php_version} --exclude='*AcceptanceCest.php' --exclude='*AcceptanceCeCest.php' --exclude='*AcceptanceCe71Cest.php' --exclude='*AcceptanceCe72Cest.php' --exclude='*AcceptanceCe73Cest.php' --exclude='*AbstractCest.php' src/Test/Functional/Acceptance | sort)) group_count=6 -if [ $(( ${#test_set_list[@]} % group_count )) -eq 0 ]; then - element_in_group=$(printf "%.0f" "$(echo "scale=2;(${#test_set_list[@]})/${group_count}" | bc)") -else - element_in_group=$(printf "%.0f" "$(echo "scale=2;(${#test_set_list[@]} + ${group_count} - 1)/${group_count}" | bc)") -fi - cp codeception.dist.yml codeception.yml echo "groups:" >> codeception.yml echo " parallel_${php_version}_*: tests/functional/_data/parallel_${php_version}_*" >> codeception.yml @@ -30,15 +24,11 @@ else start_group_id=1 fi -for((i=0, group_id=start_group_id; i < ${#test_set_list[@]}; i+=element_in_group, group_id++)) +for((i=0, group_id=start_group_id; i < ${#test_set_list[@]}; i+=1, group_id++)) do - test_file_group=( "${test_set_list[@]:i:element_in_group}" ) - echo "Batch #${group_id} = ${#test_file_group[@]}" - + if [ $group_id -gt $group_count ]; then + group_id=$start_group_id + fi group_file="tests/functional/_data/parallel_${php_version}_$group_id.yml" - - for test_file in "${test_file_group[@]}" - do - echo "$test_file" >> "$group_file" - done + echo "${test_set_list[i]}" >> "$group_file" done diff --git a/travis.php.ini b/travis.php.ini new file mode 100644 index 0000000000..0b1342670d --- /dev/null +++ b/travis.php.ini @@ -0,0 +1 @@ +memory_limit = 4G From 48ed5d4e98017b3f1545f0dcea171c267cfefc56 Mon Sep 17 00:00:00 2001 From: Oleg Posyniak Date: Wed, 16 Sep 2020 14:03:54 -0500 Subject: [PATCH 07/17] MC-37699: Add support to read-only pub/static directory (#781) --- src/Config/Environment.php | 21 +++++++ src/Config/EnvironmentData.php | 41 ++++++++++--- src/Step/Build/BackupData/StaticContent.php | 36 ++++++++--- .../Deploy/PreDeploy/CleanStaticContent.php | 14 +++-- src/Test/Unit/Config/EnvironmentDataTest.php | 6 ++ src/Test/Unit/Config/EnvironmentTest.php | 18 ++++++ .../Build/BackupData/StaticContentTest.php | 55 ++++++++++++++++- .../PreDeploy/CleanStaticContentTest.php | 60 ++++++++++++++----- src/Util/BuildDirCopier.php | 2 +- 9 files changed, 213 insertions(+), 40 deletions(-) diff --git a/src/Config/Environment.php b/src/Config/Environment.php index 7634d136be..01cb62ac35 100755 --- a/src/Config/Environment.php +++ b/src/Config/Environment.php @@ -24,6 +24,8 @@ class Environment public const VAL_DISABLED = 'disabled'; public const VARIABLE_CRYPT_KEY = 'CRYPT_KEY'; + public const MOUNT_PUB_STATIC = 'pub/static'; + /** * The environment variable for controlling the directory nesting level for error reporting */ @@ -151,4 +153,23 @@ public function isMasterBranch(): bool return !empty($branchName) && preg_match(self::GIT_MASTER_BRANCH_RE, $branchName); } + + /** + * Checks whether application has specific mount. + * + * The name of the mount may have slash in the beginning (env variable) + * or does not have it. Method checks both cases. + * + * @param string $name + * @return bool + */ + public function hasMount(string $name): bool + { + $application = $this->getApplication(); + + $name = ltrim($name, '/'); + $slashName = '/' . $name; + + return isset($application['mounts'][$name]) || isset($application['mounts'][$slashName]); + } } diff --git a/src/Config/EnvironmentData.php b/src/Config/EnvironmentData.php index 73f521bf29..cdc2672cde 100644 --- a/src/Config/EnvironmentData.php +++ b/src/Config/EnvironmentData.php @@ -136,23 +136,48 @@ public function getApplication(): array return $this->data['application']; } - $application = $this->getEnvVar(SystemConfigInterface::VAR_ENV_APPLICATION, []); + $applicationEnvConfig = $this->getEnvVar(SystemConfigInterface::VAR_ENV_APPLICATION, []); + $applicationFileConfig = $this->readApplicationConfig(); - if (!$application) { + if (!$applicationEnvConfig) { + return $this->data['application'] = $applicationFileConfig; + } + + /** + * Temporary fix for the case when environment data does not accurately represent file configuration. + * + * @url https://github.com/magento/magento-cloud-docker/issues/292 + */ + if (!isset($applicationEnvConfig['mounts']) && isset($applicationFileConfig['mounts'])) { + $applicationEnvConfig['mounts'] = $applicationFileConfig['mounts']; + } + + return $this->data['application'] = $applicationEnvConfig; + } + + /** + * Read file config file if exists. + * + * @return array + */ + private function readApplicationConfig(): array + { + $configFile = $this->fileList->getAppConfig(); + + if ($this->file->isExists($configFile)) { try { - $application = Yaml::parse( - $this->file->fileGetContents($this->fileList->getAppConfig()) - ); - } catch (FileSystemException $e) { + return Yaml::parse($this->file->fileGetContents($configFile)); + } catch (FileSystemException $exception) { // Do nothing as $application needs to be empty } } - return $this->data['application'] = $application; + return []; } /** * Returns name of environment branch + * * @return string * */ @@ -160,7 +185,7 @@ public function getBranchName(): string { $envVarName = $this->systemConfig->get(SystemConfigInterface::VAR_ENV_ENVIRONMENT); - return $this->getEnv($envVarName) ? (string) $this->getEnv($envVarName) : ''; + return $this->getEnv($envVarName) ? (string)$this->getEnv($envVarName) : ''; } /** diff --git a/src/Step/Build/BackupData/StaticContent.php b/src/Step/Build/BackupData/StaticContent.php index 502d649882..8f2eedd42e 100644 --- a/src/Step/Build/BackupData/StaticContent.php +++ b/src/Step/Build/BackupData/StaticContent.php @@ -9,10 +9,12 @@ use Magento\MagentoCloud\App\Error; use Magento\MagentoCloud\App\GenericException; +use Magento\MagentoCloud\Config\Environment; use Magento\MagentoCloud\Filesystem\DirectoryList; use Magento\MagentoCloud\Filesystem\Driver\File; use Magento\MagentoCloud\Filesystem\FileSystemException; use Magento\MagentoCloud\Filesystem\Flag\Manager as FlagManager; +use Magento\MagentoCloud\Package\UndefinedPackageException; use Magento\MagentoCloud\Step\StepException; use Magento\MagentoCloud\Step\StepInterface; use Psr\Log\LoggerInterface; @@ -42,28 +44,36 @@ class StaticContent implements StepInterface */ private $flagManager; + /** + * @var Environment + */ + private $environment; + /** * @param File $file * @param LoggerInterface $logger * @param DirectoryList $directoryList * @param FlagManager $flagManager + * @param Environment $environmentData */ public function __construct( File $file, LoggerInterface $logger, DirectoryList $directoryList, - FlagManager $flagManager + FlagManager $flagManager, + Environment $environmentData ) { $this->file = $file; $this->logger = $logger; $this->directoryList = $directoryList; $this->flagManager = $flagManager; + $this->environment = $environmentData; } /** * @inheritdoc */ - public function execute() + public function execute(): void { try { $this->flagManager->delete(FlagManager::FLAG_REGENERATE); @@ -73,17 +83,25 @@ public function execute() return; } + } catch (GenericException $e) { + throw new StepException($e->getMessage(), $e->getCode(), $e); + } + + if (!$this->environment->hasMount(Environment::MOUNT_PUB_STATIC)) { + $this->logger->info('Static content was not moved to ./init directory'); + return; + } + + try { $initPubStatic = $this->directoryList->getPath(DirectoryList::DIR_INIT) . '/pub/static'; $originalPubStatic = $this->directoryList->getPath(DirectoryList::DIR_STATIC); - - $this->cleanInitPubStatic($initPubStatic); - $this->moveStaticContent($originalPubStatic, $initPubStatic); - } catch (StepException $e) { - throw $e; - } catch (GenericException $e) { - throw new StepException($e->getMessage(), $e->getCode(), $e); + } catch (UndefinedPackageException $exception) { + throw new StepException($exception->getMessage(), $exception->getCode(), $exception); } + + $this->cleanInitPubStatic($initPubStatic); + $this->moveStaticContent($originalPubStatic, $initPubStatic); } /** diff --git a/src/Step/Deploy/PreDeploy/CleanStaticContent.php b/src/Step/Deploy/PreDeploy/CleanStaticContent.php index 2199d03b52..d1bd0c0f02 100644 --- a/src/Step/Deploy/PreDeploy/CleanStaticContent.php +++ b/src/Step/Deploy/PreDeploy/CleanStaticContent.php @@ -27,7 +27,7 @@ class CleanStaticContent implements StepInterface /** * @var Environment */ - private $env; + private $environment; /** * @var LoggerInterface @@ -56,7 +56,7 @@ class CleanStaticContent implements StepInterface /** * @param LoggerInterface $logger - * @param Environment $env + * @param Environment $environment * @param File $file * @param DirectoryList $directoryList * @param FlagManager $flagManager @@ -64,14 +64,14 @@ class CleanStaticContent implements StepInterface */ public function __construct( LoggerInterface $logger, - Environment $env, + Environment $environment, File $file, DirectoryList $directoryList, FlagManager $flagManager, DeployInterface $stageConfig ) { $this->logger = $logger; - $this->env = $env; + $this->environment = $environment; $this->file = $file; $this->directoryList = $directoryList; $this->flagManager = $flagManager; @@ -83,11 +83,13 @@ public function __construct( * * {@inheritdoc} */ - public function execute() + public function execute(): void { try { if (!$this->flagManager->exists(FlagManager::FLAG_STATIC_CONTENT_DEPLOY_IN_BUILD) - || !$this->stageConfig->get(DeployInterface::VAR_CLEAN_STATIC_FILES)) { + || !$this->stageConfig->get(DeployInterface::VAR_CLEAN_STATIC_FILES) + || !$this->environment->hasMount(Environment::MOUNT_PUB_STATIC) + ) { return; } diff --git a/src/Test/Unit/Config/EnvironmentDataTest.php b/src/Test/Unit/Config/EnvironmentDataTest.php index b8e44e48d1..6bc0e0498d 100644 --- a/src/Test/Unit/Config/EnvironmentDataTest.php +++ b/src/Test/Unit/Config/EnvironmentDataTest.php @@ -153,6 +153,9 @@ public function testGetApplicationWithNoSystemVariablesFileExists(): void { $_ENV = null; + $this->fileMock->expects($this->once()) + ->method('isExists') + ->willReturn(true); $this->fileMock->expects($this->once()) ->method('fileGetContents') ->willReturn('[]'); @@ -165,6 +168,9 @@ public function testGetApplicationWithNoSystemVariablesFileNotExists(): void $_ENV = null; $exception = new FilesystemException('.magento.app.yaml not exist'); + $this->fileMock->expects($this->once()) + ->method('isExists') + ->willReturn(true); $this->fileMock->expects($this->once()) ->method('fileGetContents') ->willThrowException($exception); diff --git a/src/Test/Unit/Config/EnvironmentTest.php b/src/Test/Unit/Config/EnvironmentTest.php index 0d08d36067..69dec9e4dc 100644 --- a/src/Test/Unit/Config/EnvironmentTest.php +++ b/src/Test/Unit/Config/EnvironmentTest.php @@ -148,4 +148,22 @@ public function testGetEnvVarMageErrorReportDirNestingLevel(): void $this->assertSame(1, $this->environment->getEnvVarMageErrorReportDirNestingLevel()); } + + public function testHasMount(): void + { + $this->environmentDataMock->method('getApplication') + ->willReturn([ + 'mounts' => [ + 'test' => [], + '/test_with_slash' => [] + ] + ]); + + self::assertTrue($this->environment->hasMount('test')); + self::assertTrue($this->environment->hasMount('/test')); + self::assertTrue($this->environment->hasMount('test_with_slash')); + self::assertTrue($this->environment->hasMount('/test_with_slash')); + self::assertFalse($this->environment->hasMount('/unknown')); + self::assertFalse($this->environment->hasMount('unknown')); + } } diff --git a/src/Test/Unit/Step/Build/BackupData/StaticContentTest.php b/src/Test/Unit/Step/Build/BackupData/StaticContentTest.php index 371b1068c1..582daec21f 100644 --- a/src/Test/Unit/Step/Build/BackupData/StaticContentTest.php +++ b/src/Test/Unit/Step/Build/BackupData/StaticContentTest.php @@ -8,6 +8,7 @@ namespace Magento\MagentoCloud\Test\Unit\Step\Build\BackupData; use Magento\MagentoCloud\App\Error; +use Magento\MagentoCloud\Config\Environment; use Magento\MagentoCloud\Step\StepException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -48,6 +49,11 @@ class StaticContentTest extends TestCase */ private $flagManagerMock; + /** + * @var Environment|MockObject + */ + private $environment; + /** * @var string */ @@ -74,6 +80,7 @@ protected function setUp(): void ->getMockForAbstractClass(); $this->directoryListMock = $this->createMock(DirectoryList::class); $this->flagManagerMock = $this->createMock(FlagManager::class); + $this->environment = $this->createMock(Environment::class); $this->flagManagerMock->expects($this->once()) ->method('delete') @@ -83,7 +90,8 @@ protected function setUp(): void $this->fileMock, $this->loggerMock, $this->directoryListMock, - $this->flagManagerMock + $this->flagManagerMock, + $this->environment ); } @@ -125,6 +133,10 @@ public function testExecuteFlagSCDInBuildExistsAndInitPubStaticExists(): void ->with($this->originalPubStaticPath, $this->initPubStaticPath); $this->fileMock->expects($this->never()) ->method('copyDirectory'); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -171,6 +183,10 @@ public function testExecuteFlagSCDInBuildExistsAndInitPubStaticDoesNotExist(): v ->with($this->originalPubStaticPath, $this->initPubStaticPath); $this->fileMock->expects($this->never()) ->method('copyDirectory'); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -216,6 +232,10 @@ public function testExecuteSCDInAndInitPubStaticDoesNotExistAndRecreatePubStatic ->with($this->originalPubStaticPath, $this->initPubStaticPath); $this->fileMock->expects($this->never()) ->method('copyDirectory'); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -273,6 +293,10 @@ public function testCopyingWithException(): void $this->expectException(StepException::class); $this->expectExceptionMessage('some error'); $this->expectExceptionCode(Error::BUILD_SCD_COPYING_FAILED); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -313,12 +337,16 @@ public function prepareCopying(): void ->method('rename') ->with($this->originalPubStaticPath, $this->initPubStaticPath) ->willThrowException(new FileSystemException('Some error')); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); } /** * @throws StepException */ - public function testClearingDirectoryWithFileSystemException() + public function testClearingDirectoryWithFileSystemException(): void { $this->expectException(StepException::class); $this->expectExceptionMessage('some error'); @@ -341,6 +369,29 @@ public function testClearingDirectoryWithFileSystemException() ->method('backgroundClearDirectory') ->with($this->initPubStaticPath) ->willThrowException(new FileSystemException('some error')); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); + + $this->step->execute(); + } + + public function testExecuteScdOnBuildAndReadonly(): void + { + $this->flagManagerMock->expects(self::once()) + ->method('exists') + ->with(FlagManager::FLAG_STATIC_CONTENT_DEPLOY_IN_BUILD) + ->willReturn(true); + $this->loggerMock->expects(self::once()) + ->method('info') + ->withConsecutive( + ['Static content was not moved to ./init directory'] + ); + $this->environment->expects($this->once()) + ->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(false); $this->step->execute(); } diff --git a/src/Test/Unit/Step/Deploy/PreDeploy/CleanStaticContentTest.php b/src/Test/Unit/Step/Deploy/PreDeploy/CleanStaticContentTest.php index c3de3e1dca..4946d218b3 100644 --- a/src/Test/Unit/Step/Deploy/PreDeploy/CleanStaticContentTest.php +++ b/src/Test/Unit/Step/Deploy/PreDeploy/CleanStaticContentTest.php @@ -93,10 +93,10 @@ public function testExecute(): void ->method('exists') ->with(FlagManager::FLAG_STATIC_CONTENT_DEPLOY_IN_BUILD) ->willReturn(true); - $this->stageConfigMock->expects($this->once()) - ->method('get') - ->with(DeployInterface::VAR_CLEAN_STATIC_FILES) - ->willReturn(true); + $this->stageConfigMock->method('get') + ->willReturnMap([ + [DeployInterface::VAR_CLEAN_STATIC_FILES, true] + ]); $this->directoryListMock->expects($this->once()) ->method('getMagentoRoot') ->willReturn('magento_root'); @@ -109,6 +109,9 @@ public function testExecute(): void ['Static content deployment was performed during build hook, cleaning old content.'], ['Clearing pub/static'] ); + $this->environmentMock->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -143,16 +146,19 @@ public function testExecuteWithDeployInBuildNoClean(): void ->method('exists') ->with(FlagManager::FLAG_STATIC_CONTENT_DEPLOY_IN_BUILD) ->willReturn(true); - $this->stageConfigMock->expects($this->once()) - ->method('get') - ->with(DeployInterface::VAR_CLEAN_STATIC_FILES) - ->willReturn(false); + $this->stageConfigMock->method('get') + ->willReturnMap([ + [DeployInterface::VAR_CLEAN_STATIC_FILES, false] + ]); $this->directoryListMock->expects($this->never()) ->method('getMagentoRoot') ->willReturn('magento_root'); $this->fileMock->expects($this->never()) ->method('backgroundClearDirectory') ->with('magento_root/pub/static'); + $this->environmentMock->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -160,7 +166,7 @@ public function testExecuteWithDeployInBuildNoClean(): void /** * @throws StepException */ - public function testExecuteWithFileSystemException() + public function testExecuteWithFileSystemException(): void { $this->expectExceptionCode(Error::DEPLOY_SCD_CLEAN_FAILED); $this->expectException(StepException::class); @@ -170,13 +176,16 @@ public function testExecuteWithFileSystemException() ->method('exists') ->with(FlagManager::FLAG_STATIC_CONTENT_DEPLOY_IN_BUILD) ->willReturn(true); - $this->stageConfigMock->expects($this->once()) - ->method('get') - ->with(DeployInterface::VAR_CLEAN_STATIC_FILES) - ->willReturn(true); + $this->stageConfigMock->method('get') + ->willReturnMap([ + [DeployInterface::VAR_CLEAN_STATIC_FILES, true] + ]); $this->fileMock->expects($this->once()) ->method('backgroundClearDirectory') ->willThrowException(new FileSystemException('some error')); + $this->environmentMock->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(true); $this->step->execute(); } @@ -184,7 +193,7 @@ public function testExecuteWithFileSystemException() /** * @throws StepException */ - public function testExecuteWithGenericException() + public function testExecuteWithGenericException(): void { $this->expectExceptionCode(10); $this->expectException(StepException::class); @@ -200,4 +209,27 @@ public function testExecuteWithGenericException() $this->step->execute(); } + + public function testExecuteWithDeployInBuildCleanNoMoveScd(): void + { + $this->flagManagerMock->expects(self::once()) + ->method('exists') + ->with(FlagManager::FLAG_STATIC_CONTENT_DEPLOY_IN_BUILD) + ->willReturn(true); + $this->stageConfigMock->method('get') + ->willReturnMap([ + [DeployInterface::VAR_CLEAN_STATIC_FILES, true] + ]); + $this->directoryListMock->expects(self::never()) + ->method('getMagentoRoot') + ->willReturn('magento_root'); + $this->fileMock->expects(self::never()) + ->method('backgroundClearDirectory') + ->with('magento_root/pub/static'); + $this->environmentMock->method('hasMount') + ->with(Environment::MOUNT_PUB_STATIC) + ->willReturn(false); + + $this->step->execute(); + } } diff --git a/src/Util/BuildDirCopier.php b/src/Util/BuildDirCopier.php index 839d7b8ea4..64a0a26477 100644 --- a/src/Util/BuildDirCopier.php +++ b/src/Util/BuildDirCopier.php @@ -56,7 +56,7 @@ public function __construct( * * @throws UndefinedPackageException */ - public function copy(string $dir, string $strategyName) + public function copy(string $dir, string $strategyName): void { try { $magentoRoot = $this->directoryList->getMagentoRoot(); From e3b13ca023afe324cd93d0caa2fd62d5683b60b9 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Mon, 21 Sep 2020 08:52:55 -0500 Subject: [PATCH 08/17] MCLOUD-6937: Deployment is not failed if install Magento 2.4.0 with mysql search engine (#782) --- config/schema.error.yaml | 10 ++- dist/error-codes.md | 3 +- src/App/Error.php | 3 +- src/Command/Dev/GenerateSchemaError.php | 1 + .../Deploy/ElasticSearchIntegrity.php | 2 +- .../Validator/Deploy/SearchConfiguration.php | 51 ++++++++++---- .../Deploy/ElasticsearchIntegrityTest.php | 2 +- .../Deploy/SearchConfigurationTest.php | 67 ++++++++++++++++++- 8 files changed, 117 insertions(+), 22 deletions(-) diff --git a/config/schema.error.yaml b/config/schema.error.yaml index 8d18abf819..5ae9639af5 100644 --- a/config/schema.error.yaml +++ b/config/schema.error.yaml @@ -322,7 +322,7 @@ type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_ES_CANNOT_CONNECT: title: 'Can not connect to the Elasticsearch service' - suggestion: 'Check that credentials for elasticsearch are correct and service is running' + suggestion: 'Check for valid elasticsearch credentials and verify that the service is running' stage: deploy type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_WRONG_BRAINTREE_VARIABLE: @@ -331,12 +331,18 @@ suggestion: 'Support for the Braintree module is no longer included with Magento 2.4.0 and later. Remove the CONFIG__STORES__DEFAULT__PAYMENT__BRAINTREE__CHANNEL variable from the variables section of the .magento.app.yaml file. For Braintree support, use an official Braintree Payments extension from the Magento Marketplace instead.' stage: deploy type: critical -!php/const Magento\MagentoCloud\App\Error::DEPLOY_WRONG_SEARCH_ENGINE: +!php/const Magento\MagentoCloud\App\Error::DEPLOY_ES_SERVICE_NOT_INSTALLED: step: validate-config title: 'Magento 2.4.0 requires Elasticsearch service to be installed' suggestion: 'Install Elasticsearch service' stage: deploy type: critical +!php/const Magento\MagentoCloud\App\Error::DEPLOY_WRONG_SEARCH_ENGINE: + step: validate-config + title: 'The search engine must be set to Elasticsearch for Magento >= 2.4.0' + suggestion: 'Check the SEARCH_CONFIGURATION variable for the `engine` option. If it is configured, remove the option, or set the value to "elasticsearch".' + stage: deploy + type: critical !php/const Magento\MagentoCloud\App\Error::PD_DEPLOY_IS_FAILED: step: is-deploy-failed title: 'Deploy stage failed' diff --git a/dist/error-codes.md b/dist/error-codes.md index ab36d0d0b9..1d879874b3 100644 --- a/dist/error-codes.md +++ b/dist/error-codes.md @@ -71,9 +71,10 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 129 | install-update: reset-password | Unable to read reset password template | | | 130 | install-update: cache_type | Command failed: `php ./bin/magento cache:enable` | Command `php ./bin/magento cache:enable` runs only when Magento was installed but `./app/etc/env.php` file was absent or empty at the beginning of the deployment. Check the `cloud.log` for more information. Add `VERBOSE_COMMANDS: '-vvv'` into `.magento.env.yaml` for more detailed command output. | | 131 | install-update | The `crypt/key` key value does not exist in the `./app/etc/env.php` file or the `CRYPT_KEY` cloud environment variable | This error occurs if the `./app/etc/env.php` file is not present when Magento deployment begins, or if the `crypt/key` value is undefined. If you migrated the database from another environment, retrieve the crypt key value from that environment. Then, add the value to the [CRYPT_KEY](https://devdocs.magento.com/cloud/env/variables-deploy.html#crypt_key) cloud environment variable in your current environment. See [Add the Magento encryption key](https://devdocs.magento.com/cloud/setup/first-time-setup-import-import.html#encryption-key). If you accidentally removed the `./app/etc/env.php` file, use the following command to restore it from the backup files created from a previous deployment: `./vendor/bin/ece-tools backup:restore` CLI command ." | -| 132 | | Can not connect to the Elasticsearch service | Check that credentials for elasticsearch are correct and service is running | +| 132 | | Can not connect to the Elasticsearch service | Check for valid elasticsearch credentials and verify that the service is running | | 133 | validate-config | Remove Magento Braintree module configuration which is no longer supported in Magento 2.4 and later versions. | Support for the Braintree module is no longer included with Magento 2.4.0 and later. Remove the CONFIG__STORES__DEFAULT__PAYMENT__BRAINTREE__CHANNEL variable from the variables section of the .magento.app.yaml file. For Braintree support, use an official Braintree Payments extension from the Magento Marketplace instead. | | 134 | validate-config | Magento 2.4.0 requires Elasticsearch service to be installed | Install Elasticsearch service | +| 135 | validate-config | The search engine must be set to Elasticsearch for Magento >= 2.4.0 | Check the SEARCH_CONFIGURATION variable for the `engine` option. If it is configured, remove the option, or set the value to "elasticsearch". | ### Post-deploy stage diff --git a/src/App/Error.php b/src/App/Error.php index 80143277f2..79d726fe4c 100644 --- a/src/App/Error.php +++ b/src/App/Error.php @@ -73,7 +73,8 @@ class Error public const DEPLOY_CRYPT_KEY_IS_ABSENT = 131; public const DEPLOY_ES_CANNOT_CONNECT = 132; public const DEPLOY_WRONG_BRAINTREE_VARIABLE = 133; - public const DEPLOY_WRONG_SEARCH_ENGINE = 134; + public const DEPLOY_ES_SERVICE_NOT_INSTALLED = 134; + public const DEPLOY_WRONG_SEARCH_ENGINE = 135; public const PD_DEPLOY_IS_FAILED = 201; public const PD_ENV_PHP_IS_NOT_WRITABLE = 202; diff --git a/src/Command/Dev/GenerateSchemaError.php b/src/Command/Dev/GenerateSchemaError.php index 6a38625677..10c940dda8 100644 --- a/src/Command/Dev/GenerateSchemaError.php +++ b/src/Command/Dev/GenerateSchemaError.php @@ -51,6 +51,7 @@ class GenerateSchemaError extends Command width: 200px; } + EOT; /** diff --git a/src/Config/Validator/Deploy/ElasticSearchIntegrity.php b/src/Config/Validator/Deploy/ElasticSearchIntegrity.php index 67f0fe1cd4..6095a179da 100644 --- a/src/Config/Validator/Deploy/ElasticSearchIntegrity.php +++ b/src/Config/Validator/Deploy/ElasticSearchIntegrity.php @@ -60,7 +60,7 @@ public function validate(): Validator\ResultInterface if ($this->magentoVersion->isGreaterOrEqual('2.4.0') && !$this->elasticsearch->isInstalled() ) { - return $this->resultFactory->errorByCode(Error::DEPLOY_WRONG_SEARCH_ENGINE); + return $this->resultFactory->errorByCode(Error::DEPLOY_ES_SERVICE_NOT_INSTALLED); } } catch (UndefinedPackageException | FileSystemException $exception) { throw new ValidatorException($exception->getMessage(), $exception->getCode(), $exception); diff --git a/src/Config/Validator/Deploy/SearchConfiguration.php b/src/Config/Validator/Deploy/SearchConfiguration.php index 0fe1509e9c..dd6dfd8507 100644 --- a/src/Config/Validator/Deploy/SearchConfiguration.php +++ b/src/Config/Validator/Deploy/SearchConfiguration.php @@ -8,11 +8,15 @@ namespace Magento\MagentoCloud\Config\Validator\Deploy; use Magento\MagentoCloud\App\Error; +use Magento\MagentoCloud\App\GenericException; use Magento\MagentoCloud\Config\ConfigMerger; use Magento\MagentoCloud\Config\Stage\DeployInterface; use Magento\MagentoCloud\Config\Validator; use Magento\MagentoCloud\Config\Validator\ResultFactory; +use Magento\MagentoCloud\Config\ValidatorException; use Magento\MagentoCloud\Config\ValidatorInterface; +use Magento\MagentoCloud\Package\MagentoVersion; +use Magento\MagentoCloud\Service\ElasticSearch; /** * Validates SEARCH_CONFIGURATION variable @@ -34,39 +38,60 @@ class SearchConfiguration implements ValidatorInterface */ private $configMerger; + /** + * @var MagentoVersion + */ + private $magentoVersion; + /** * @param ResultFactory $resultFactory * @param DeployInterface $stageConfig * @param ConfigMerger $configMerger + * @param MagentoVersion $magentoVersion */ public function __construct( ResultFactory $resultFactory, DeployInterface $stageConfig, - ConfigMerger $configMerger + ConfigMerger $configMerger, + MagentoVersion $magentoVersion ) { $this->resultFactory = $resultFactory; $this->stageConfig = $stageConfig; $this->configMerger = $configMerger; + $this->magentoVersion = $magentoVersion; } /** - * Checks that SEARCH_CONFIGURATION variable contains at least 'engine' option if _merge was not set + * Checks that SEARCH_CONFIGURATION variable contains at least 'engine' option if _merge was not set. + * Checks that search engine for Magento 2.4 is set to elasticsearch * - * @return Validator\ResultInterface + * {@inheritDoc} */ public function validate(): Validator\ResultInterface { - $searchConfig = $this->stageConfig->get(DeployInterface::VAR_SEARCH_CONFIGURATION); - if ($this->configMerger->isEmpty($searchConfig) || $this->configMerger->isMergeRequired($searchConfig)) { - return $this->resultFactory->success(); - } + try { + $searchConfig = $this->stageConfig->get(DeployInterface::VAR_SEARCH_CONFIGURATION); + + if ($this->magentoVersion->isGreaterOrEqual('2.4.0') + && isset($searchConfig['engine']) + && $searchConfig['engine'] != ElasticSearch::ENGINE_NAME + ) { + return $this->resultFactory->errorByCode(Error::DEPLOY_WRONG_SEARCH_ENGINE); + } + + if ($this->configMerger->isEmpty($searchConfig) || $this->configMerger->isMergeRequired($searchConfig)) { + return $this->resultFactory->success(); + } - if (!isset($searchConfig['engine'])) { - return $this->resultFactory->error( - sprintf('Variable %s is not configured properly', DeployInterface::VAR_SEARCH_CONFIGURATION), - 'At least engine option must be configured', - Error::DEPLOY_WRONG_CONFIGURATION_SEARCH - ); + if (!isset($searchConfig['engine'])) { + return $this->resultFactory->error( + sprintf('Variable %s is not configured properly', DeployInterface::VAR_SEARCH_CONFIGURATION), + 'At least engine option must be configured', + Error::DEPLOY_WRONG_CONFIGURATION_SEARCH + ); + } + } catch (GenericException $exception) { + throw new ValidatorException($exception->getMessage(), $exception->getCode(), $exception); } return $this->resultFactory->success(); diff --git a/src/Test/Unit/Config/Validator/Deploy/ElasticsearchIntegrityTest.php b/src/Test/Unit/Config/Validator/Deploy/ElasticsearchIntegrityTest.php index 3effc2fce5..717f44b946 100644 --- a/src/Test/Unit/Config/Validator/Deploy/ElasticsearchIntegrityTest.php +++ b/src/Test/Unit/Config/Validator/Deploy/ElasticsearchIntegrityTest.php @@ -86,7 +86,7 @@ public function testValidateNoElasticSearch(): void ->willReturn(false); $this->resultFactoryMock->expects($this->once()) ->method('errorByCode') - ->with(Error::DEPLOY_WRONG_SEARCH_ENGINE); + ->with(Error::DEPLOY_ES_SERVICE_NOT_INSTALLED); $this->validator->validate(); } diff --git a/src/Test/Unit/Config/Validator/Deploy/SearchConfigurationTest.php b/src/Test/Unit/Config/Validator/Deploy/SearchConfigurationTest.php index 9d47b4a486..29001079f2 100644 --- a/src/Test/Unit/Config/Validator/Deploy/SearchConfigurationTest.php +++ b/src/Test/Unit/Config/Validator/Deploy/SearchConfigurationTest.php @@ -14,6 +14,9 @@ use Magento\MagentoCloud\Config\Validator\Result\Error; use Magento\MagentoCloud\Config\Validator\Result\Success; use Magento\MagentoCloud\Config\Validator\ResultFactory; +use Magento\MagentoCloud\Config\ValidatorException; +use Magento\MagentoCloud\Package\MagentoVersion; +use Magento\MagentoCloud\Package\UndefinedPackageException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -37,6 +40,11 @@ class SearchConfigurationTest extends TestCase */ private $stageConfigMock; + /** + * @var MagentoVersion|MockObject + */ + private $magentoVersionMock; + /** * @inheritdoc */ @@ -47,11 +55,13 @@ protected function setUp() 'error' => $this->createMock(Error::class) ]); $this->stageConfigMock = $this->getMockForAbstractClass(DeployInterface::class); + $this->magentoVersionMock = $this->createMock(MagentoVersion::class); $this->validator = new SearchConfiguration( $this->resultFactoryMock, $this->stageConfigMock, - new ConfigMerger() + new ConfigMerger(), + $this->magentoVersionMock ); } @@ -75,10 +85,16 @@ public function testErrorCode() /** * @param array $searchConfiguration * @param string $expectedResultClass + * @param bool $isMagento24plus * @dataProvider validateDataProvider + * @throws ValidatorException */ - public function testValidate(array $searchConfiguration, string $expectedResultClass) + public function testValidate(array $searchConfiguration, string $expectedResultClass, bool $isMagento24plus = false) { + $this->magentoVersionMock->expects($this->once()) + ->method('isGreaterOrEqual') + ->with('2.4.0') + ->willReturn($isMagento24plus); $this->stageConfigMock->expects($this->once()) ->method('get') ->with(DeployInterface::VAR_SEARCH_CONFIGURATION) @@ -125,13 +141,58 @@ public function validateDataProvider(): array Success::class, ], [ - [ 'engine' => 'mysql', '_merge' => true, ], Success::class, ], + [ + [], + Success::class, + true + ], + [ + [ + 'engine' => 'mysql', + '_merge' => true, + ], + Error::class, + true + ], + [ + [ + 'engine' => 'mysql', + ], + Error::class, + true + ], + [ + [ + 'engine' => 'elasticsearch', + ], + Success::class, + true + ], ]; } + + /** + * @throws ValidatorException + */ + public function testValidateWithException() + { + $this->expectException(ValidatorException::class); + $this->expectExceptionMessage('some error'); + + $this->stageConfigMock->expects($this->once()) + ->method('get') + ->with(DeployInterface::VAR_SEARCH_CONFIGURATION) + ->willReturn(['engine' => 'elasticsearch']); + $this->magentoVersionMock->expects($this->once()) + ->method('isGreaterOrEqual') + ->willThrowException(new UndefinedPackageException('some error')); + + $this->validator->validate(); + } } From e4bb6c3c441d29c10d1f4c42ebdf87de7e2c154f Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Thu, 8 Oct 2020 13:33:21 -0500 Subject: [PATCH 09/17] MCLOUD-6939: Errors occur running magento commands in composer cloud docker installation (#2) --- config/schema.yaml | 11 +++ src/Config/Stage/BuildInterface.php | 5 ++ src/Step/Build/ComposerDumpAutoload.php | 28 +++++++- src/Test/Unit/Config/SchemaTest.php | 3 +- .../Step/Build/ComposerDumpAutoloadTest.php | 70 +++++++++++++++++-- 5 files changed, 109 insertions(+), 8 deletions(-) diff --git a/config/schema.yaml b/config/schema.yaml index 5d2748b05c..2fd628c61e 100644 --- a/config/schema.yaml +++ b/config/schema.yaml @@ -176,6 +176,17 @@ variables: - stage: global: SKIP_HTML_MINIFICATION: true + SKIP_COMPOSER_DUMP_AUTOLOAD: + description: Skip running compose dump-autoload command + type: boolean + stages: + - build + default: + build: false + examples: + - stage: + build: + SKIP_COMPOSER_DUMP_AUTOLOAD: true SCD_ON_DEMAND: description: Enable generation of static content when requested by a user. Pre-loading the cache using the post_deploy hook reduces site downtime. diff --git a/src/Config/Stage/BuildInterface.php b/src/Config/Stage/BuildInterface.php index 6b8f852b00..f8919dccdd 100644 --- a/src/Config/Stage/BuildInterface.php +++ b/src/Config/Stage/BuildInterface.php @@ -30,4 +30,9 @@ interface BuildInterface extends StageConfigInterface * Magento quality patches list */ public const VAR_QUALITY_PATCHES = 'QUALITY_PATCHES'; + + /** + * Skip composer dump-autoload + */ + public const VAR_SKIP_COMPOSER_DUMP_AUTOLOAD = 'SKIP_COMPOSER_DUMP_AUTOLOAD'; } diff --git a/src/Step/Build/ComposerDumpAutoload.php b/src/Step/Build/ComposerDumpAutoload.php index 015f655c99..65a0c89fc6 100644 --- a/src/Step/Build/ComposerDumpAutoload.php +++ b/src/Step/Build/ComposerDumpAutoload.php @@ -8,10 +8,13 @@ namespace Magento\MagentoCloud\Step\Build; use Magento\MagentoCloud\App\Error; +use Magento\MagentoCloud\Config\ConfigException; use Magento\MagentoCloud\Step\StepException; use Magento\MagentoCloud\Step\StepInterface; use Magento\MagentoCloud\Shell\ShellException; use Magento\MagentoCloud\Shell\ShellInterface; +use Magento\MagentoCloud\Config\Stage\BuildInterface; +use Psr\Log\LoggerInterface; /** * @inheritdoc @@ -23,12 +26,25 @@ class ComposerDumpAutoload implements StepInterface */ private $shell; + /** + * @var BuildInterface + */ + private $stageConfig; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param ShellInterface $shell + * @param BuildInterface $stageConfig + * @param LoggerInterface $logger */ - public function __construct(ShellInterface $shell) + public function __construct(ShellInterface $shell, BuildInterface $stageConfig, LoggerInterface $logger) { $this->shell = $shell; + $this->stageConfig = $stageConfig; + $this->logger = $logger; } /** @@ -37,7 +53,17 @@ public function __construct(ShellInterface $shell) public function execute() { try { + if ($this->stageConfig->get(BuildInterface::VAR_SKIP_COMPOSER_DUMP_AUTOLOAD)) { + $this->logger->info(sprintf( + 'The composer dump-autoload command was skipped as %s variable is set to true', + BuildInterface::VAR_SKIP_COMPOSER_DUMP_AUTOLOAD + )); + + return; + } $this->shell->execute('composer dump-autoload -o --ansi --no-interaction'); + } catch (ConfigException $e) { + throw new StepException($e->getMessage(), $e->getCode(), $e); } catch (ShellException $e) { throw new StepException($e->getMessage(), Error::BUILD_COMPOSER_DUMP_AUTOLOAD_FAILED, $e); } diff --git a/src/Test/Unit/Config/SchemaTest.php b/src/Test/Unit/Config/SchemaTest.php index fba9691931..2adc45778c 100644 --- a/src/Test/Unit/Config/SchemaTest.php +++ b/src/Test/Unit/Config/SchemaTest.php @@ -77,7 +77,8 @@ public function testGetDefaultsForBuild(): void BuildInterface::VAR_SCD_MAX_EXEC_TIME => null, BuildInterface::VAR_ERROR_REPORT_DIR_NESTING_LEVEL => 1, BuildInterface::VAR_SCD_USE_BALER => false, - BuildInterface::VAR_QUALITY_PATCHES => [] + BuildInterface::VAR_QUALITY_PATCHES => [], + BuildInterface::VAR_SKIP_COMPOSER_DUMP_AUTOLOAD => false, ], $this->schema->getDefaults(StageConfigInterface::STAGE_BUILD) ); diff --git a/src/Test/Unit/Step/Build/ComposerDumpAutoloadTest.php b/src/Test/Unit/Step/Build/ComposerDumpAutoloadTest.php index 6d151f5d2a..04fab70783 100644 --- a/src/Test/Unit/Step/Build/ComposerDumpAutoloadTest.php +++ b/src/Test/Unit/Step/Build/ComposerDumpAutoloadTest.php @@ -8,12 +8,15 @@ namespace Magento\MagentoCloud\Test\Unit\Step\Build; use Magento\MagentoCloud\App\Error; +use Magento\MagentoCloud\Config\ConfigException; +use Magento\MagentoCloud\Config\Stage\BuildInterface; use Magento\MagentoCloud\Step\Build\ComposerDumpAutoload; use Magento\MagentoCloud\Shell\ShellException; use Magento\MagentoCloud\Shell\ShellInterface; use Magento\MagentoCloud\Step\StepException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** * @inheritdoc @@ -28,18 +31,31 @@ class ComposerDumpAutoloadTest extends TestCase /** * @var ShellInterface|MockObject */ - private $shell; + private $shellMock; + + /** + * @var BuildInterface|MockObject + */ + private $stageConfigMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; /** * @inheritdoc */ protected function setUp(): void { - $this->shell = $this->getMockBuilder(ShellInterface::class) - ->getMockForAbstractClass(); + $this->shellMock = $this->getMockForAbstractClass(ShellInterface::class); + $this->stageConfigMock = $this->getMockForAbstractClass(BuildInterface::class); + $this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class); $this->step = new ComposerDumpAutoload( - $this->shell + $this->shellMock, + $this->stageConfigMock, + $this->loggerMock ); } @@ -48,13 +64,37 @@ protected function setUp(): void */ public function testExecute(): void { - $this->shell->expects($this->once()) + $this->stageConfigMock->expects($this->once()) + ->method('get') + ->with(BuildInterface::VAR_SKIP_COMPOSER_DUMP_AUTOLOAD) + ->willReturn(false); + $this->shellMock->expects($this->once()) ->method('execute') ->with('composer dump-autoload -o --ansi --no-interaction'); $this->step->execute(); } + /** + * @throws StepException + */ + public function testExecuteDumpSkipped(): void + { + $this->stageConfigMock->expects($this->once()) + ->method('get') + ->with(BuildInterface::VAR_SKIP_COMPOSER_DUMP_AUTOLOAD) + ->willReturn(true); + $this->loggerMock->expects($this->once()) + ->method('info') + ->with( + 'The composer dump-autoload command was skipped as SKIP_COMPOSER_DUMP_AUTOLOAD variable is set to true' + ); + $this->shellMock->expects($this->never()) + ->method('execute'); + + $this->step->execute(); + } + /** * @throws StepException */ @@ -64,11 +104,29 @@ public function testExecuteWithException(): void $this->expectExceptionMessage('something went wrong'); $this->expectExceptionCode(Error::BUILD_COMPOSER_DUMP_AUTOLOAD_FAILED); - $this->shell->expects($this->once()) + $this->shellMock->expects($this->once()) ->method('execute') ->with('composer dump-autoload -o --ansi --no-interaction') ->willThrowException(new ShellException('something went wrong')); $this->step->execute(); } + + /** + * @throws StepException + */ + public function testExecuteWithConfigException(): void + { + $this->expectException(StepException::class); + $this->expectExceptionMessage('something went wrong'); + $this->expectExceptionCode(10); + + $this->stageConfigMock->expects($this->once()) + ->method('get') + ->willThrowException(new ConfigException('something went wrong', 10)); + $this->shellMock->expects($this->never()) + ->method('execute'); + + $this->step->execute(); + } } From eaea526fdc48f96a1ab63090da365965e0d16a41 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Fri, 16 Oct 2020 09:47:02 -0500 Subject: [PATCH 10/17] MCLOUD-7072: Add CLI command for generating .magento.env.yaml (#3) --- src/Application.php | 2 + src/Command/ApplyPatches.php | 2 +- src/Command/BackupList.php | 2 +- src/Command/BackupRestore.php | 4 +- src/Command/Build.php | 2 +- src/Command/Build/Generate.php | 2 +- src/Command/Build/Transfer.php | 2 +- src/Command/ConfigCreate.php | 88 ++++++++++ src/Command/ConfigShow.php | 2 +- src/Command/ConfigUpdate.php | 98 +++++++++++ src/Command/CronDisable.php | 2 +- src/Command/CronEnable.php | 2 +- src/Command/CronKill.php | 2 +- src/Command/DbDump.php | 2 +- src/Command/Deploy.php | 2 +- src/Command/Dev/GenerateSchemaError.php | 2 +- src/Command/ErrorShow.php | 2 +- src/Command/GenerateSchema.php | 2 +- src/Command/ModuleRefresh.php | 2 +- src/Command/RunCommand.php | 2 +- src/Command/Wizard/IdealState.php | 2 +- src/Command/Wizard/MasterSlave.php | 2 +- src/Command/Wizard/ScdOnBuild.php | 2 +- src/Command/Wizard/ScdOnDemand.php | 2 +- src/Command/Wizard/ScdOnDeploy.php | 2 +- .../Acceptance/AcceptanceCe71Cest.php | 2 +- .../Acceptance/AdminCredential21Cest.php | 2 +- src/Test/Functional/Acceptance/Cron21Cest.php | 2 +- .../Integration/Command/ConfigCreateTest.php | 110 ++++++++++++ .../Integration/Command/ConfigUpdateTest.php | 110 ++++++++++++ .../.magento.env.dbconfiguration.yaml | 14 ++ .../_files/ConfigCreate/.magento.env.scd.yaml | 3 + .../dbconfiguration/.magento.env.yaml | 11 ++ .../dbconfiguration/.magento.env_exp.yaml | 11 ++ .../ConfigUpdate/scdupdate/.magento.env.yaml | 4 + .../scdupdate/.magento.env_exp.yaml | 4 + src/Test/Unit/ApplicationTest.php | 2 + src/Test/Unit/Command/ConfigCreateTest.php | 121 ++++++++++++++ src/Test/Unit/Command/ConfigUpdateTest.php | 156 ++++++++++++++++++ 39 files changed, 761 insertions(+), 25 deletions(-) create mode 100644 src/Command/ConfigCreate.php create mode 100644 src/Command/ConfigUpdate.php create mode 100644 src/Test/Integration/Command/ConfigCreateTest.php create mode 100644 src/Test/Integration/Command/ConfigUpdateTest.php create mode 100644 src/Test/Integration/Command/_files/ConfigCreate/.magento.env.dbconfiguration.yaml create mode 100644 src/Test/Integration/Command/_files/ConfigCreate/.magento.env.scd.yaml create mode 100644 src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env.yaml create mode 100644 src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env_exp.yaml create mode 100644 src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env.yaml create mode 100644 src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env_exp.yaml create mode 100644 src/Test/Unit/Command/ConfigCreateTest.php create mode 100644 src/Test/Unit/Command/ConfigUpdateTest.php diff --git a/src/Application.php b/src/Application.php index ff0c510c9e..cda00402bc 100644 --- a/src/Application.php +++ b/src/Application.php @@ -75,6 +75,8 @@ protected function getDefaultCommands() $this->container->create(Command\CronKill::class), $this->container->create(Command\CronUnlock::class), $this->container->create(Command\ConfigShow::class), + $this->container->create(Command\ConfigCreate::class), + $this->container->create(Command\ConfigUpdate::class), $this->container->create(Command\RunCommand::class), $this->container->create(Command\GenerateSchema::class), $this->container->create(Command\ErrorShow::class) diff --git a/src/Command/ApplyPatches.php b/src/Command/ApplyPatches.php index d4c5c70539..f8802358c2 100644 --- a/src/Command/ApplyPatches.php +++ b/src/Command/ApplyPatches.php @@ -43,7 +43,7 @@ public function __construct(Manager $manager) protected function configure(): void { $this->setName(self::NAME) - ->setDescription('Applies custom patches'); + ->setDescription('Applies custom patches.'); parent::configure(); } diff --git a/src/Command/BackupList.php b/src/Command/BackupList.php index 4ae77d40e6..efa4d9ffed 100644 --- a/src/Command/BackupList.php +++ b/src/Command/BackupList.php @@ -55,7 +55,7 @@ public function __construct( protected function configure() { $this->setName(self::NAME) - ->setDescription('Shows the list of backup files'); + ->setDescription('Shows the list of backup files.'); parent::configure(); } diff --git a/src/Command/BackupRestore.php b/src/Command/BackupRestore.php index 323b9399a3..53e3ada4fe 100644 --- a/src/Command/BackupRestore.php +++ b/src/Command/BackupRestore.php @@ -54,7 +54,9 @@ public function __construct(Restore $restore, LoggerInterface $logger) protected function configure() { $this->setName(self::NAME) - ->setDescription('Restore important configuration files. Run backup:list to show the list of backup files'); + ->setDescription( + 'Restore important configuration files. Run backup:list to show the list of backup files.' + ); $this->addOption( 'force', 'f', diff --git a/src/Command/Build.php b/src/Command/Build.php index 0c6cd4e3cf..445c05cc4c 100755 --- a/src/Command/Build.php +++ b/src/Command/Build.php @@ -28,7 +28,7 @@ class Build extends Command protected function configure() { $this->setName(static::NAME) - ->setDescription('Builds application'); + ->setDescription('Builds application.'); parent::configure(); } diff --git a/src/Command/Build/Generate.php b/src/Command/Build/Generate.php index 6a3bc8aca1..1b4634c401 100644 --- a/src/Command/Build/Generate.php +++ b/src/Command/Build/Generate.php @@ -44,7 +44,7 @@ public function __construct(Processor $processor) protected function configure() { $this->setName(static::NAME) - ->setDescription('Generates all necessary files for build stage'); + ->setDescription('Generates all necessary files for build stage.'); parent::configure(); } diff --git a/src/Command/Build/Transfer.php b/src/Command/Build/Transfer.php index 8ebb1ea955..bbdc7111a8 100644 --- a/src/Command/Build/Transfer.php +++ b/src/Command/Build/Transfer.php @@ -43,7 +43,7 @@ public function __construct(Processor $processor) protected function configure() { $this->setName(static::NAME) - ->setDescription('Transfer generated files into init directory'); + ->setDescription('Transfers generated files into init directory.'); parent::configure(); } diff --git a/src/Command/ConfigCreate.php b/src/Command/ConfigCreate.php new file mode 100644 index 0000000000..99e012a182 --- /dev/null +++ b/src/Command/ConfigCreate.php @@ -0,0 +1,88 @@ +configFileList = $configFileList; + $this->file = $file; + + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $this->setName(static::NAME) + ->setDescription( + 'Creates a `.magento.env.yaml` file with the specified build, deploy, and post-deploy variable ' . + 'configuration. Overwrites any existing `.magento,.env.yaml` file.' + ) + ->addArgument( + self::ARG_CONFIGURATION, + InputArgument::REQUIRED, + 'Configuration in JSON format' + ); + + parent::configure(); + } + + /** + * @inheritDoc + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $configuration = $input->getArgument(self::ARG_CONFIGURATION); + + $decodedConfig = json_decode($configuration, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception('Wrong JSON format: ' . json_last_error_msg()); + } + + $yaml = Yaml::dump($decodedConfig, 10, 2); + $filePath = $this->configFileList->getEnvConfig(); + + $this->file->filePutContents($filePath, $yaml); + + $output->writeln(sprintf("Config file %s was created", $filePath)); + } +} diff --git a/src/Command/ConfigShow.php b/src/Command/ConfigShow.php index 4e70711773..4fbb6d6f7b 100644 --- a/src/Command/ConfigShow.php +++ b/src/Command/ConfigShow.php @@ -66,7 +66,7 @@ public function __construct( protected function configure() { $this->setName(static::NAME) - ->setDescription('Display encoded cloud configuration environment variables') + ->setDescription('Display encoded cloud configuration environment variables.') ->addArgument( 'variable', InputArgument::IS_ARRAY, diff --git a/src/Command/ConfigUpdate.php b/src/Command/ConfigUpdate.php new file mode 100644 index 0000000000..0b69146684 --- /dev/null +++ b/src/Command/ConfigUpdate.php @@ -0,0 +1,98 @@ +configFileList = $configFileList; + $this->file = $file; + $this->reader = $reader; + + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $this->setName(static::NAME) + ->setDescription( + 'Updates the existing `.magento.env.yaml` file with the specified configuration. ' . + 'Creates `.magento.env.yaml` file if it does not exist.' + ) + ->addArgument( + self::ARG_CONFIGURATION, + InputArgument::REQUIRED, + 'Configuration in JSON format' + ); + + parent::configure(); + } + + /** + * @inheritDoc + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $configuration = $input->getArgument(self::ARG_CONFIGURATION); + + $decodedConfig = json_decode($configuration, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception('Wrong JSON format: ' . json_last_error_msg()); + } + + $config = array_replace_recursive($this->reader->read(), $decodedConfig); + + $yaml = Yaml::dump($config, 10, 2); + $filePath = $this->configFileList->getEnvConfig(); + + $this->file->filePutContents($filePath, $yaml); + + $output->writeln(sprintf("Config file %s was updated", $filePath)); + } +} diff --git a/src/Command/CronDisable.php b/src/Command/CronDisable.php index b49be3e92a..470c4e8fec 100644 --- a/src/Command/CronDisable.php +++ b/src/Command/CronDisable.php @@ -60,7 +60,7 @@ public function __construct( protected function configure() { $this->setName(static::NAME) - ->setDescription('Disable all Magento cron processes and kills currently running'); + ->setDescription('Disable all Magento cron processes and terminates all running processes.'); parent::configure(); } diff --git a/src/Command/CronEnable.php b/src/Command/CronEnable.php index 501456efdd..4a321640d2 100644 --- a/src/Command/CronEnable.php +++ b/src/Command/CronEnable.php @@ -49,7 +49,7 @@ public function __construct(Switcher $cronSwitcher, LoggerInterface $logger) protected function configure() { $this->setName(static::NAME) - ->setDescription('Enable Magento cron processes'); + ->setDescription('Enables Magento cron processes.'); parent::configure(); } diff --git a/src/Command/CronKill.php b/src/Command/CronKill.php index 16277c344d..388e81a604 100644 --- a/src/Command/CronKill.php +++ b/src/Command/CronKill.php @@ -42,7 +42,7 @@ public function __construct(BackgroundProcess $backgroundProcess) protected function configure() { $this->setName(static::NAME) - ->setDescription('Kill all Magento cron processes'); + ->setDescription('Terminates all Magento cron processes.'); parent::configure(); } diff --git a/src/Command/DbDump.php b/src/Command/DbDump.php index e0baec754c..58b006132f 100644 --- a/src/Command/DbDump.php +++ b/src/Command/DbDump.php @@ -60,7 +60,7 @@ public function __construct( protected function configure() { $this->setName(self::NAME) - ->setDescription('Creates backups of databases'); + ->setDescription('Creates database backups.'); $this->addArgument( self::ARGUMENT_DATABASES, InputArgument::IS_ARRAY, diff --git a/src/Command/Deploy.php b/src/Command/Deploy.php index 6dc506afee..e6f6adf85e 100755 --- a/src/Command/Deploy.php +++ b/src/Command/Deploy.php @@ -52,7 +52,7 @@ public function __construct(Processor $processor, Manager $flagManager) protected function configure() { $this->setName(static::NAME) - ->setDescription('Deploys application'); + ->setDescription('Deploys the application.'); parent::configure(); } diff --git a/src/Command/Dev/GenerateSchemaError.php b/src/Command/Dev/GenerateSchemaError.php index 10c940dda8..22b7c15a66 100644 --- a/src/Command/Dev/GenerateSchemaError.php +++ b/src/Command/Dev/GenerateSchemaError.php @@ -73,7 +73,7 @@ public function __construct(File $file, FileList $fileList) protected function configure(): void { $this->setName(static::NAME) - ->setDescription('Generates dist/error-codes.md file from schema.error.yaml'); + ->setDescription('Generates the dist/error-codes.md file from the schema.error.yaml file.'); parent::configure(); } diff --git a/src/Command/ErrorShow.php b/src/Command/ErrorShow.php index 54e2320068..d73ac1a5e0 100644 --- a/src/Command/ErrorShow.php +++ b/src/Command/ErrorShow.php @@ -53,7 +53,7 @@ public function __construct(ErrorInfo $errorInfo, ReaderInterface $reader) protected function configure() { $this->setName(self::NAME) - ->setDescription('Display info about error by error id or info about all errors from the last deployment') + ->setDescription('Displays info about error by error id or info about all errors from the last deployment.') ->addArgument( self::ARGUMENT_ERROR_CODE, InputArgument::OPTIONAL, diff --git a/src/Command/GenerateSchema.php b/src/Command/GenerateSchema.php index b2c10013ed..9bfa53808a 100644 --- a/src/Command/GenerateSchema.php +++ b/src/Command/GenerateSchema.php @@ -68,7 +68,7 @@ public function __construct( */ protected function configure(): void { - $this->setDescription('Generate the schema dist file'); + $this->setDescription('Generates the schema *.dist file.'); parent::configure(); } diff --git a/src/Command/ModuleRefresh.php b/src/Command/ModuleRefresh.php index 702e1f3282..3fabdbacbc 100644 --- a/src/Command/ModuleRefresh.php +++ b/src/Command/ModuleRefresh.php @@ -44,7 +44,7 @@ public function __construct(Module $module) protected function configure() { $this->setName(self::NAME) - ->setDescription('Refresh config to enable newly added modules'); + ->setDescription('Refreshes the configuration to enable newly added modules.'); } /** diff --git a/src/Command/RunCommand.php b/src/Command/RunCommand.php index 91046b5e22..b594ce9a52 100644 --- a/src/Command/RunCommand.php +++ b/src/Command/RunCommand.php @@ -44,7 +44,7 @@ public function __construct(Processor $processor) */ protected function configure() { - $this->setDescription('Execute scenario(s)') + $this->setDescription('Execute scenario(s).') ->addArgument( self::ARG_SCENARIO, InputArgument::REQUIRED | InputArgument::IS_ARRAY, diff --git a/src/Command/Wizard/IdealState.php b/src/Command/Wizard/IdealState.php index 8ef2b2189e..a6e96271ac 100644 --- a/src/Command/Wizard/IdealState.php +++ b/src/Command/Wizard/IdealState.php @@ -52,7 +52,7 @@ public function __construct(OutputFormatter $outputFormatter, IdealStateValidato protected function configure() { $this->setName(self::NAME) - ->setDescription('Verifies ideal state of configuration'); + ->setDescription('Verifies ideal state of configuration.'); parent::configure(); } diff --git a/src/Command/Wizard/MasterSlave.php b/src/Command/Wizard/MasterSlave.php index 8239626040..8d9b9e5747 100644 --- a/src/Command/Wizard/MasterSlave.php +++ b/src/Command/Wizard/MasterSlave.php @@ -50,7 +50,7 @@ public function __construct(OutputFormatter $outputFormatter, DeployInterface $d protected function configure() { $this->setName(self::NAME) - ->setDescription('Verifies master-slave configuration'); + ->setDescription('Verifies master-slave configuration.'); } /** diff --git a/src/Command/Wizard/ScdOnBuild.php b/src/Command/Wizard/ScdOnBuild.php index 4d4b1792d7..1cf3cf123d 100644 --- a/src/Command/Wizard/ScdOnBuild.php +++ b/src/Command/Wizard/ScdOnBuild.php @@ -53,7 +53,7 @@ public function __construct( protected function configure() { $this->setName(self::NAME) - ->setDescription('Verifies SCD on build configuration'); + ->setDescription('Verifies SCD on build configuration.'); parent::configure(); } diff --git a/src/Command/Wizard/ScdOnDemand.php b/src/Command/Wizard/ScdOnDemand.php index aac8982276..19ebbd9e63 100644 --- a/src/Command/Wizard/ScdOnDemand.php +++ b/src/Command/Wizard/ScdOnDemand.php @@ -50,7 +50,7 @@ public function __construct(OutputFormatter $outputFormatter, GlobalSection $glo protected function configure() { $this->setName(self::NAME) - ->setDescription('Verifies SCD on demand configuration'); + ->setDescription('Verifies SCD on demand configuration.'); parent::configure(); } diff --git a/src/Command/Wizard/ScdOnDeploy.php b/src/Command/Wizard/ScdOnDeploy.php index 6aa8b0d954..57afac01eb 100644 --- a/src/Command/Wizard/ScdOnDeploy.php +++ b/src/Command/Wizard/ScdOnDeploy.php @@ -51,7 +51,7 @@ public function __construct(OutputFormatter $outputFormatter, ScdOnDeployValidat protected function configure() { $this->setName(self::NAME) - ->setDescription('Verifies SCD on deploy configuration'); + ->setDescription('Verifies SCD on deploy configuration.'); } /** diff --git a/src/Test/Functional/Acceptance/AcceptanceCe71Cest.php b/src/Test/Functional/Acceptance/AcceptanceCe71Cest.php index e04a446664..9f04040dfa 100644 --- a/src/Test/Functional/Acceptance/AcceptanceCe71Cest.php +++ b/src/Test/Functional/Acceptance/AcceptanceCe71Cest.php @@ -22,5 +22,5 @@ class AcceptanceCe71Cest extends AcceptanceCeCest /** * @var string */ - protected $magentoCloudTemplate = '2.1.17'; + protected $magentoCloudTemplate = '2.1.18'; } diff --git a/src/Test/Functional/Acceptance/AdminCredential21Cest.php b/src/Test/Functional/Acceptance/AdminCredential21Cest.php index 8ea34c9f69..3ff42875fe 100644 --- a/src/Test/Functional/Acceptance/AdminCredential21Cest.php +++ b/src/Test/Functional/Acceptance/AdminCredential21Cest.php @@ -15,5 +15,5 @@ class AdminCredential21Cest extends AdminCredentialCest /** * @var string */ - protected $magentoCloudTemplate = '2.1.17'; + protected $magentoCloudTemplate = '2.1.18'; } diff --git a/src/Test/Functional/Acceptance/Cron21Cest.php b/src/Test/Functional/Acceptance/Cron21Cest.php index 25c1c561a1..5a8c18d279 100644 --- a/src/Test/Functional/Acceptance/Cron21Cest.php +++ b/src/Test/Functional/Acceptance/Cron21Cest.php @@ -19,7 +19,7 @@ protected function cronDataProvider(): array { return [ [ - 'version' => '2.1.17', + 'version' => '2.1.18', 'variables' => [ 'MAGENTO_CLOUD_VARIABLES' => [ 'ADMIN_EMAIL' => 'admin@example.com', diff --git a/src/Test/Integration/Command/ConfigCreateTest.php b/src/Test/Integration/Command/ConfigCreateTest.php new file mode 100644 index 0000000000..42af74fcde --- /dev/null +++ b/src/Test/Integration/Command/ConfigCreateTest.php @@ -0,0 +1,110 @@ +baseDir); + + $this->command = new ConfigCreate( + $container->get(ConfigFileList::class), + $container->get(File::class) + ); + } + + /** + * @param array $inputConfiguration + * @param string $expectedFile + * @throws \ReflectionException + * @dataProvider executeDataProvider + */ + public function testExecute(array $inputConfiguration, string $expectedFile) + { + $inputMock = $this->getMockForAbstractClass(InputInterface::class); + $outputMock = $this->getMockForAbstractClass(OutputInterface::class); + + $inputMock->expects($this->once()) + ->method('getArgument') + ->with(ConfigCreate::ARG_CONFIGURATION) + ->willReturn(json_encode($inputConfiguration)); + + $this->command->execute($inputMock, $outputMock); + + $this->assertEquals( + file_get_contents($this->baseDir . '/' . $expectedFile), + file_get_contents($this->baseDir . '/.magento.env.yaml') + ); + } + + /** + * @return array + */ + public function executeDataProvider(): array + { + return [ + [ + ['stage' => ['build' => ['SCD_THREADS' => 3]]], + '.magento.env.scd.yaml' + ], + [ + [ + 'stage' => [ + 'build' => [ + BuildInterface::VAR_SCD_THREADS => 3, + BuildInterface::VAR_ERROR_REPORT_DIR_NESTING_LEVEL => 10, + ], + 'deploy' => [ + DeployInterface::VAR_DATABASE_CONFIGURATION => [ + '_merge' => true, + 'connection' => [ + 'default' => [ + 'host' => 'localhost', + 'port' => '3307', + 'password' => '1234', + 'user' => 'localuser' + ] + ] + ], + DeployInterface::VAR_LOCK_PROVIDER => 'db', + ], + ] + ], + '.magento.env.dbconfiguration.yaml' + ], + ]; + } +} diff --git a/src/Test/Integration/Command/ConfigUpdateTest.php b/src/Test/Integration/Command/ConfigUpdateTest.php new file mode 100644 index 0000000000..191396207b --- /dev/null +++ b/src/Test/Integration/Command/ConfigUpdateTest.php @@ -0,0 +1,110 @@ +get(ConfigFileList::class), + $container->get(File::class), + $container->get(ReaderInterface::class) + ); + + $inputMock = $this->getMockForAbstractClass(InputInterface::class); + $outputMock = $this->getMockForAbstractClass(OutputInterface::class); + + $inputMock->expects($this->once()) + ->method('getArgument') + ->with(ConfigUpdate::ARG_CONFIGURATION) + ->willReturn(json_encode($inputConfiguration)); + + $command->execute($inputMock, $outputMock); + + $this->assertEquals( + file_get_contents($baseDir . '/.magento.env_exp.yaml'), + file_get_contents($baseDir . '/.magento.env.yaml') + ); + + file_put_contents($baseDir . '/.magento.env.yaml', $tmpMagentoEnvYaml); + } + + /** + * @return array + */ + public function executeDataProvider(): array + { + return [ + [ + [ + 'stage' => [ + 'build' => [ + BuildInterface::VAR_SCD_THREADS => 6, + BuildInterface::VAR_ERROR_REPORT_DIR_NESTING_LEVEL => 12, + ], + ], + ], + $this->baseDir . '/scdupdate' + ], + [ + [ + 'stage' => [ + 'deploy' => [ + DeployInterface::VAR_DATABASE_CONFIGURATION => [ + '_merge' => true, + 'connection' => [ + 'default' => [ + 'host' => '127.0.0.1', + 'port' => '3306', + 'password' => 'newpassword', + 'user' => 'newuser' + ] + ] + ], + DeployInterface::VAR_LOCK_PROVIDER => 'redis', + ], + ] + ], + $this->baseDir . '/dbconfiguration' + ], + ]; + } +} diff --git a/src/Test/Integration/Command/_files/ConfigCreate/.magento.env.dbconfiguration.yaml b/src/Test/Integration/Command/_files/ConfigCreate/.magento.env.dbconfiguration.yaml new file mode 100644 index 0000000000..ba4ac3f5c7 --- /dev/null +++ b/src/Test/Integration/Command/_files/ConfigCreate/.magento.env.dbconfiguration.yaml @@ -0,0 +1,14 @@ +stage: + build: + SCD_THREADS: 3 + ERROR_REPORT_DIR_NESTING_LEVEL: 10 + deploy: + DATABASE_CONFIGURATION: + _merge: true + connection: + default: + host: localhost + port: '3307' + password: '1234' + user: localuser + LOCK_PROVIDER: db diff --git a/src/Test/Integration/Command/_files/ConfigCreate/.magento.env.scd.yaml b/src/Test/Integration/Command/_files/ConfigCreate/.magento.env.scd.yaml new file mode 100644 index 0000000000..ea43b54751 --- /dev/null +++ b/src/Test/Integration/Command/_files/ConfigCreate/.magento.env.scd.yaml @@ -0,0 +1,3 @@ +stage: + build: + SCD_THREADS: 3 diff --git a/src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env.yaml b/src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env.yaml new file mode 100644 index 0000000000..2698b46cc0 --- /dev/null +++ b/src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env.yaml @@ -0,0 +1,11 @@ +stage: + deploy: + DATABASE_CONFIGURATION: + _merge: true + connection: + default: + host: localhost + port: '3307' + password: 1234 + user: user + LOCK_PROVIDER: db diff --git a/src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env_exp.yaml b/src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env_exp.yaml new file mode 100644 index 0000000000..c2bd8064ea --- /dev/null +++ b/src/Test/Integration/Command/_files/ConfigUpdate/dbconfiguration/.magento.env_exp.yaml @@ -0,0 +1,11 @@ +stage: + deploy: + DATABASE_CONFIGURATION: + _merge: true + connection: + default: + host: 127.0.0.1 + port: '3306' + password: newpassword + user: newuser + LOCK_PROVIDER: redis diff --git a/src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env.yaml b/src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env.yaml new file mode 100644 index 0000000000..3eed606289 --- /dev/null +++ b/src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env.yaml @@ -0,0 +1,4 @@ +stage: + build: + SCD_THREADS: 3 + ERROR_REPORT_DIR_NESTING_LEVEL: 10 diff --git a/src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env_exp.yaml b/src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env_exp.yaml new file mode 100644 index 0000000000..d5cfebcbda --- /dev/null +++ b/src/Test/Integration/Command/_files/ConfigUpdate/scdupdate/.magento.env_exp.yaml @@ -0,0 +1,4 @@ +stage: + build: + SCD_THREADS: 6 + ERROR_REPORT_DIR_NESTING_LEVEL: 12 diff --git a/src/Test/Unit/ApplicationTest.php b/src/Test/Unit/ApplicationTest.php index bdc2a95577..9de9861c43 100644 --- a/src/Test/Unit/ApplicationTest.php +++ b/src/Test/Unit/ApplicationTest.php @@ -80,6 +80,8 @@ class ApplicationTest extends TestCase Command\CronEnable::NAME => Command\CronEnable::class, Command\CronDisable::NAME => Command\CronDisable::class, Command\ConfigShow::NAME => Command\ConfigShow::class, + Command\ConfigCreate::NAME => Command\ConfigCreate::class, + Command\ConfigUpdate::NAME => Command\ConfigUpdate::class, Command\RunCommand::NAME => Command\RunCommand::class, Command\GenerateSchema::NAME => Command\GenerateSchema::class, Command\ErrorShow::NAME => Command\ErrorShow::class, diff --git a/src/Test/Unit/Command/ConfigCreateTest.php b/src/Test/Unit/Command/ConfigCreateTest.php new file mode 100644 index 0000000000..d693e35f13 --- /dev/null +++ b/src/Test/Unit/Command/ConfigCreateTest.php @@ -0,0 +1,121 @@ +configFileListMock = $this->createMock(ConfigFileList::class); + $this->fileMock = $this->createMock(File::class); + $this->inputMock = $this->getMockForAbstractClass(InputInterface::class); + $this->outputMock = $this->getMockForAbstractClass(OutputInterface::class); + + $this->command = new ConfigCreate( + $this->configFileListMock, + $this->fileMock + ); + } + + /** + * @dataProvider executeDataProvider + * @param string $configuration + * @param string $expected + */ + public function testExecute(string $configuration, string $expected) + { + $this->inputMock->expects($this->once()) + ->method('getArgument') + ->with('configuration') + ->willReturn($configuration); + $this->configFileListMock->expects($this->once()) + ->method('getEnvConfig') + ->willReturn('/path/to/.magento.env.yaml'); + $this->fileMock->expects($this->once()) + ->method('filePutContents') + ->with('/path/to/.magento.env.yaml', $expected); + + $this->command->execute($this->inputMock, $this->outputMock); + } + + public function executeDataProvider(): array + { + return [ + [ + '{"stage":{"build":{"SKIP_COMPOSER_DUMP_AUTOLOAD":false}}}', + 'stage: + build: + SKIP_COMPOSER_DUMP_AUTOLOAD: false +' + ], + [ + '{"stage":{"deploy":{"DATABASE_CONFIGURATION":{"password":"test", "_merge":true}}}}', + 'stage: + deploy: + DATABASE_CONFIGURATION: + password: test + _merge: true +' + ], + ]; + } + + public function testExecuteWithWrongArgument() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessageRegExp('/Wrong JSON format.*/'); + + $this->inputMock->expects($this->once()) + ->method('getArgument') + ->with('configuration') + ->willReturn('wrong-json'); + $this->outputMock->expects($this->never()) + ->method('writeln'); + + $this->command->execute($this->inputMock, $this->outputMock); + } +} diff --git a/src/Test/Unit/Command/ConfigUpdateTest.php b/src/Test/Unit/Command/ConfigUpdateTest.php new file mode 100644 index 0000000000..743c68a189 --- /dev/null +++ b/src/Test/Unit/Command/ConfigUpdateTest.php @@ -0,0 +1,156 @@ +configFileListMock = $this->createMock(ConfigFileList::class); + $this->fileMock = $this->createMock(File::class); + $this->readerMock = $this->getMockForAbstractClass(ReaderInterface::class); + $this->inputMock = $this->getMockForAbstractClass(InputInterface::class); + $this->outputMock = $this->getMockForAbstractClass(OutputInterface::class); + + $this->command = new ConfigUpdate( + $this->configFileListMock, + $this->fileMock, + $this->readerMock + ); + } + + /** + * @dataProvider executeDataProvider + * @param string $configuration + * @param array $currentConfig + * @param string $expected + */ + public function testExecute(string $configuration, array $currentConfig, string $expected) + { + $this->inputMock->expects($this->once()) + ->method('getArgument') + ->with('configuration') + ->willReturn($configuration); + $this->readerMock->expects($this->once()) + ->method('read') + ->willReturn($currentConfig); + $this->configFileListMock->expects($this->once()) + ->method('getEnvConfig') + ->willReturn('/path/to/.magento.env.yaml'); + $this->fileMock->expects($this->once()) + ->method('filePutContents') + ->with('/path/to/.magento.env.yaml', $expected); + + $this->command->execute($this->inputMock, $this->outputMock); + } + + public function executeDataProvider(): array + { + return [ + [ + '{"stage":{"build":{"SKIP_COMPOSER_DUMP_AUTOLOAD":false},"deploy":{"SCD_THREADS":6}}}', + [ + 'stage' => [ + 'build' => [ + 'SCD_THREADS' => 5, + ], + 'deploy' => [ + 'SCD_THREADS' => 4, + ], + ], + ], + 'stage: + build: + SCD_THREADS: 5 + SKIP_COMPOSER_DUMP_AUTOLOAD: false + deploy: + SCD_THREADS: 6 +' + ], + [ + '{"stage":{"deploy":{"DATABASE_CONFIGURATION":{"password":"test test", "_merge":true}}}}', + [ + 'stage' => [ + 'deploy' => [ + 'DATABASE_CONFIGURATION' => [ + 'host' => 'localhost', + 'password' => 'test' + ] + ] + ] + ], + 'stage: + deploy: + DATABASE_CONFIGURATION: + host: localhost + password: \'test test\' + _merge: true +' + ], + ]; + } + + public function testExecuteWithWrongArgument() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessageRegExp('/Wrong JSON format.*/'); + + $this->inputMock->expects($this->once()) + ->method('getArgument') + ->with('configuration') + ->willReturn('wrong-json'); + $this->outputMock->expects($this->never()) + ->method('writeln'); + + $this->command->execute($this->inputMock, $this->outputMock); + } +} From c1ae7b6c841e2688747b4c7f56b45ab44b31b4c9 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Wed, 28 Oct 2020 20:23:47 -0500 Subject: [PATCH 11/17] MCLOUD-7191: Add new supported service versions for Magento 2.4.2/2.3.7 release --- src/Service/Validator.php | 9 +++-- src/Test/Unit/Service/ValidatorTest.php | 45 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Service/Validator.php b/src/Service/Validator.php index 5a9fed8f88..66f6c174c5 100644 --- a/src/Service/Validator.php +++ b/src/Service/Validator.php @@ -43,14 +43,17 @@ class Validator '>=2.3.3' => '^4.0 || ^5.0 || ^6.2', ], ServiceInterface::NAME_REDIS => [ - '*' => '~3.2.0 || ~4.0.0 || ~5.0.0', + '*' => '~3.2.0 || ~4.0.0 || ~5.0.0 || ~6.0.0', ], ServiceInterface::NAME_ELASTICSEARCH => [ '<2.2.0' => '~1.7.0 || ~2.4.0', '>=2.2.0 <2.2.8 || 2.3.0' => '~1.7.0 || ~2.4.0 || ~5.2.0', '>=2.2.8 <2.3.0 || >=2.3.1 <2.3.5' => '~1.7.0 || ~2.4.0 || ~5.2.0 || ~6.5.0', - '>=2.3.5 <2.4.0' => '~1.7.0 || ~2.4.0 || ~5.2.0 || ~6.5.0 || ~6.8.0 || ~7.5.0 || ~7.6.0 || ~7.7.0', - '>=2.4.0' => '~7.5.0 || ~7.6.0 || ~7.7.0', + '>=2.3.5 <2.3.7' => '~1.7.0 || ~2.4.0 || ~5.2.0 || ~6.5.0 || ~6.8.0 || ~7.5.0 || ~7.6.0 || ~7.7.0', + '>=2.3.7 <2.4.0' => '~1.7.0 || ~2.4.0 || ~5.2.0 || ~6.5.0 || ~6.8.0 || ~7.5.0 || ~7.6.0 || ~7.7.0' . + ' || ~7.9.0', + '>=2.4.0 <2.4.2' => '~7.5.0 || ~7.6.0 || ~7.7.0', + '>=2.4.2' => '~7.5.0 || ~7.6.0 || ~7.7.0 || ~7.9.0', ], ServiceInterface::NAME_RABBITMQ => [ '<2.3.0' => '~3.5.0', diff --git a/src/Test/Unit/Service/ValidatorTest.php b/src/Test/Unit/Service/ValidatorTest.php index fa42ad192c..89b56c3fee 100644 --- a/src/Test/Unit/Service/ValidatorTest.php +++ b/src/Test/Unit/Service/ValidatorTest.php @@ -180,6 +180,51 @@ public function validateVersionsDataProvider(): array ], 0 ], + [ + '2.4.1', + [ + ServiceInterface::NAME_PHP => '7.4', + ServiceInterface::NAME_DB => '10.4', + ServiceInterface::NAME_NGINX => '1.9', + ServiceInterface::NAME_VARNISH => '6.2', + ServiceInterface::NAME_REDIS => '5.0', + ServiceInterface::NAME_ELASTICSEARCH => '7.9', // wrong + ServiceInterface::NAME_RABBITMQ => '3.8' + ], + 1 + ], + [ + '2.4.2', + [ + ServiceInterface::NAME_PHP => '7.4', + ServiceInterface::NAME_DB => '10.4', + ServiceInterface::NAME_NGINX => '1.9', + ServiceInterface::NAME_VARNISH => '6.2', + ServiceInterface::NAME_REDIS => '5.0', + ServiceInterface::NAME_ELASTICSEARCH => '7.9', + ServiceInterface::NAME_RABBITMQ => '3.8' + ], + 0 + ], + [ + '2.3.6', + [ + ServiceInterface::NAME_PHP => '7.4', // wrong + ServiceInterface::NAME_DB => '10.2', + ServiceInterface::NAME_NGINX => '1.19', + ServiceInterface::NAME_VARNISH => '6.2', + ServiceInterface::NAME_REDIS => '5.0', + ServiceInterface::NAME_ELASTICSEARCH => '7.9', //wrong + ], + 2 + ], + [ + '2.3.7', + [ + ServiceInterface::NAME_ELASTICSEARCH => '7.9', + ], + 0 + ], [ '2.1.4', [ServiceInterface::NAME_PHP => '5.6'], From a70c9fec7d05b05c3030f922cf91092db572105d Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Wed, 28 Oct 2020 20:28:36 -0500 Subject: [PATCH 12/17] MCLOUD-7191: Add new supported service versions for Magento 2.4.2/2.3.7 release --- src/Test/Unit/Service/ValidatorTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Test/Unit/Service/ValidatorTest.php b/src/Test/Unit/Service/ValidatorTest.php index 89b56c3fee..b46745b890 100644 --- a/src/Test/Unit/Service/ValidatorTest.php +++ b/src/Test/Unit/Service/ValidatorTest.php @@ -107,6 +107,8 @@ public function testValidateNonexistentService() /** * @return array + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function validateVersionsDataProvider(): array { From 3806fe03817dcb3882b2e3d449126cec37595ee8 Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko Date: Wed, 28 Oct 2020 23:41:43 -0500 Subject: [PATCH 13/17] MCLOUD-7098: Validation which compare service versions and magento version doesn't work on PRO (#6) --- src/Config/Validator/Deploy/ServiceEol.php | 8 +- .../Validator/Deploy/ServiceVersion.php | 15 +- src/DB/Data/ConnectionTypes.php | 93 ++++++++ src/DB/Data/RelationshipConnectionFactory.php | 24 +- src/Service/Database.php | 101 +++------ src/Service/Php.php | 16 +- src/Service/RabbitMq.php | 30 ++- src/Service/Redis.php | 37 ++- .../Validator/Deploy/ServiceVersionTest.php | 23 +- src/Test/Unit/DB/Data/ConnectionTypesTest.php | 169 ++++++++++++++ .../RelationshipConnectionFactoryTest.php | 12 +- src/Test/Unit/Service/DatabaseTest.php | 210 +++++++----------- src/Test/Unit/Service/RabbitMqTest.php | 100 ++++++++- src/Test/Unit/Service/RedisTest.php | 128 +++++++++-- 14 files changed, 698 insertions(+), 268 deletions(-) create mode 100644 src/DB/Data/ConnectionTypes.php create mode 100644 src/Test/Unit/DB/Data/ConnectionTypesTest.php diff --git a/src/Config/Validator/Deploy/ServiceEol.php b/src/Config/Validator/Deploy/ServiceEol.php index f1c270b739..96364ba500 100644 --- a/src/Config/Validator/Deploy/ServiceEol.php +++ b/src/Config/Validator/Deploy/ServiceEol.php @@ -8,11 +8,10 @@ namespace Magento\MagentoCloud\Config\Validator\Deploy; use Magento\MagentoCloud\App\Error; +use Magento\MagentoCloud\App\GenericException; use Magento\MagentoCloud\Config\Validator; -use Magento\MagentoCloud\Filesystem\FileSystemException; use Magento\MagentoCloud\Service\EolValidator as EOLValidator; use Magento\MagentoCloud\Config\ValidatorInterface; -use Magento\MagentoCloud\Service\ServiceMismatchException; /** * Class to check if services approaching their EOLs. @@ -52,8 +51,7 @@ public function __construct( /** * Get the defined services and versions and check for their EOLs by error level. * - * @return Validator\ResultInterface - * @throws FileSystemException + * {@inheritDoc} */ public function validate(): Validator\ResultInterface { @@ -70,7 +68,7 @@ public function validate(): Validator\ResultInterface $this->errorLevel == ValidatorInterface::LEVEL_WARNING ? Error::WARN_SERVICE_PASSED_EOL : null ); } - } catch (ServiceMismatchException $e) { + } catch (GenericException $e) { return $this->resultFactory->error('Can\'t validate version of some services: ' . $e->getMessage()); } diff --git a/src/Config/Validator/Deploy/ServiceVersion.php b/src/Config/Validator/Deploy/ServiceVersion.php index 4170186b7b..43ab96cb21 100644 --- a/src/Config/Validator/Deploy/ServiceVersion.php +++ b/src/Config/Validator/Deploy/ServiceVersion.php @@ -14,6 +14,7 @@ use Magento\MagentoCloud\Service\Validator as ServiceVersionValidator; use Magento\MagentoCloud\Config\Validator; use Magento\MagentoCloud\Config\ValidatorInterface; +use Psr\Log\LoggerInterface; /** * Validates installed service versions according to version mapping. @@ -36,19 +37,27 @@ class ServiceVersion implements ValidatorInterface */ private $serviceFactory; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param Validator\ResultFactory $resultFactory * @param ServiceVersionValidator $serviceVersionValidator * @param ServiceFactory $serviceFactory + * @param LoggerInterface $logger */ public function __construct( Validator\ResultFactory $resultFactory, ServiceVersionValidator $serviceVersionValidator, - ServiceFactory $serviceFactory + ServiceFactory $serviceFactory, + LoggerInterface $logger ) { $this->resultFactory = $resultFactory; $this->serviceVersionValidator = $serviceVersionValidator; $this->serviceFactory = $serviceFactory; + $this->logger = $logger; } /** @@ -69,6 +78,10 @@ public function validate(): Validator\ResultInterface foreach ($services as $serviceName) { $service = $this->serviceFactory->create($serviceName); $serviceVersion = $service->getVersion(); + + $logMsq = $serviceVersion ? 'is ' . $serviceVersion : 'is not detected'; + $this->logger->info(sprintf('Version of service \'%s\' %s', $serviceName, $logMsq)); + if ($serviceVersion !== '0' && $error = $this->serviceVersionValidator->validateService($serviceName, $serviceVersion) ) { diff --git a/src/DB/Data/ConnectionTypes.php b/src/DB/Data/ConnectionTypes.php new file mode 100644 index 0000000000..02605986d6 --- /dev/null +++ b/src/DB/Data/ConnectionTypes.php @@ -0,0 +1,93 @@ +environment = $environment; + } + + /** + * @inheritdoc + */ + public function getConfiguration(): array + { + return $this->environment->getRelationship(self::RELATIONSHIP_KEY)[0] ?? []; + } + + /** + * Returns service configuration for slave. + * + * @return array + */ + public function getSlaveConfiguration(): array + { + return $this->environment->getRelationship(self::RELATIONSHIP_SLAVE_KEY)[0] ?? []; + } + + /** + * Returns configuration for quote service. + */ + public function getQuoteConfiguration(): array + { + return $this->environment->getRelationship(self::RELATIONSHIP_QUOTE_KEY)[0] ?? []; + } + + /** + * Returns configuration for quote slave service. + * + * @return array + */ + public function getQuoteSlaveConfiguration(): array + { + return $this->environment->getRelationship(self::RELATIONSHIP_QUOTE_SLAVE_KEY)[0] ?? []; + } + + /** + * Returns configuration for sales service. + */ + public function getSalesConfiguration(): array + { + return $this->environment->getRelationship(self::RELATIONSHIP_SALES_KEY)[0] ?? []; + } + + /** + * Returns configuration for slave sales service. + * + * @return array + */ + public function getSalesSlaveConfiguration(): array + { + return $this->environment->getRelationship(self::RELATIONSHIP_SALES_SLAVE_KEY)[0] ?? []; + } +} diff --git a/src/DB/Data/RelationshipConnectionFactory.php b/src/DB/Data/RelationshipConnectionFactory.php index 4f1db41519..ced3a8f5b5 100644 --- a/src/DB/Data/RelationshipConnectionFactory.php +++ b/src/DB/Data/RelationshipConnectionFactory.php @@ -7,8 +7,6 @@ namespace Magento\MagentoCloud\DB\Data; -use Magento\MagentoCloud\Service\Database; - /** * Responsible for creating and configuring Magento\MagentoCloud\DB\Data\ConnectionInterface instances. */ @@ -24,16 +22,16 @@ class RelationshipConnectionFactory const CONNECTION_SALES_SLAVE = 'sales-slave'; /** - * @var Database + * @var ConnectionTypes */ - private $database; + private $connectionType; /** - * @param Database $database + * @param ConnectionTypes $connectionType */ - public function __construct(Database $database) + public function __construct(ConnectionTypes $connectionType) { - $this->database = $database; + $this->connectionType = $connectionType; } /** @@ -47,22 +45,22 @@ public function create(string $connectionType): ConnectionInterface { switch ($connectionType) { case self::CONNECTION_MAIN: - $configuration = $this->database->getConfiguration(); + $configuration = $this->connectionType->getConfiguration(); break; case self::CONNECTION_SLAVE: - $configuration = $this->database->getSlaveConfiguration(); + $configuration = $this->connectionType->getSlaveConfiguration(); break; case self::CONNECTION_QUOTE_MAIN: - $configuration = $this->database->getQuoteConfiguration(); + $configuration = $this->connectionType->getQuoteConfiguration(); break; case self::CONNECTION_QUOTE_SLAVE: - $configuration = $this->database->getQuoteSlaveConfiguration(); + $configuration = $this->connectionType->getQuoteSlaveConfiguration(); break; case self::CONNECTION_SALES_MAIN: - $configuration = $this->database->getSalesConfiguration(); + $configuration = $this->connectionType->getSalesConfiguration(); break; case self::CONNECTION_SALES_SLAVE: - $configuration = $this->database->getSalesSlaveConfiguration(); + $configuration = $this->connectionType->getSalesSlaveConfiguration(); break; default: throw new \RuntimeException( diff --git a/src/Service/Database.php b/src/Service/Database.php index 6a0e3be181..3c6f37b00b 100644 --- a/src/Service/Database.php +++ b/src/Service/Database.php @@ -7,26 +7,23 @@ namespace Magento\MagentoCloud\Service; -use Magento\MagentoCloud\Config\Environment; +use Magento\MagentoCloud\DB\ConnectionInterface; +use Magento\MagentoCloud\DB\Data\ConnectionTypes; /** - * Returns database service configurations. + * Returns main database service configurations. */ class Database implements ServiceInterface { - const RELATIONSHIP_KEY = 'database'; - const RELATIONSHIP_SLAVE_KEY = 'database-slave'; - - const RELATIONSHIP_QUOTE_KEY = 'database-quote'; - const RELATIONSHIP_QUOTE_SLAVE_KEY = 'database-quote-slave'; - - const RELATIONSHIP_SALES_KEY = 'database-sales'; - const RELATIONSHIP_SALES_SLAVE_KEY = 'database-sales-slave'; + /** + * @var ConnectionTypes + */ + private $connectionType; /** - * @var Environment + * @var ConnectionInterface */ - private $environment; + private $connection; /** * @var string @@ -34,11 +31,15 @@ class Database implements ServiceInterface private $version; /** - * @param Environment $environment + * @param ConnectionTypes $connectionType + * @param ConnectionInterface $connection */ - public function __construct(Environment $environment) - { - $this->environment = $environment; + public function __construct( + ConnectionTypes $connectionType, + ConnectionInterface $connection + ) { + $this->connectionType = $connectionType; + $this->connection = $connection; } /** @@ -46,69 +47,33 @@ public function __construct(Environment $environment) */ public function getConfiguration(): array { - return $this->environment->getRelationship(self::RELATIONSHIP_KEY)[0] ?? []; - } - - /** - * Returns service configuration for slave. - * - * @return array - */ - public function getSlaveConfiguration(): array - { - return $this->environment->getRelationship(self::RELATIONSHIP_SLAVE_KEY)[0] ?? []; - } - - /** - * Returns configuration for quote service. - */ - public function getQuoteConfiguration(): array - { - return $this->environment->getRelationship(self::RELATIONSHIP_QUOTE_KEY)[0] ?? []; - } - - /** - * Returns configuration for quote slave service. - * - * @return array - */ - public function getQuoteSlaveConfiguration(): array - { - return $this->environment->getRelationship(self::RELATIONSHIP_QUOTE_SLAVE_KEY)[0] ?? []; + return $this->connectionType->getConfiguration(); } /** - * Returns configuration for sales service. - */ - public function getSalesConfiguration(): array - { - return $this->environment->getRelationship(self::RELATIONSHIP_SALES_KEY)[0] ?? []; - } - - /** - * Returns configuration for slave sales service. - * - * @return array - */ - public function getSalesSlaveConfiguration(): array - { - return $this->environment->getRelationship(self::RELATIONSHIP_SALES_SLAVE_KEY)[0] ?? []; - } - - /** - * Returns version of the service. + * Retrieves MySQL service version whether from relationship configuration + * or using SQL query (for PRO environments) * - * @return string + * {@inheritDoc} */ public function getVersion(): string { if ($this->version === null) { $this->version = '0'; - $databaseConfig = $this->getConfiguration(); + try { + $databaseConfig = $this->getConfiguration(); + + if (isset($databaseConfig['type']) && strpos($databaseConfig['type'], ':') !== false) { + $this->version = explode(':', $databaseConfig['type'])[1]; + } elseif (!empty($databaseConfig['host'])) { + $rawVersion = $this->connection->selectOne('SELECT VERSION() as version'); + preg_match('/^\d+\.\d+/', $rawVersion['version'] ?? '', $matches); - if (isset($databaseConfig['type']) && strpos($databaseConfig['type'], ':') !== false) { - $this->version = explode(':', $databaseConfig['type'])[1]; + $this->version = $matches[0] ?? '0'; + } + } catch (\Exception $e) { + throw new ServiceException($e->getMessage()); } } diff --git a/src/Service/Php.php b/src/Service/Php.php index e841a6c731..c3048efd70 100644 --- a/src/Service/Php.php +++ b/src/Service/Php.php @@ -12,11 +12,6 @@ */ class Php implements ServiceInterface { - /** - * @var string - */ - private $version; - /** * Get PHP configuration. * @@ -34,15 +29,6 @@ public function getConfiguration(): array */ public function getVersion(): string { - if ($this->version === null) { - $this->version = '0'; - - $phpConfigs = $this->getConfiguration(); - if (isset($phpConfigs['version'])) { - $this->version = $phpConfigs['version']; - } - } - - return $this->version; + return $this->getConfiguration()['version']; } } diff --git a/src/Service/RabbitMq.php b/src/Service/RabbitMq.php index a9217321df..a7fad83d3c 100644 --- a/src/Service/RabbitMq.php +++ b/src/Service/RabbitMq.php @@ -8,6 +8,8 @@ namespace Magento\MagentoCloud\Service; use Magento\MagentoCloud\Config\Environment; +use Magento\MagentoCloud\Shell\ShellException; +use Magento\MagentoCloud\Shell\ShellInterface; /** * @@ -26,6 +28,11 @@ class RabbitMq implements ServiceInterface */ private $environment; + /** + * @var ShellInterface + */ + private $shell; + /** * @var string */ @@ -33,17 +40,21 @@ class RabbitMq implements ServiceInterface /** * @param Environment $environment + * @param ShellInterface $shell */ - public function __construct(Environment $environment) - { + public function __construct( + Environment $environment, + ShellInterface $shell + ) { $this->environment = $environment; + $this->shell = $shell; } /** * Finds if configuration exists for one of possible amqp relationship names and return first match, * amqp relationship can have different name on different environment. * - * {@inheritdoc} + * {@inheritDoc} */ public function getConfiguration(): array { @@ -58,7 +69,10 @@ public function getConfiguration(): array } /** - * @inheritdoc + * Retrieve RabbitMQ service version whether from relationship configuration + * or using CLI command (for PRO environments) + * + * {@inheritDoc} */ public function getVersion(): string { @@ -69,6 +83,14 @@ public function getVersion(): string if (isset($config['type']) && strpos($config['type'], ':') !== false) { $this->version = explode(':', $config['type'])[1]; + } elseif (isset($config['host']) && isset($config['port'])) { + try { + $process = $this->shell->execute('dpkg -s rabbitmq-server | grep Version'); + preg_match('/^(?:Version:(?:\s)?)(\d+\.\d+)/', $process->getOutput(), $matches); + $this->version = $matches[1] ?? '0'; + } catch (ShellException $exception) { + throw new ServiceException($exception->getMessage()); + } } } diff --git a/src/Service/Redis.php b/src/Service/Redis.php index b468f3c1d9..7a8d50d885 100644 --- a/src/Service/Redis.php +++ b/src/Service/Redis.php @@ -8,6 +8,8 @@ namespace Magento\MagentoCloud\Service; use Magento\MagentoCloud\Config\Environment; +use Magento\MagentoCloud\Shell\ShellException; +use Magento\MagentoCloud\Shell\ShellInterface; /** * Returns Redis service configurations. @@ -22,6 +24,11 @@ class Redis implements ServiceInterface */ private $environment; + /** + * @var ShellInterface + */ + private $shell; + /** * @var string */ @@ -29,10 +36,14 @@ class Redis implements ServiceInterface /** * @param Environment $environment + * @param ShellInterface $shell */ - public function __construct(Environment $environment) - { + public function __construct( + Environment $environment, + ShellInterface $shell + ) { $this->environment = $environment; + $this->shell = $shell; } /** @@ -54,19 +65,35 @@ public function getSlaveConfiguration(): array } /** - * Returns version of the service. + * Retrieves Redis service version whether from relationship configuration + * or using CLI command (for PRO environments) * - * @return string + * {@inheritDoc} */ public function getVersion(): string { if ($this->version === null) { $this->version = '0'; - $redisConfig = $this->getConfiguration(); + //on integration environments if (isset($redisConfig['type']) && strpos($redisConfig['type'], ':') !== false) { $this->version = explode(':', $redisConfig['type'])[1]; + } elseif (isset($redisConfig['host']) && isset($redisConfig['port'])) { + //on dedicated environments + try { + $process = $this->shell->execute( + sprintf( + 'redis-cli -p %s -h %s info | grep redis_version', + $redisConfig['port'], + $redisConfig['host'] + ) + ); + preg_match('/^(?:redis_version:)(\d+\.\d+)/', $process->getOutput(), $matches); + $this->version = $matches[1] ?? '0'; + } catch (ShellException $exception) { + throw new ServiceException($exception->getMessage()); + } } } diff --git a/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php b/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php index 8ee187bc5c..3d0a2db765 100644 --- a/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php +++ b/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php @@ -17,6 +17,7 @@ use Magento\MagentoCloud\Config\Validator\ResultFactory; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** * @inheritdoc @@ -38,6 +39,11 @@ class ServiceVersionTest extends TestCase */ private $serviceVersionValidatorMock; + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + /** * @var ServiceFactory|MockObject */ @@ -54,15 +60,17 @@ protected function setUp() ]); $this->serviceVersionValidatorMock = $this->createMock(ServiceVersionValidator::class); $this->serviceFactory = $this->createMock(ServiceFactory::class); + $this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class); $this->validator = new ServiceVersion( $this->resultFactoryMock, $this->serviceVersionValidatorMock, - $this->serviceFactory + $this->serviceFactory, + $this->loggerMock ); } - public function testValidate() + public function testValidate(): void { $service1 = $this->createMock(ServiceInterface::class); $service1->expects($this->once()) @@ -79,6 +87,13 @@ public function testValidate() $this->serviceFactory->expects($this->exactly(3)) ->method('create') ->willReturnOnConsecutiveCalls($service1, $service2, $service3); + $this->loggerMock->expects($this->exactly(3)) + ->method('info') + ->withConsecutive( + ['Version of service \'rabbitmq\' is not detected', []], + ['Version of service \'redis\' is 3.2', []], + ['Version of service \'mysql\' is 10.2', []] + ); $this->serviceVersionValidatorMock->expects($this->exactly(2)) ->method('validateService') ->withConsecutive( @@ -92,7 +107,7 @@ public function testValidate() $this->validator->validate(); } - public function testValidateWithErrors() + public function testValidateWithErrors(): void { $errorMessages = ['error message 1', 'error message 2', 'error message 3']; $service1 = $this->createMock(ServiceInterface::class); @@ -125,7 +140,7 @@ public function testValidateWithErrors() $this->validator->validate(); } - public function testValidateWithException() + public function testValidateWithException(): void { $this->serviceFactory->expects($this->any()) ->method('create') diff --git a/src/Test/Unit/DB/Data/ConnectionTypesTest.php b/src/Test/Unit/DB/Data/ConnectionTypesTest.php new file mode 100644 index 0000000000..562f637dd3 --- /dev/null +++ b/src/Test/Unit/DB/Data/ConnectionTypesTest.php @@ -0,0 +1,169 @@ +environmentMock = $this->createMock(Environment::class); + + $this->connectionType = new ConnectionTypes( + $this->environmentMock + ); + } + + public function testGetConfiguration(): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(ConnectionTypes::RELATIONSHIP_KEY) + ->willReturn([ + [ + 'host' => '127.0.0.1', + 'port' => '3306', + ] + ]); + + $this->assertSame( + [ + 'host' => '127.0.0.1', + 'port' => '3306', + ], + $this->connectionType->getConfiguration() + ); + } + + public function testGetSlaveConfiguration(): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(ConnectionTypes::RELATIONSHIP_SLAVE_KEY) + ->willReturn([ + [ + 'host' => '127.0.0.1', + 'port' => '3304', + ] + ]); + + $this->assertSame( + [ + 'host' => '127.0.0.1', + 'port' => '3304', + ], + $this->connectionType->getSlaveConfiguration() + ); + } + + public function testGetQuoteConfiguration(): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(ConnectionTypes::RELATIONSHIP_QUOTE_KEY) + ->willReturn([ + [ + 'host' => '127.0.0.1', + 'port' => '3307', + ] + ]); + + $this->assertSame( + [ + 'host' => '127.0.0.1', + 'port' => '3307', + ], + $this->connectionType->getQuoteConfiguration() + ); + } + + public function testGetQuoteSlaveConfiguration(): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(ConnectionTypes::RELATIONSHIP_QUOTE_SLAVE_KEY) + ->willReturn([ + [ + 'host' => '127.0.0.1', + 'port' => '3308', + ] + ]); + + $this->assertSame( + [ + 'host' => '127.0.0.1', + 'port' => '3308', + ], + $this->connectionType->getQuoteSlaveConfiguration() + ); + } + + public function testGetSalesConfiguration(): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(ConnectionTypes::RELATIONSHIP_SALES_KEY) + ->willReturn([ + [ + 'host' => '127.0.0.1', + 'port' => '3309', + ] + ]); + + $this->assertSame( + [ + 'host' => '127.0.0.1', + 'port' => '3309', + ], + $this->connectionType->getSalesConfiguration() + ); + } + + public function testGetSaleSlaveConfiguration(): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(ConnectionTypes::RELATIONSHIP_SALES_SLAVE_KEY) + ->willReturn([ + [ + 'host' => '127.0.0.1', + 'port' => '3310', + ] + ]); + + $this->assertSame( + [ + 'host' => '127.0.0.1', + 'port' => '3310', + ], + $this->connectionType->getSalesSlaveConfiguration() + ); + } +} diff --git a/src/Test/Unit/DB/Data/RelationshipConnectionFactoryTest.php b/src/Test/Unit/DB/Data/RelationshipConnectionFactoryTest.php index cbf96ac53d..046723ec6b 100644 --- a/src/Test/Unit/DB/Data/RelationshipConnectionFactoryTest.php +++ b/src/Test/Unit/DB/Data/RelationshipConnectionFactoryTest.php @@ -7,9 +7,9 @@ namespace Magento\MagentoCloud\Test\Unit\DB\Data; +use Magento\MagentoCloud\DB\Data\ConnectionTypes; use Magento\MagentoCloud\DB\Data\RelationshipConnection; use Magento\MagentoCloud\DB\Data\RelationshipConnectionFactory; -use Magento\MagentoCloud\Service\Database; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -24,19 +24,19 @@ class RelationshipConnectionFactoryTest extends TestCase private $factory; /** - * @var Database|MockObject + * @var ConnectionTypes|MockObject */ - private $databaseMock; + private $connectionTypeMock; /** * @inheritdoc */ protected function setUp() { - $this->databaseMock = $this->createMock(Database::class); + $this->connectionTypeMock = $this->createMock(ConnectionTypes::class); $this->factory = new RelationshipConnectionFactory( - $this->databaseMock + $this->connectionTypeMock ); } @@ -47,7 +47,7 @@ protected function setUp() */ public function testCreate(string $method, string $connectionType) { - $this->databaseMock->expects($this->once()) + $this->connectionTypeMock->expects($this->once()) ->method($method) ->willReturn([]); diff --git a/src/Test/Unit/Service/DatabaseTest.php b/src/Test/Unit/Service/DatabaseTest.php index 1c44a13b2a..8d5b4a9116 100644 --- a/src/Test/Unit/Service/DatabaseTest.php +++ b/src/Test/Unit/Service/DatabaseTest.php @@ -7,7 +7,8 @@ namespace Magento\MagentoCloud\Test\Unit\Service; -use Magento\MagentoCloud\Config\Environment; +use Magento\MagentoCloud\DB\ConnectionInterface; +use Magento\MagentoCloud\DB\Data\ConnectionTypes; use Magento\MagentoCloud\Service\Database; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -19,176 +20,129 @@ class DatabaseTest extends TestCase { /** - * @var Database|MockObject + * @var Database */ private $database; /** - * @var Environment|MockObject + * @var ConnectionTypes|MockObject */ - private $environmentMock; + private $connectionTypeMock; + + /** + * @var ConnectionInterface|MockObject + */ + private $connectionMock; /** * @inheritdoc */ public function setUp() { - $this->environmentMock = $this->createMock(Environment::class); + $this->connectionTypeMock = $this->createMock(ConnectionTypes::class); + $this->connectionMock = $this->getMockForAbstractClass(ConnectionInterface::class); $this->database = new Database( - $this->environmentMock + $this->connectionTypeMock, + $this->connectionMock ); } - public function testGetConfiguration() + public function testGetConfiguration(): void { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_KEY) - ->willReturn([ - [ - 'host' => '127.0.0.1', - 'port' => '3306', - ] - ]); + $connection = [ + 'host' => '127.0.0.1', + 'port' => '3306', + ]; - $this->assertSame( - [ - 'host' => '127.0.0.1', - 'port' => '3306', - ], - $this->database->getConfiguration() - ); - } - - public function testGetSlaveConfiguration() - { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_SLAVE_KEY) - ->willReturn([ - [ - 'host' => '127.0.0.1', - 'port' => '3307', - ] - ]); + $this->connectionTypeMock->expects($this->once()) + ->method('getConfiguration') + ->willReturn($connection); $this->assertSame( - [ - 'host' => '127.0.0.1', - 'port' => '3307', - ], - $this->database->getSlaveConfiguration() + $connection, + $this->database->getConfiguration() ); } - public function testGetQuoteConfiguration() + /** + * @param array $config + * @param string $expectedVersion + * + * @dataProvider getVersionFromConfigDataProvider + */ + public function testGetVersionFromConfig(array $config, string $expectedVersion): void { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_QUOTE_KEY) - ->willReturn([ - [ - 'host' => '127.0.0.1', - 'port' => '3308', - ] - ]); + $this->connectionTypeMock->expects($this->once()) + ->method('getConfiguration') + ->willReturn($config); + $this->connectionMock->expects($this->never()) + ->method('selectOne'); - $this->assertSame( - [ - 'host' => '127.0.0.1', - 'port' => '3308', - ], - $this->database->getQuoteConfiguration() - ); + $this->assertEquals($expectedVersion, $this->database->getVersion()); } - public function testGetQuoteSlaveConfiguration() + /** + * Data provider for testGetVersionFromConfig + * @return array + */ + public function getVersionFromConfigDataProvider(): array { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_QUOTE_SLAVE_KEY) - ->willReturn([ - [ - 'host' => '127.0.0.1', - 'port' => '3309', - ] - ]); - - $this->assertSame( + return [ [ - 'host' => '127.0.0.1', - 'port' => '3309', - ], - $this->database->getQuoteSlaveConfiguration() - ); - } - - public function testGetSalesConfiguration() - { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_SALES_KEY) - ->willReturn([ [ 'host' => '127.0.0.1', - 'port' => '3308', - ] - ]); - - $this->assertSame( - [ - 'host' => '127.0.0.1', - 'port' => '3308', + 'port' => '3306', + 'type' => 'mysql:10.2', + ], + '10.2' ], - $this->database->getSalesConfiguration() - ); - } - - public function testGetSaleSlaveConfiguration() - { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_SALES_SLAVE_KEY) - ->willReturn([ - [ - 'host' => '127.0.0.1', - 'port' => '3309', - ] - ]); - - $this->assertSame( [ - 'host' => '127.0.0.1', - 'port' => '3309', - ], - $this->database->getSalesSlaveConfiguration() - ); + ['host' => ''], + '0' + ] + ]; } - public function testGetVersion() + /** + * @param array $version + * @param string $expectedResult + * @throws \Magento\MagentoCloud\Service\ServiceException + * + * @dataProvider getVersionDataProvider + */ + public function testGetVersion(array $version, string $expectedResult): void { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_KEY) - ->willReturn([ + $this->connectionTypeMock->expects($this->once()) + ->method('getConfiguration') + ->willReturn( [ 'host' => '127.0.0.1', 'port' => '3306', - 'type' => 'mysql:10.2', ] - ]); + ); - $this->assertEquals('10.2', $this->database->getVersion()); + $this->connectionMock->expects($this->once()) + ->method('selectOne') + ->with('SELECT VERSION() as version') + ->willReturn($version); + + $this->assertEquals($expectedResult, $this->database->getVersion()); } - public function testGetVersionNotConfigured() + /** + * Data provider for testGetVersion + * @return array + */ + public function getVersionDataProvider(): array { - $this->environmentMock->expects($this->once()) - ->method('getRelationship') - ->with(Database::RELATIONSHIP_KEY) - ->willReturn([]); - - $this->assertEquals('0', $this->database->getVersion()); + return [ + [['version' => '10.2.33-MariaDB-10.2.33+maria~stretch-lo'], '10.2'], + [['version' => '10.3.20-MariaDB-1:10.3.20+maria~jessie'], '10.3'], + [['version' => 's10.3.20-MariaDB-1:10.3.20+maria~jessie'], '0'], + [['version' => ''], '0'], + [['version' => '10.version'], '0'], + [[], '0'], + ]; } } diff --git a/src/Test/Unit/Service/RabbitMqTest.php b/src/Test/Unit/Service/RabbitMqTest.php index a2eeb32e34..1cf9cfe097 100644 --- a/src/Test/Unit/Service/RabbitMqTest.php +++ b/src/Test/Unit/Service/RabbitMqTest.php @@ -9,6 +9,10 @@ use Magento\MagentoCloud\Config\Environment; use Magento\MagentoCloud\Service\RabbitMq; +use Magento\MagentoCloud\Service\ServiceException; +use Magento\MagentoCloud\Shell\ProcessInterface; +use Magento\MagentoCloud\Shell\ShellException; +use Magento\MagentoCloud\Shell\ShellInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -28,19 +32,26 @@ class RabbitMqTest extends TestCase */ private $environmentMock; + /** + * @var ShellInterface|MockObject + */ + private $shellMock; + /** * @inheritdoc */ public function setUp() { $this->environmentMock = $this->createMock(Environment::class); + $this->shellMock = $this->getMockForAbstractClass(ShellInterface::class); $this->rabbitMq = new RabbitMq( - $this->environmentMock + $this->environmentMock, + $this->shellMock ); } - public function testGetConfiguration() + public function testGetConfiguration(): void { $this->environmentMock->expects($this->exactly(3)) ->method('getRelationship') @@ -65,7 +76,7 @@ public function testGetConfiguration() ); } - public function testGetVersion() + public function testGetVersion(): void { $this->environmentMock->expects($this->exactly(3)) ->method('getRelationship') @@ -82,15 +93,94 @@ public function testGetVersion() ] ); + $this->shellMock->expects($this->never()) + ->method('execute'); $this->assertEquals('3.7', $this->rabbitMq->getVersion()); } - public function testGetVersionNotConfigured() + /** + * @throws ServiceException + */ + public function testGetVersionNotInstalled(): void { $this->environmentMock->expects($this->exactly(3)) ->method('getRelationship') - ->willReturn([]); + ->withConsecutive(['rabbitmq'], ['mq'], ['amqp']) + ->willReturnOnConsecutiveCalls( + [], + [], + [] + ); + $this->shellMock->expects($this->never()) + ->method('execute'); $this->assertEquals('0', $this->rabbitMq->getVersion()); } + + /** + * @param string $version + * @param string $expectedResult + * @throws ServiceException + * + * @dataProvider getVersionFromCliDataProvider + */ + public function testGetVersionFromCli(string $version, string $expectedResult): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with('rabbitmq') + ->willReturn([[ + 'host' => '127.0.0.1', + 'port' => '5672', + ]]); + + $processMock = $this->getMockForAbstractClass(ProcessInterface::class); + $processMock->expects($this->once()) + ->method('getOutput') + ->willReturn($version); + $this->shellMock->expects($this->once()) + ->method('execute') + ->with('dpkg -s rabbitmq-server | grep Version') + ->willReturn($processMock); + + $this->assertEquals($expectedResult, $this->rabbitMq->getVersion()); + } + + /** + * Data provider for testGetVersionFromCli + * @return array + */ + public function getVersionFromCliDataProvider(): array + { + return [ + ['Version: 3.8.5', '3.8'], + ['Version:3.8.5', '3.8'], + ['Version: 111.222.333', '111.222'], + ['Version: some version', '0'], + ['redis_version:abc', '0'], + ['redis:5.3.6', '0'], + ['', '0'], + ['error', '0'], + ]; + } + + public function testGetVersionWithException(): void + { + $exceptionMessage = 'Some shell exception'; + $this->expectException(ServiceException::class); + $this->expectExceptionMessage($exceptionMessage); + + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with('rabbitmq') + ->willReturn([[ + 'host' => '127.0.0.1', + 'port' => '5672', + ]]); + + $this->shellMock->expects($this->once()) + ->method('execute') + ->willThrowException(new ShellException($exceptionMessage)); + $this->rabbitMq->getVersion(); + } } diff --git a/src/Test/Unit/Service/RedisTest.php b/src/Test/Unit/Service/RedisTest.php index ba93f0daf0..7d27c4f2ec 100644 --- a/src/Test/Unit/Service/RedisTest.php +++ b/src/Test/Unit/Service/RedisTest.php @@ -9,6 +9,10 @@ use Magento\MagentoCloud\Config\Environment; use Magento\MagentoCloud\Service\Redis; +use Magento\MagentoCloud\Service\ServiceException; +use Magento\MagentoCloud\Shell\ProcessInterface; +use Magento\MagentoCloud\Shell\ShellException; +use Magento\MagentoCloud\Shell\ShellInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -19,7 +23,7 @@ class RedisTest extends TestCase { /** - * @var Redis|MockObject + * @var Redis */ private $redis; @@ -28,19 +32,26 @@ class RedisTest extends TestCase */ private $environmentMock; + /** + * @var ShellInterface|MockObject + */ + private $shellMock; + /** * @inheritdoc */ public function setUp() { $this->environmentMock = $this->createMock(Environment::class); + $this->shellMock = $this->getMockForAbstractClass(ShellInterface::class); $this->redis = new Redis( - $this->environmentMock + $this->environmentMock, + $this->shellMock ); } - public function testGetConfiguration() + public function testGetConfiguration(): void { $this->environmentMock->expects($this->once()) ->method('getRelationship') @@ -61,7 +72,7 @@ public function testGetConfiguration() ); } - public function testGetSlaveConfiguration() + public function testGetSlaveConfiguration(): void { $this->environmentMock->expects($this->once()) ->method('getRelationship') @@ -82,29 +93,118 @@ public function testGetSlaveConfiguration() ); } - public function testGetVersion() + /** + * @param array $config + * @param string $expectedResult + * @throws \Magento\MagentoCloud\Service\ServiceException + * @throws \ReflectionException + * + * @dataProvider getVersionFromConfigDataProvider + */ + public function testGetVersionFromConfig(array $config, string $expectedResult): void { $this->environmentMock->expects($this->once()) ->method('getRelationship') ->with(Redis::RELATIONSHIP_KEY) - ->willReturn([ - [ + ->willReturn($config); + + $this->shellMock->expects($this->never()) + ->method('execute'); + + $this->assertEquals($expectedResult, $this->redis->getVersion()); + } + + /** + * Data provider for testGetVersionFromConfig + * @return array + */ + public function getVersionFromConfigDataProvider(): array + { + return [ + [ + [[ 'host' => '127.0.0.1', 'port' => '3306', - 'type' => 'mysql:10.2', - ] - ]); + 'type' => 'redis:10.2' + ]], + '10.2' + ], + [ + [[ + 'type' => 'redis:10.2.5' + ]], + '10.2.5' + ], + [ + [], + '0' + ], + ]; + } + + /** + * @param string $version + * @param string $expectedResult + * @throws \Magento\MagentoCloud\Service\ServiceException + * @throws \ReflectionException + * + * @dataProvider getVersionFromCliDataProvider + */ + public function testGetVersionFromCli(string $version, string $expectedResult): void + { + $this->environmentMock->expects($this->once()) + ->method('getRelationship') + ->with(Redis::RELATIONSHIP_KEY) + ->willReturn([[ + 'host' => '127.0.0.1', + 'port' => '3306', + ]]); + + $processMock = $this->getMockForAbstractClass(ProcessInterface::class); + $processMock->expects($this->once()) + ->method('getOutput') + ->willReturn($version); + $this->shellMock->expects($this->once()) + ->method('execute') + ->with('redis-cli -p 3306 -h 127.0.0.1 info | grep redis_version') + ->willReturn($processMock); - $this->assertEquals('10.2', $this->redis->getVersion()); + $this->assertEquals($expectedResult, $this->redis->getVersion()); + } + + /** + * Data provider for testGetVersionFromCli + * @return array + */ + public function getVersionFromCliDataProvider(): array + { + return [ + ['redis_version:5.3.6', '5.3'], + ['redis_version:1.2.3.4.5', '1.2'], + ['redis_version:abc', '0'], + ['redis:5.3.6', '0'], + ['', '0'], + ['error', '0'], + ]; } - public function testGetVersionNotConfigured() + public function testGetVersionWithException() { + $exceptionMessage = 'Some shell exception'; + $this->expectException(ServiceException::class); + $this->expectExceptionMessage($exceptionMessage); + $this->environmentMock->expects($this->once()) ->method('getRelationship') ->with(Redis::RELATIONSHIP_KEY) - ->willReturn([]); + ->willReturn([[ + 'host' => '127.0.0.1', + 'port' => '3306', + ]]); - $this->assertEquals('0', $this->redis->getVersion()); + $this->shellMock->expects($this->once()) + ->method('execute') + ->willThrowException(new ShellException($exceptionMessage)); + $this->redis->getVersion(); } } From 770eca7b4315489a86775b795f5dc57b90d5a4cf Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Thu, 29 Oct 2020 15:31:41 -0500 Subject: [PATCH 14/17] MCLOUD-7197: Error code message updates --- config/schema.error.yaml | 4 ++-- dist/error-codes.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/schema.error.yaml b/config/schema.error.yaml index 5ae9639af5..40cabaa4ba 100644 --- a/config/schema.error.yaml +++ b/config/schema.error.yaml @@ -322,7 +322,7 @@ type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_ES_CANNOT_CONNECT: title: 'Can not connect to the Elasticsearch service' - suggestion: 'Check for valid elasticsearch credentials and verify that the service is running' + suggestion: 'Check for valid Elasticsearch credentials and verify that the service is running' stage: deploy type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_WRONG_BRAINTREE_VARIABLE: @@ -586,7 +586,7 @@ !php/const Magento\MagentoCloud\App\Error::WARN_NOT_SUPPORTED_MAGE_MODE: title: 'Mode value for MAGE_MODE environment variable not supported' stage: deploy - suggestion: 'Remove MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports only "production" mode.' + suggestion: 'Remove the MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports "production" mode only.' step: 'validate-config:mage-mode-variable' type: warning !php/const Magento\MagentoCloud\App\Error::WARN_DEBUG_LOG_ENABLED: diff --git a/dist/error-codes.md b/dist/error-codes.md index 1d879874b3..cd9ca2f6fe 100644 --- a/dist/error-codes.md +++ b/dist/error-codes.md @@ -71,7 +71,7 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 129 | install-update: reset-password | Unable to read reset password template | | | 130 | install-update: cache_type | Command failed: `php ./bin/magento cache:enable` | Command `php ./bin/magento cache:enable` runs only when Magento was installed but `./app/etc/env.php` file was absent or empty at the beginning of the deployment. Check the `cloud.log` for more information. Add `VERBOSE_COMMANDS: '-vvv'` into `.magento.env.yaml` for more detailed command output. | | 131 | install-update | The `crypt/key` key value does not exist in the `./app/etc/env.php` file or the `CRYPT_KEY` cloud environment variable | This error occurs if the `./app/etc/env.php` file is not present when Magento deployment begins, or if the `crypt/key` value is undefined. If you migrated the database from another environment, retrieve the crypt key value from that environment. Then, add the value to the [CRYPT_KEY](https://devdocs.magento.com/cloud/env/variables-deploy.html#crypt_key) cloud environment variable in your current environment. See [Add the Magento encryption key](https://devdocs.magento.com/cloud/setup/first-time-setup-import-import.html#encryption-key). If you accidentally removed the `./app/etc/env.php` file, use the following command to restore it from the backup files created from a previous deployment: `./vendor/bin/ece-tools backup:restore` CLI command ." | -| 132 | | Can not connect to the Elasticsearch service | Check for valid elasticsearch credentials and verify that the service is running | +| 132 | | Can not connect to the Elasticsearch service | Check for valid Elasticsearch credentials and verify that the service is running | | 133 | validate-config | Remove Magento Braintree module configuration which is no longer supported in Magento 2.4 and later versions. | Support for the Braintree module is no longer included with Magento 2.4.0 and later. Remove the CONFIG__STORES__DEFAULT__PAYMENT__BRAINTREE__CHANNEL variable from the variables section of the .magento.app.yaml file. For Braintree support, use an official Braintree Payments extension from the Magento Marketplace instead. | | 134 | validate-config | Magento 2.4.0 requires Elasticsearch service to be installed | Install Elasticsearch service | | 135 | validate-config | The search engine must be set to Elasticsearch for Magento >= 2.4.0 | Check the SEARCH_CONFIGURATION variable for the `engine` option. If it is configured, remove the option, or set the value to "elasticsearch". | @@ -149,7 +149,7 @@ Warning errors indicate a problem with the Magento Commerce Cloud project config | 2024 | install-update:split-db | The SPLIT_DB variable is missing the configuration for split connection types. | | | 2025 | install-update:split-db | Slave connection not set. | | | 2026 | pre-deploy:restore-writable-dirs | Failed to restore some data generated during the build phase to the mounted directories | Check the `cloud.log` for more information. | -| 2027 | validate-config:mage-mode-variable | Mode value for MAGE_MODE environment variable not supported | Remove MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports only "production" mode. | +| 2027 | validate-config:mage-mode-variable | Mode value for MAGE_MODE environment variable not supported | Remove the MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports "production" mode only. | ### Post-deploy stage From 5b1bf188cdb944f391e32374e675a6943cfdbcd9 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Thu, 29 Oct 2020 16:14:12 -0500 Subject: [PATCH 15/17] MCLOUD-7072: Add CLI command for generating .magento.env.yaml --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 06707c1581..aedd0cde83 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magento/ece-tools", "description": "Provides tools to build and deploy Magento 2 Enterprise Edition", "type": "magento2-component", - "version": "2002.1.2", + "version": "2002.1.3", "license": "OSL-3.0", "repositories": { "repo.magento.com": { From bbedd528cdeff89ff75bb63df62ab8d1337a35e9 Mon Sep 17 00:00:00 2001 From: Oleksandr Shmyheliuk Date: Thu, 29 Oct 2020 17:52:29 -0500 Subject: [PATCH 16/17] MCLOUD-7072: Add CLI command for generating .magento.env.yaml --- config/schema.error.yaml | 2 +- dist/error-codes.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/schema.error.yaml b/config/schema.error.yaml index 40cabaa4ba..af34cc9651 100644 --- a/config/schema.error.yaml +++ b/config/schema.error.yaml @@ -174,7 +174,7 @@ type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_REDIS_CACHE_CLEAN_FAILED: title: 'Failed to clean the Redis cache' - suggestion: 'Failed to clean the Redis cache. Check that the Redis cache configuration is correct and that the Redis service is available. See [Setup Redis service](https://devdocs.magento.com/cloud/project/project-conf-files_services-redis.html).' + suggestion: 'Failed to clean the Redis cache. Check that the Redis cache configuration is correct and that the Redis service is available. See [Setup Redis service](https://devdocs.magento.com/cloud/project/services-redis.html).' step: 'pre-deploy: clean-redis-cache' stage: deploy type: critical diff --git a/dist/error-codes.md b/dist/error-codes.md index cd9ca2f6fe..8477a4aa4e 100644 --- a/dist/error-codes.md +++ b/dist/error-codes.md @@ -46,7 +46,7 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 104 | | Failed to parse the `.magento.env.yaml` file | Configuration is not defined in the `./vendor/magento/ece-tools/config/schema.yaml` file. Check that the config variable name is correct, and that it is defined. | | 105 | | Unable to read the `.magento.env.yaml` file | Unable to read the `./.magento.env.yaml` file. Check file permissions. | | 106 | | Unable to read the `.schema.yaml` file | | -| 107 | pre-deploy: clean-redis-cache | Failed to clean the Redis cache | Failed to clean the Redis cache. Check that the Redis cache configuration is correct and that the Redis service is available. See [Setup Redis service](https://devdocs.magento.com/cloud/project/project-conf-files_services-redis.html). | +| 107 | pre-deploy: clean-redis-cache | Failed to clean the Redis cache | Failed to clean the Redis cache. Check that the Redis cache configuration is correct and that the Redis service is available. See [Setup Redis service](https://devdocs.magento.com/cloud/project/services-redis.html). | | 108 | pre-deploy: set-production-mode | Command `/bin/magento maintenance:enable` failed | Check the `cloud.log` for more information. For more detailed command output, add the `VERBOSE_COMMANDS: '-vvv'` option to the `.magento.env.yaml` file. | | 109 | validate-config | Incorrect database configuration | Check that the the `DATABASE_CONFIGURATION` environment variable is configured correctly. | | 110 | validate-config | Incorrect session configuration | Check that the `SESSION_CONFIGURATION` environment variable is configured correctly. The configuration must contain at least the `save` parameter. | From 35540f53d702cf26a98cecce7ff14a040a0ea26a Mon Sep 17 00:00:00 2001 From: Nadiya Syvokonenko Date: Mon, 2 Nov 2020 10:28:10 -0600 Subject: [PATCH 17/17] MCLOUD-7193: Add ES to compatibility service version validator --- .../Validator/Deploy/ServiceVersion.php | 3 +- .../Validator/Deploy/ServiceVersionTest.php | 45 ++++++++++++------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/Config/Validator/Deploy/ServiceVersion.php b/src/Config/Validator/Deploy/ServiceVersion.php index 43ab96cb21..2aca3e2e83 100644 --- a/src/Config/Validator/Deploy/ServiceVersion.php +++ b/src/Config/Validator/Deploy/ServiceVersion.php @@ -71,7 +71,8 @@ public function validate(): Validator\ResultInterface $services = [ ServiceInterface::NAME_RABBITMQ, ServiceInterface::NAME_REDIS, - ServiceInterface::NAME_DB + ServiceInterface::NAME_DB, + ServiceInterface::NAME_ELASTICSEARCH ]; $errors = []; diff --git a/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php b/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php index 3d0a2db765..08b23ce581 100644 --- a/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php +++ b/src/Test/Unit/Config/Validator/Deploy/ServiceVersionTest.php @@ -72,33 +72,39 @@ protected function setUp() public function testValidate(): void { - $service1 = $this->createMock(ServiceInterface::class); - $service1->expects($this->once()) + $serviceRmq = $this->createMock(ServiceInterface::class); + $serviceRmq->expects($this->once()) ->method('getVersion') ->willReturn('0'); - $service2 = $this->createMock(ServiceInterface::class); - $service2->expects($this->once()) + $serviceRedis = $this->createMock(ServiceInterface::class); + $serviceRedis->expects($this->once()) ->method('getVersion') ->willReturn('3.2'); - $service3 = $this->createMock(ServiceInterface::class); - $service3->expects($this->once()) + $serviceDB = $this->createMock(ServiceInterface::class); + $serviceDB->expects($this->once()) ->method('getVersion') ->willReturn('10.2'); - $this->serviceFactory->expects($this->exactly(3)) + $serviceES = $this->createMock(ServiceInterface::class); + $serviceES->expects($this->once()) + ->method('getVersion') + ->willReturn('7.7'); + $this->serviceFactory->expects($this->exactly(4)) ->method('create') - ->willReturnOnConsecutiveCalls($service1, $service2, $service3); - $this->loggerMock->expects($this->exactly(3)) + ->willReturnOnConsecutiveCalls($serviceRmq, $serviceRedis, $serviceDB, $serviceES); + $this->loggerMock->expects($this->exactly(4)) ->method('info') ->withConsecutive( ['Version of service \'rabbitmq\' is not detected', []], ['Version of service \'redis\' is 3.2', []], - ['Version of service \'mysql\' is 10.2', []] + ['Version of service \'mysql\' is 10.2', []], + ['Version of service \'elasticsearch\' is 7.7', []] ); - $this->serviceVersionValidatorMock->expects($this->exactly(2)) + $this->serviceVersionValidatorMock->expects($this->exactly(3)) ->method('validateService') ->withConsecutive( [ServiceInterface::NAME_REDIS, '3.2'], - [ServiceInterface::NAME_DB, '10.2'] + [ServiceInterface::NAME_DB, '10.2'], + [ServiceInterface::NAME_ELASTICSEARCH, '7.7'] ) ->willReturn(''); $this->resultFactoryMock->expects($this->once()) @@ -109,7 +115,7 @@ public function testValidate(): void public function testValidateWithErrors(): void { - $errorMessages = ['error message 1', 'error message 2', 'error message 3']; + $errorMessages = ['error message 1', 'error message 2', 'error message 3', 'error message 4']; $service1 = $this->createMock(ServiceInterface::class); $service1->expects($this->once()) ->method('getVersion') @@ -122,15 +128,20 @@ public function testValidateWithErrors(): void $service3->expects($this->once()) ->method('getVersion') ->willReturn('5.7'); - $this->serviceFactory->expects($this->exactly(3)) + $service4 = $this->createMock(ServiceInterface::class); + $service4->expects($this->once()) + ->method('getVersion') + ->willReturn('7.7'); + $this->serviceFactory->expects($this->exactly(4)) ->method('create') - ->willReturnOnConsecutiveCalls($service1, $service2, $service3); - $this->serviceVersionValidatorMock->expects($this->exactly(3)) + ->willReturnOnConsecutiveCalls($service1, $service2, $service3, $service4); + $this->serviceVersionValidatorMock->expects($this->exactly(4)) ->method('validateService') ->withConsecutive( [ServiceInterface::NAME_RABBITMQ, '1.5'], [ServiceInterface::NAME_REDIS, '2.2'], - [ServiceInterface::NAME_DB, '5.7'] + [ServiceInterface::NAME_DB, '5.7'], + [ServiceInterface::NAME_ELASTICSEARCH, '7.7'] ) ->willReturnOnConsecutiveCalls(...$errorMessages); $this->resultFactoryMock->expects($this->once())