diff --git a/composer.json b/composer.json index 7de06cbd9f..084403ef6b 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.12", + "version": "2002.1.13", "license": "OSL-3.0", "repositories": { "repo.magento.com": { @@ -23,7 +23,7 @@ "illuminate/config": "^5.5||^8.77", "magento/magento-cloud-components": "^1.0.8", "magento/magento-cloud-docker": "^1.0.0", - "magento/magento-cloud-patches": "^1.0.11", + "magento/magento-cloud-patches": "^1.0.20", "magento/quality-patches": "^1.1.0", "monolog/monolog": "^1.25 || ^2.3", "nesbot/carbon": "^1.0 || ^2.0", diff --git a/config/schema.error.yaml b/config/schema.error.yaml index ca0fbe1a9a..6c0cd68bc0 100644 --- a/config/schema.error.yaml +++ b/config/schema.error.yaml @@ -139,7 +139,7 @@ !php/const Magento\MagentoCloud\App\Error::BUILD_WRONG_BRAINTREE_VARIABLE: step: validate-config title: 'Remove Magento Braintree module configuration which is no longer supported in Adobe Commerce and Magento Open Source 2.4 and later versions.' - 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 payment support, use an official extension from the Commerce Marketplace instead.' + 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 payment support, use an official extension from the Commerce Marketplace instead.' stage: build type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_WRONG_CACHE_CONFIGURATION: @@ -334,7 +334,7 @@ !php/const Magento\MagentoCloud\App\Error::DEPLOY_WRONG_BRAINTREE_VARIABLE: step: validate-config title: 'Remove Magento Braintree module configuration which is no longer supported in Adobe Commerce or Magento Open Source 2.4 and later versions.' - suggestion: 'Support for the Braintree module is no longer included with Adobe Commerce or Magento Open Source 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 Commerce Marketplace instead.' + suggestion: 'Support for the Braintree module is no longer included with Adobe Commerce or Magento Open Source 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 Commerce Marketplace instead.' stage: deploy type: critical !php/const Magento\MagentoCloud\App\Error::DEPLOY_ES_SERVICE_NOT_INSTALLED: @@ -430,6 +430,16 @@ suggestion: '' stage: general type: critical +!php/const Magento\MagentoCloud\App\Error::GLOBAL_EVENTING_MODULE_GENERATE_FAILED: + title: 'Unable to generate a module for eventing' + suggestion: 'Check the `cloud.log` for more information.' + stage: general + type: critical +!php/const Magento\MagentoCloud\App\Error::GLOBAL_EVENTING_MODULE_ENABLEMENT_FAILED: + title: 'Unable to enable a module for eventing' + suggestion: 'Check the `cloud.log` for more information.' + stage: general + type: critical # Warning errors !php/const Magento\MagentoCloud\App\Error::WARN_CONFIG_PHP_NOT_EXISTS: title: 'File app/etc/config.php does not exist' diff --git a/config/schema.yaml b/config/schema.yaml index baee9d1cd8..1d8e3fb820 100644 --- a/config/schema.yaml +++ b/config/schema.yaml @@ -706,6 +706,17 @@ variables: config: region: us-east-1 bucket: test-bucket + ENABLE_EVENTING: + description: Enables commerce eventing. + type: boolean + stages: + - global + default: + global: false + examples: + - stage: + global: + ENABLE_EVENTING: true # Environment variables ENV_RELATIONSHIPS: diff --git a/dist/error-codes.md b/dist/error-codes.md index dd5cf02e8a..b36a6d36b4 100644 --- a/dist/error-codes.md +++ b/dist/error-codes.md @@ -2,7 +2,7 @@ ## Critical Errors -Critical errors indicate a problem with the Magento Commerce Cloud project configuration that causes deployment failure, for example incorrect, unsupported, or missing configuration for required settings. Before you can deploy, you must update the configuration to resolve these errors. +Critical errors indicate a problem with the Commerce on cloud infrastructure project configuration that causes deployment failure, for example incorrect, unsupported, or missing configuration for required settings. Before you can deploy, you must update the configuration to resolve these errors. ### Build stage @@ -16,7 +16,7 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 6 | | Unable to read the `.schema.yaml` file | Unable to read the `./vendor/magento/ece-tools/config/magento.env.yaml` file. Check file permissions and redeploy (`magento-cloud environment:redeploy`). | | 7 | refresh-modules | Cannot write to the `./app/etc/config.php` file | The deployment script cannot make required changes to the `/app/etc/config.php` file. Check your filesystem permissions. | | 8 | validate-config | Cannot read the `composer.json` file | Unable to read the `./composer.json` file. Check file permissions. | -| 9 | validate-config | Composer.json is missing required autoload section | Required `autoload` section is missing from the `composer.json` file. Compare the autoload section to the `composer.json` file in the Magento Cloud template, and add the missing configuration. | +| 9 | validate-config | Composer.json is missing required autoload section | Required `autoload` section is missing from the `composer.json` file. Compare the autoload section to the `composer.json` file in the Cloud template, and add the missing configuration. | | 10 | validate-config | The file `.magento.env.yaml` contains an option that is not declared in the schema, or an option configured with an invalid value or stage | The `./.magento.env.yaml` file contains invalid configuration. Check the error log for detailed info. | | 11 | refresh-modules | Command failed: `/bin/magento module:enable --all` | Try to run `composer update` locally. Then, commit and push the updated `composer.lock` file. Also check the `cloud.log` for more information. For more detailed command output, add the `VERBOSE_COMMANDS: '-vvv'` option to the `.magento.env.yaml` file. | | 12 | apply-patches | Failed to apply patch | | @@ -32,8 +32,8 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 22 | backup-data: writable-dirs | Failed to copy some writable directories into the `init` directory | Failed to copy writable directories into the `./init` folder. Check your filesystem permissions. | | 23 | | Unable to create a logger object | | | 24 | backup-data: static-content | Failed to clean the `./init/pub/static/` directory | Failed to clean `./init/pub/static` folder. Check your filesystem permissions. | -| 25 | | Cannot find the Composer package | If you installed the Magento application version directly from the Magento git repository, verify that the `DEPLOYED_MAGENTO_VERSION_FROM_GIT` environment variable is configured. | -| 26 | 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 payment support, use an official extension from the Magento Marketplace instead. | +| 25 | | Cannot find the Composer package | If you installed the Adobe Commerce application version directly from the GitHub repository, verify that the `DEPLOYED_MAGENTO_VERSION_FROM_GIT` environment variable is configured. | +| 26 | validate-config | Remove Magento Braintree module configuration which is no longer supported in Adobe Commerce and Magento Open Source 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 payment support, use an official extension from the Commerce Marketplace instead. | ### Deploy stage @@ -48,7 +48,7 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 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/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. | +| 109 | validate-config | Incorrect database configuration | Check that 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. | | 111 | validate-config | Incorrect search configuration | Check that the `SEARCH_CONFIGURATION` environment variable is configured correctly. The configuration must contain at least the `engine` parameter. | | 112 | validate-config | Incorrect resource configuration | Check that the `RESOURCE_CONFIGURATION` environment variable is configured correctly. The configuration must contain at least `connection` parameter. | @@ -69,16 +69,16 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 127 | clean-cache | Command `/bin/magento cache:flush` 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. | | 128 | disable-maintenance-mode | Command `/bin/magento maintenance:disable` failed | Check the `cloud.log` for more information. Add `VERBOSE_COMMANDS: '-vvv'` into `.magento.env.yaml` for more detailed command output. | | 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 ." | +| 130 | install-update: cache_type | Command failed: `php ./bin/magento cache:enable` | Command `php ./bin/magento cache:enable` runs only when Adobe Commerce 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 Adobe Commerce 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 | | 137 | | Can not connect to the Opensearch service | Check for valid Opensearch 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 | -| 138 | validate-config | Magento 2.4.4 requires Opensearch or Elasticsearch service to be installed | Install Opensearch 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". | -| 136 | validate-config | Split Database was removed starting from Magento 2.5.0. | If you use split database you have to revert to or migrate to a single database or use an alternative approach. | -| 139 | validate-config | Incorrect search engine | This Magento version does not support Opensearch. You must use versions 2.3.7-p3, 2.4.3-p2, or higher | +| 133 | validate-config | Remove Magento Braintree module configuration which is no longer supported in Adobe Commerce or Magento Open Source 2.4 and later versions. | Support for the Braintree module is no longer included with Adobe Commerce or Magento Open Source 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 Commerce Marketplace instead. | +| 134 | validate-config | Adobe Commerce and Magento Open Source 2.4.0 require Elasticsearch service to be installed | Install Elasticsearch service | +| 138 | validate-config | Adobe Commerce and Magento Open Source 2.4.4 requires Opensearch or Elasticsearch service to be installed | Install Opensearch service | +| 135 | validate-config | The search engine must be set to Elasticsearch for Adobe Commerce and Magento Open Source >= 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". | +| 136 | validate-config | Split Database was removed starting from Adobe Commerce and Magento Open Source 2.5.0. | If you use split database you have to revert to or migrate to a single database or use an alternative approach. | +| 139 | validate-config | Incorrect search engine | This Adobe Commerce or Magento Open Source version does not support Opensearch. You must use versions 2.3.7-p3, 2.4.3-p2, or higher | ### Post-deploy stage @@ -104,10 +104,12 @@ Critical errors indicate a problem with the Magento Commerce Cloud project confi | 244 | | Failed to parse the `.magento.env.yaml` file | The `./.magento.env.yaml` file format is invalid. Use a YAML parser to check the syntax and fix any errors. | | 245 | | Unable to read the `.magento.env.yaml` file | Unable to read the `./.magento.env.yaml` file. Check file permissions. | | 246 | | Unable to read the `.schema.yaml` file | | +| 247 | | Unable to generate a module for eventing | Check the `cloud.log` for more information. | +| 248 | | Unable to enable a module for eventing | Check the `cloud.log` for more information. | ## Warning Errors -Warning errors indicate a problem with the Magento Commerce Cloud project configuration such as incorrect, deprecated, unsupported, or missing configuration settings for optional features that can affect site operation. Although a warning does not cause deployment failure, you should review warning messages and update the configuration to resolve them. +Warning errors indicate a problem with the Commerce on cloud infrastructure project configuration such as incorrect, deprecated, unsupported, or missing configuration settings for optional features that can affect site operation. Although a warning does not cause deployment failure, you should review warning messages and update the configuration to resolve them. ### Build stage @@ -134,36 +136,36 @@ Warning errors indicate a problem with the Magento Commerce Cloud project config | 2005 | validate-config | Admin data is used to create an admin user during initial installation only. Any changes to Admin data are ignored during the upgrade process. | After the initial installation, you can remove admin data from the configuration. | | 2006 | validate-config | Admin user was not created as admin email was not set | After installation, you can create an admin user manually: Use ssh to connect to your environment. Then, run the `bin/magento admin:user:create` command. | | 2007 | validate-config | Update php version to recommended version | | -| 2008 | validate-config | Solr support has been deprecated in Magento 2.1. | | -| 2009 | validate-config | Solr is no longer supported by Magento 2.2 or later. | | +| 2008 | validate-config | Solr support has been deprecated in Adobe Commerce and Magento Open Source 2.1. | | +| 2009 | validate-config | Solr is no longer supported by Adobe Commerce and Magento Open Source 2.2 or later. | | | 2010 | validate-config | Elasticsearch service is installed at infrastructure layer, but it is not used as a search engine. | Consider removing the Elasticsearch service from the infrastructure layer to optimize resource usage. | -| 2011 | validate-config | Elasticsearch service version on infrastructure layer is not compatible with current version of the elasticsearch/elasticsearch module, used by your Magento application. | | -| 2012 | validate-config | The current configuration is not compatible with this version of Magento | | +| 2011 | validate-config | Elasticsearch service version on infrastructure layer is not compatible with current version of the elasticsearch/elasticsearch module, used by your Adobe Commerce application. | | +| 2012 | validate-config | The current configuration is not compatible with this version of Adobe Commerce | | | 2013 | validate-config | SCD options ignored because the deploy process did not run on the build phase | | | 2014 | validate-config | The configuration contains deprecated variables or values | | | 2015 | validate-config | Environment configuration is not valid | | | 2016 | validate-config | JSON type configuration can not be decoded | | -| 2017 | validate-config | The current configuration is not compatible with this version of Magento | | +| 2017 | validate-config | The current configuration is not compatible with this version of Adobe Commerce | | | 2018 | validate-config | Some services have passed EOL | | | 2019 | validate-config | The MySQL search configuration option is deprecated | Use Elasticsearch instead. | -| 2029 | validate-config | Split Database was deprecated in the Magento 2.4.2 and will be removed in 2.5. | If you use split database you should start planning to revert to or migrate to a single database or use an alternative approach. | -| 2020 | install-update | Magento installation completed, but the `app/etc/env.php` configuration file was missing or empty. | Required data will be restored from environment configurations and from .magento.env.yaml file. | +| 2029 | validate-config | Split Database was deprecated in the Adobe Commerce and Magento Open Source 2.4.2 and will be removed in 2.5. | If you use split database you should start planning to revert to or migrate to a single database or use an alternative approach. | +| 2020 | install-update | Adobe Commerce installation completed, but the `app/etc/env.php` configuration file was missing or empty. | Required data will be restored from environment configurations and from .magento.env.yaml file. | | 2021 | install-update:db-connection | For split databases used custom connections | | | 2022 | install-update:db-connection | You have changed to a database configuration that is not compatible with the slave connection. | | | 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 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 the MAGE_MODE environment variable, or change its value to "production". Magento Cloud supports "production" mode only. | +| 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". Adobe Commerce on cloud infrastructure supports "production" mode only. | | 2028 | remote-storage | Remote storage could not be enabled. | Verify remote storage credentials. | -| 2030 | validate-config | Elasticsearch and Opensearch services are both installed at infrastructure layer. Magento 2.4.4 and higher uses Opensearch by default | Consider removing the Elasticsearch or Opensearch service from the infrastructure layer to optimize resource usage. | +| 2030 | validate-config | Elasticsearch and Opensearch services are both installed at infrastructure layer. Adobe Commerce and Magento Open Source 2.4.4 and higher use Opensearch by default | Consider removing the Elasticsearch or Opensearch service from the infrastructure layer to optimize resource usage. | ### Post-deploy stage {:.error-table} | Error code | Post-deploy step | Error description (Title) | Suggested action | | - | - | - | - | -| 3001 | validate-config | Debug logging is enabled in Magento | To save disk space, do not enable debug logging for your production environments. | +| 3001 | validate-config | Debug logging is enabled in Adobe Commerce | To save disk space, do not enable debug logging for your production environments. | | 3002 | warm-up | Can not fetch store urls | | | 3003 | warm-up | Can not fetch store url | | | 3004 | backup | Cannot create backup files | | diff --git a/scenario/build/generate.xml b/scenario/build/generate.xml index 29909e169f..008161aa08 100644 --- a/scenario/build/generate.xml +++ b/scenario/build/generate.xml @@ -14,6 +14,7 @@ Magento\MagentoCloud\Config\Validator\Build\ConfigFileExists + Magento\MagentoCloud\Config\Validator\Build\OpcacheExcludePaths Magento\MagentoCloud\Config\Validator\Build\UnsupportedBuildOptionsIni Magento\MagentoCloud\Config\Validator\Build\ModulesExists Magento\MagentoCloud\Config\Validator\Build\AppropriateVersion @@ -28,6 +29,7 @@ + diff --git a/src/App/Error.php b/src/App/Error.php index da14a53dfc..11d6cd8307 100644 --- a/src/App/Error.php +++ b/src/App/Error.php @@ -94,6 +94,8 @@ class Error public const GLOBAL_CONFIG_PARSE_FAILED = 244; public const GLOBAL_CONFIG_UNABLE_TO_READ = 245; public const GLOBAL_CONFIG_UNABLE_TO_READ_SCHEMA_YAML = 246; + public const GLOBAL_EVENTING_MODULE_GENERATE_FAILED = 247; + public const GLOBAL_EVENTING_MODULE_ENABLEMENT_FAILED = 248; /** * Build @@ -105,6 +107,7 @@ class Error public const WARN_SCD_OPTIONS_IGNORANCE = 1005; public const WARN_CONFIGURATION_STATE_NOT_IDEAL = 1006; public const WARN_BALER_CANNOT_BE_USED = 1007; + public const WARN_WRONG_OPCACHE_CONFIG = 1008; /** * Deploy diff --git a/src/Config/StageConfigInterface.php b/src/Config/StageConfigInterface.php index ab4e99dff8..36d1ca4e83 100644 --- a/src/Config/StageConfigInterface.php +++ b/src/Config/StageConfigInterface.php @@ -42,6 +42,7 @@ interface StageConfigInterface public const VAR_SCD_MATRIX = 'SCD_MATRIX'; public const VAR_SCD_NO_PARENT = 'SCD_NO_PARENT'; public const VAR_X_FRAME_CONFIGURATION = 'X_FRAME_CONFIGURATION'; + public const VAR_ENABLE_EVENTING = 'ENABLE_EVENTING'; /** * Settings for deployment from git. diff --git a/src/Config/Validator/Build/OpcacheExcludePaths.php b/src/Config/Validator/Build/OpcacheExcludePaths.php new file mode 100644 index 0000000000..846868904a --- /dev/null +++ b/src/Config/Validator/Build/OpcacheExcludePaths.php @@ -0,0 +1,107 @@ +file = $file; + $this->fileList = $fileList; + $this->resultFactory = $resultFactory; + } + + /** + * Checks if php.ini and op-exclude.txt are present, and they contain needed configuration + * + * {@inheritdoc} + */ + public function validate(): Validator\ResultInterface + { + $phpIni = $this->fileList->getPhpIni(); + $excludeList = $this->fileList->getOpCacheExcludeList(); + + // Checks if files are present + if (!$this->file->isExists($phpIni) || !$this->file->isExists($excludeList)) { + return $this->resultFactory->error( + 'File php.ini or op-exclude.txt does not exist', + 'Check if your cloud template contains latest php.ini and op-exclude.txt files', + Error::WARN_WRONG_OPCACHE_CONFIG + ); + } + + // Checks if the php.ini file contains correct path to the op-exclude.txt file + $parsedPhpIni = (array) $this->file->parseIni($phpIni); + + if (!(array_key_exists('opcache.blacklist_filename', $parsedPhpIni) + && $parsedPhpIni['opcache.blacklist_filename'] == $excludeList)) { + return $this->resultFactory->error( + 'File php.ini does not contain opcache.blacklist_filename configuration', + 'Check if your cloud template contains latest php.ini configuration file' + . ' https://github.com/magento/magento-cloud/blob/master/php.ini', + Error::WARN_WRONG_OPCACHE_CONFIG + ); + } + + // Checks if the op-exclude.txt file contains all needed paths to exclude for OPCache + $diff = array_diff( + [ + '/app/*/app/etc/config.php', + '/app/*/app/etc/env.php', + '/app/app/etc/config.php', + '/app/app/etc/env.php', + '/app/etc/config.php', + '/app/etc/env.php' + ], + explode(PHP_EOL, (string) $this->file->fileGetContents($excludeList)) + ); + + if (!empty($diff)) { + return $this->resultFactory->error( + 'File op-exclude.txt does not contain required paths to exclude for OPCache', + 'Check if your op-exclude.txt contains the next paths:' . PHP_EOL + . implode(PHP_EOL, $diff), + Error::WARN_WRONG_OPCACHE_CONFIG + ); + } + + return $this->resultFactory->create(Validator\ResultInterface::SUCCESS); + } +} diff --git a/src/Filesystem/ConfigFileList.php b/src/Filesystem/ConfigFileList.php index f3eeefa2df..2d6e5beb11 100644 --- a/src/Filesystem/ConfigFileList.php +++ b/src/Filesystem/ConfigFileList.php @@ -73,4 +73,20 @@ public function getErrorReportConfig(): string { return $this->systemList->getMagentoRoot() . '/pub/errors/local.xml'; } + + /** + * @return string + */ + public function getPhpIni(): string + { + return $this->systemList->getMagentoRoot() . '/php.ini'; + } + + /** + * @return string + */ + public function getOpCacheExcludeList(): string + { + return $this->systemList->getMagentoRoot() . '/op-exclude.txt'; + } } diff --git a/src/Step/Build/EnableEventing.php b/src/Step/Build/EnableEventing.php new file mode 100644 index 0000000000..2dcc2f2a44 --- /dev/null +++ b/src/Step/Build/EnableEventing.php @@ -0,0 +1,92 @@ +logger = $logger; + $this->magentoShell = $shellFactory->createMagento(); + $this->globalConfig = $globalConfig; + } + + /** + * Generates and enables a module for eventing if @see StageConfigInterface::VAR_ENABLE_EVENTING set to true + * + * {@inheritDoc} + */ + public function execute() + { + try { + if (!$this->globalConfig->get(StageConfigInterface::VAR_ENABLE_EVENTING)) { + return; + } + } catch (ConfigException $e) { + throw new StepException($e->getMessage(), $e->getCode(), $e); + } + + try { + $this->logger->notice('Generating module for eventing'); + $this->magentoShell->execute('events:generate:module'); + } catch (ShellException $e) { + $this->logger->error( + 'Failed to generate the Magento_AdobeCommerceEvents module. ' . + 'Refer to the eventing documentation to determine if all required modules are have been installed. ' . + 'Error: ' . $e->getMessage() + ); + throw new StepException($e->getMessage(), Error::GLOBAL_EVENTING_MODULE_GENERATE_FAILED, $e); + } + + try { + $this->logger->notice('Enabling module for eventing'); + $this->magentoShell->execute('module:enable Magento_AdobeCommerceEvents'); + } catch (ShellException $e) { + $this->logger->error('Failed to enable module for eventing: ' . $e->getMessage()); + throw new StepException($e->getMessage(), Error::GLOBAL_EVENTING_MODULE_ENABLEMENT_FAILED, $e); + } + } +} diff --git a/src/Step/Deploy/PreDeploy/ConfigUpdate/Cache.php b/src/Step/Deploy/PreDeploy/ConfigUpdate/Cache.php index 410075cb00..009fc4ec90 100644 --- a/src/Step/Deploy/PreDeploy/ConfigUpdate/Cache.php +++ b/src/Step/Deploy/PreDeploy/ConfigUpdate/Cache.php @@ -76,6 +76,7 @@ public function execute() try { $config = $this->configReader->read(); $cacheConfig = $this->cacheConfig->get(); + $graphqlConfig = $config['cache']['graphql'] ?? []; if (isset($cacheConfig['frontend'])) { $cacheConfig['frontend'] = array_filter($cacheConfig['frontend'], function ($cacheFrontend) { @@ -108,6 +109,10 @@ public function execute() $config['cache'] = $cacheConfig; } + if (!empty($graphqlConfig)) { + $config['cache']['graphql'] = $graphqlConfig; + } + $this->configWriter->create($config); } catch (FileSystemException $e) { throw new StepException($e->getMessage(), Error::DEPLOY_ENV_PHP_IS_NOT_WRITABLE); diff --git a/src/Test/Unit/Config/SchemaTest.php b/src/Test/Unit/Config/SchemaTest.php index c1be3f44e7..e8ff400301 100644 --- a/src/Test/Unit/Config/SchemaTest.php +++ b/src/Test/Unit/Config/SchemaTest.php @@ -174,6 +174,7 @@ public function testGetDefaultsForGlobalSection(): void StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS => [], StageConfigInterface::VAR_MIN_LOGGING_LEVEL => '', StageConfigInterface::VAR_X_FRAME_CONFIGURATION => 'SAMEORIGIN', + StageConfigInterface::VAR_ENABLE_EVENTING => false, ], $this->schema->getDefaults(StageConfigInterface::STAGE_GLOBAL) ); diff --git a/src/Test/Unit/Config/Validator/Build/OpcacheExcludePathsTest.php b/src/Test/Unit/Config/Validator/Build/OpcacheExcludePathsTest.php new file mode 100644 index 0000000000..68fbe63263 --- /dev/null +++ b/src/Test/Unit/Config/Validator/Build/OpcacheExcludePathsTest.php @@ -0,0 +1,282 @@ +fileMock = $this->createMock(File::class); + $this->fileListMock = $this->createMock(FileList::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->opcacheExcludePaths = new OpcacheExcludePaths( + $this->fileMock, + $this->fileListMock, + $this->resultFactoryMock + ); + } + + /** + * @return void + * @throws \Magento\MagentoCloud\Config\ValidatorException + */ + public function testValidateSuccess(): void + { + $phpIniPath = '/app/php.ini'; + $excludeListPath = '/app/op-exclude.txt'; + $phpIni = ['opcache.blacklist_filename' => $excludeListPath]; + $excludeList = <<fileListMock->expects($this->once()) + ->method('getPhpIni') + ->willReturn($phpIniPath); + $this->fileListMock->expects($this->once()) + ->method('getOpCacheExcludeList') + ->willReturn($excludeListPath); + $this->fileMock->expects($this->exactly(2)) + ->method('isExists') + ->withConsecutive([$phpIniPath], [$excludeListPath]) + ->willReturn(true); + $this->fileMock->expects($this->once()) + ->method('parseIni') + ->with($phpIniPath) + ->willReturn($phpIni); + $this->fileMock->expects($this->once()) + ->method('fileGetContents') + ->with($excludeListPath) + ->willReturn($excludeList); + $this->resultFactoryMock->expects($this->never()) + ->method('error'); + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultInterface::SUCCESS) + ->willReturn($this->createMock(Success::class)); + + $this->assertInstanceOf( + Success::class, + $this->opcacheExcludePaths->validate() + ); + } + + /** + * @param int $invokeCount + * @param bool $phpIniExists + * @param bool $opCacheExcludeListExists + * @return void + * @throws \Magento\MagentoCloud\Config\ValidatorException + * @dataProvider validateFilesDoNotExistDataProvider + */ + public function testValidateFilesDoNotExist( + int $invokeCount, + bool $phpIniExists, + bool $opCacheExcludeListExists + ): void { + $phpIniPath = '/app/php.ini'; + $excludeListPath = '/app/op-exclude.txt'; + + $this->fileListMock->expects($this->once()) + ->method('getPhpIni') + ->willReturn($phpIniPath); + $this->fileListMock->expects($this->once()) + ->method('getOpCacheExcludeList') + ->willReturn($excludeListPath); + $this->fileMock->expects($this->exactly($invokeCount)) + ->method('isExists') + ->withConsecutive([$phpIniPath], [$excludeListPath]) + ->willReturnOnConsecutiveCalls($phpIniExists, $opCacheExcludeListExists); + $this->resultFactoryMock->expects($this->never()) + ->method('create'); + $this->resultFactoryMock->expects($this->once()) + ->method('error') + ->with( + 'File php.ini or op-exclude.txt does not exist', + 'Check if your cloud template contains latest php.ini and op-exclude.txt files', + AppError::WARN_WRONG_OPCACHE_CONFIG + ) + ->willReturn($this->createMock(ResultError::class)); + + $this->assertInstanceOf( + ResultError::class, + $this->opcacheExcludePaths->validate() + ); + } + + /** + * @return array[] + */ + public function validateFilesDoNotExistDataProvider(): array + { + return [ + [ + 'invokeCount' => 2, + 'phpIniExists' => true, + 'opCacheExcludeListExists' => false + ], + [ + 'invokeCount' => 1, + 'phpIniExists' => false, + 'opCacheExcludeListExists' => true + ], + [ + 'invokeCount' => 1, + 'phpIniExists' => false, + 'opCacheExcludeListExists' => false + ], + ]; + } + + /** + * @param array|bool $phpIni + * @return void + * @throws \Magento\MagentoCloud\Config\ValidatorException + * @dataProvider validatePhpIniWrongConfigurationDataProvider + */ + public function testValidatePhpIniWrongConfiguration($phpIni): void + { + $phpIniPath = '/app/php.ini'; + $excludeListPath = '/app/op-exclude.txt'; + + $this->fileListMock->expects($this->once()) + ->method('getPhpIni') + ->willReturn($phpIniPath); + $this->fileListMock->expects($this->once()) + ->method('getOpCacheExcludeList') + ->willReturn($excludeListPath); + $this->fileMock->expects($this->exactly(2)) + ->method('isExists') + ->withConsecutive([$phpIniPath], [$excludeListPath]) + ->willReturn(true); + $this->fileMock->expects($this->once()) + ->method('parseIni') + ->with($phpIniPath) + ->willReturn($phpIni); + $this->resultFactoryMock->expects($this->never()) + ->method('create'); + $this->resultFactoryMock->expects($this->once()) + ->method('error') + ->with( + 'File php.ini does not contain opcache.blacklist_filename configuration', + 'Check if your cloud template contains latest php.ini configuration file' + . ' https://github.com/magento/magento-cloud/blob/master/php.ini', + AppError::WARN_WRONG_OPCACHE_CONFIG + ) + ->willReturn($this->createMock(ResultError::class)); + + $this->assertInstanceOf( + ResultError::class, + $this->opcacheExcludePaths->validate() + ); + } + + /** + * @return array + */ + public function validatePhpIniWrongConfigurationDataProvider(): array + { + return [ + ['phpIni' => false], + ['phpIni' => ['opcache.blacklist_filename' => '/tmp/some.file']], + ['phpIni' => ['some.config' => 'some.value']], + ]; + } + + /** + * @return void + * @throws \Magento\MagentoCloud\Config\ValidatorException + */ + public function testValidateMissedPaths(): void + { + $phpIniPath = '/app/php.ini'; + $excludeListPath = '/app/op-exclude.txt'; + $phpIni = ['opcache.blacklist_filename' => $excludeListPath]; + $excludeList = <<fileListMock->expects($this->once()) + ->method('getPhpIni') + ->willReturn($phpIniPath); + $this->fileListMock->expects($this->once()) + ->method('getOpCacheExcludeList') + ->willReturn($excludeListPath); + $this->fileMock->expects($this->exactly(2)) + ->method('isExists') + ->withConsecutive([$phpIniPath], [$excludeListPath]) + ->willReturn(true); + $this->fileMock->expects($this->once()) + ->method('parseIni') + ->with($phpIniPath) + ->willReturn($phpIni); + $this->fileMock->expects($this->once()) + ->method('fileGetContents') + ->with($excludeListPath) + ->willReturn($excludeList); + $this->resultFactoryMock->expects($this->once()) + ->method('error') + ->with( + 'File op-exclude.txt does not contain required paths to exclude for OPCache', + 'Check if your op-exclude.txt contains the next paths:' . PHP_EOL + . '/app/*/app/etc/config.php'. PHP_EOL .'/app/*/app/etc/env.php', + AppError::WARN_WRONG_OPCACHE_CONFIG + ) + ->willReturn($this->createMock(ResultError::class)); + + $this->assertInstanceOf( + ResultError::class, + $this->opcacheExcludePaths->validate() + ); + } +} diff --git a/src/Test/Unit/Filesystem/ConfigFileListTest.php b/src/Test/Unit/Filesystem/ConfigFileListTest.php index 9b18e96fb3..16157ef4d6 100644 --- a/src/Test/Unit/Filesystem/ConfigFileListTest.php +++ b/src/Test/Unit/Filesystem/ConfigFileListTest.php @@ -72,4 +72,14 @@ public function testGetErrorReportConfig(): void { $this->assertSame('magento_root/pub/errors/local.xml', $this->configFileList->getErrorReportConfig()); } + + public function testGetPhpIni(): void + { + $this->assertSame('magento_root/php.ini', $this->configFileList->getPhpIni()); + } + + public function testGetOpCacheExcludeList(): void + { + $this->assertSame('magento_root/op-exclude.txt', $this->configFileList->getOpCacheExcludeList()); + } } diff --git a/src/Test/Unit/Step/Build/EnableEventingTest.php b/src/Test/Unit/Step/Build/EnableEventingTest.php new file mode 100644 index 0000000000..e97d692127 --- /dev/null +++ b/src/Test/Unit/Step/Build/EnableEventingTest.php @@ -0,0 +1,165 @@ +loggerMock = $this->getMockForAbstractClass(LoggerInterface::class); + $this->magentoShellMock = $this->createMock(MagentoShell::class); + /** @var ShellFactory|MockObject $shellFactoryMock */ + $shellFactoryMock = $this->createMock(ShellFactory::class); + $shellFactoryMock->expects($this->once()) + ->method('createMagento') + ->willReturn($this->magentoShellMock); + $this->globalConfigMock = $this->createMock(GlobalSection::class); + + $this->step = new EnableEventing( + $this->loggerMock, + $shellFactoryMock, + $this->globalConfigMock + ); + } + + /** + * @return void + * @throws \Magento\MagentoCloud\Step\StepException + */ + public function testExecuteEventingNotEnabled() + { + $this->globalConfigMock->expects(self::once()) + ->method('get') + ->with(StageConfigInterface::VAR_ENABLE_EVENTING) + ->willReturn(false); + + $this->magentoShellMock->expects(self::never()) + ->method('execute'); + $this->loggerMock->expects(self::never()) + ->method('notice'); + + $this->step->execute(); + } + + /** + * @return void + * @throws \Magento\MagentoCloud\Step\StepException + */ + public function testExecuteGenerateCommandFailed() + { + $this->expectException(StepException::class); + $this->expectExceptionMessage('error during module generation'); + $this->expectExceptionCode(Error::GLOBAL_EVENTING_MODULE_GENERATE_FAILED); + + $this->globalConfigMock->expects(self::once()) + ->method('get') + ->with(StageConfigInterface::VAR_ENABLE_EVENTING) + ->willReturn(true); + $this->magentoShellMock->expects(self::once()) + ->method('execute') + ->with('events:generate:module') + ->willThrowException(new ShellException('error during module generation')); + $this->loggerMock->expects(self::once()) + ->method('notice'); + $this->loggerMock->expects(self::once()) + ->method('error'); + + $this->step->execute(); + } + + /** + * @return void + * @throws \Magento\MagentoCloud\Step\StepException + */ + public function testExecuteEnableModuleCommandFailed() + { + $this->expectException(StepException::class); + $this->expectExceptionMessage('error during module enablement'); + $this->expectExceptionCode(Error::GLOBAL_EVENTING_MODULE_ENABLEMENT_FAILED); + + $this->globalConfigMock->expects(self::once()) + ->method('get') + ->with(StageConfigInterface::VAR_ENABLE_EVENTING) + ->willReturn(true); + $this->magentoShellMock->expects(self::at(0)) + ->method('execute') + ->with('events:generate:module'); + $this->magentoShellMock->expects(self::at(1)) + ->method('execute') + ->with('module:enable Magento_AdobeCommerceEvents') + ->willThrowException(new ShellException('error during module enablement')); + $this->loggerMock->expects(self::exactly(2)) + ->method('notice'); + $this->loggerMock->expects(self::once()) + ->method('error'); + + $this->step->execute(); + } + + /** + * @return void + * @throws \Magento\MagentoCloud\Step\StepException + */ + public function testExecuteSuccess() + { + $this->globalConfigMock->expects(self::once()) + ->method('get') + ->with(StageConfigInterface::VAR_ENABLE_EVENTING) + ->willReturn(true); + $this->magentoShellMock->expects(self::at(0)) + ->method('execute') + ->with('events:generate:module'); + $this->magentoShellMock->expects(self::at(1)) + ->method('execute') + ->with('module:enable Magento_AdobeCommerceEvents'); + $this->loggerMock->expects(self::exactly(2)) + ->method('notice'); + $this->loggerMock->expects(self::never()) + ->method('error'); + + $this->step->execute(); + } +} diff --git a/src/Test/Unit/Step/Deploy/PreDeploy/ConfigUpdate/CacheTest.php b/src/Test/Unit/Step/Deploy/PreDeploy/ConfigUpdate/CacheTest.php index 8714e81636..9f727330bc 100644 --- a/src/Test/Unit/Step/Deploy/PreDeploy/ConfigUpdate/CacheTest.php +++ b/src/Test/Unit/Step/Deploy/PreDeploy/ConfigUpdate/CacheTest.php @@ -106,28 +106,36 @@ protected function setUp(): void } /** + * @param array $configFromFile * @param array $config + * @param array $finalConfig * @param bool $isGreaterOrEqual * @param string $address * @param int $port * @throws StepException * @dataProvider executeDataProvider */ - public function testExecute(array $config, bool $isGreaterOrEqual, $address, $port) - { + public function testExecute( + array $configFromFile, + array $config, + array $finalConfig, + bool $isGreaterOrEqual, + $address, + $port + ) { $this->magentoVersion->expects($this->any()) ->method('isGreaterOrEqual') ->with('2.3.0') ->willReturn($isGreaterOrEqual); $this->configReaderMock->expects($this->once()) ->method('read') - ->willReturn([]); + ->willReturn($configFromFile); $this->cacheConfigMock->expects($this->once()) ->method('get') ->willReturn($config); $this->configWriterMock->expects($this->once()) ->method('create') - ->with(['cache' => $config]); + ->with($finalConfig); $this->loggerMock->expects($this->once()) ->method('info') ->with('Updating cache configuration.'); @@ -145,10 +153,51 @@ public function testExecute(array $config, bool $isGreaterOrEqual, $address, $po $this->step->execute(); } + /** + * @return array[] + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function executeDataProvider(): array { return [ + 'with qraphql config in file' => [ + 'configFromFile' => [ + 'cache' => ['graphql' => ['id_salt' => 'some salt']], + ], + 'config' => [ + 'frontend' => [ + 'frontName' => [ + 'backend' => CacheFactory::REDIS_BACKEND_CM_CACHE, + 'backend_options' => [ + 'server' => 'localhost', + 'port' => 6370, + ], + ], + ], + ], + 'finalConfig' => [ + 'cache' => [ + 'frontend' => [ + 'frontName' => [ + 'backend' => CacheFactory::REDIS_BACKEND_CM_CACHE, + 'backend_options' => [ + 'server' => 'localhost', + 'port' => 6370, + ], + ], + ], + 'graphql' => [ + 'id_salt' => 'some salt', + ], + ], + ], + 'isGreaterOrEqual' => false, + 'address' => 'localhost', + 'port' => 6370 + ], 'backend model without remote_backend_options' => [ + 'configFromFile' => [], 'config' => [ 'frontend' => [ 'frontName' => [ @@ -160,11 +209,25 @@ public function executeDataProvider(): array ], ], ], + 'finalConfig' => [ + 'cache' => [ + 'frontend' => [ + 'frontName' => [ + 'backend' => CacheFactory::REDIS_BACKEND_CM_CACHE, + 'backend_options' => [ + 'server' => 'localhost', + 'port' => 6370, + ], + ], + ], + ], + ], 'isGreaterOrEqual' => false, 'address' => 'localhost', 'port' => 6370 ], 'backend model with remote_backend_options' => [ + 'configFromFile' => [], 'config' => [ 'frontend' => [ 'frontName' => [ @@ -178,11 +241,27 @@ public function executeDataProvider(): array ], ], ], + 'finalConfig' => [ + 'cache' => [ + 'frontend' => [ + 'frontName' => [ + 'backend' => CacheFactory::REDIS_BACKEND_REMOTE_SYNCHRONIZED_CACHE, + 'backend_options' => [ + 'remote_backend_options' => [ + 'server' => 'localhost', + 'port' => 6370, + ], + ], + ], + ], + ], + ], 'isGreaterOrEqual' => true, 'address' => 'localhost', 'port' => 6370 ], 'Server contains port data' => [ + 'configFromFile' => [], 'config' => [ 'frontend' => [ 'frontName' => [ @@ -195,11 +274,26 @@ public function executeDataProvider(): array ], ], ], + 'finalConfig' => [ + 'cache' => [ + 'frontend' => [ + 'frontName' => [ + 'backend' => CacheFactory::REDIS_BACKEND_REMOTE_SYNCHRONIZED_CACHE, + 'backend_options' => [ + 'remote_backend_options' => [ + 'server' => '127.0.0.1:6371', + ], + ], + ], + ], + ], + ], 'isGreaterOrEqual' => true, 'address' => '127.0.0.1', 'port' => 6371 ], 'Server contains protocol and port data' => [ + 'configFromFile' => [], 'config' => [ 'frontend' => [ 'frontName' => [ @@ -212,6 +306,20 @@ public function executeDataProvider(): array ], ], ], + 'finalConfig' => [ + 'cache' => [ + 'frontend' => [ + 'frontName' => [ + 'backend' => CacheFactory::REDIS_BACKEND_REMOTE_SYNCHRONIZED_CACHE, + 'backend_options' => [ + 'remote_backend_options' => [ + 'server' => 'tcp://localhost:6379', + ], + ], + ], + ], + ], + ], 'isGreaterOrEqual' => true, 'address' => 'localhost', 'port' => 6379 @@ -219,19 +327,24 @@ public function executeDataProvider(): array ]; } - public function testExecuteEmptyConfig() + /** + * @param array $cacheConfig + * @param array $finalConfig + * @return void + * @throws StepException + * @dataProvider executeEmptyConfig + */ + public function testExecuteEmptyConfig(array $cacheConfig, array $finalConfig): void { $this->configReaderMock->expects($this->once()) ->method('read') - ->willReturn(['cache' => [ - 'frontend' => ['frontName' => ['backend' => 'cacheDriver']], - ]]); + ->willReturn($cacheConfig); $this->cacheConfigMock->expects($this->once()) ->method('get') ->willReturn([]); $this->configWriterMock->expects($this->once()) ->method('create') - ->with([]); + ->with($finalConfig); $this->loggerMock->expects($this->once()) ->method('info') ->with('Cache configuration was not found. Removing cache configuration.'); @@ -243,6 +356,33 @@ public function testExecuteEmptyConfig() $this->step->execute(); } + public function executeEmptyConfig(): array + { + return [ + 'without graphql in config' => [ + 'cacheConfig' => [ + 'cache' => [ + 'frontend' => ['frontName' => ['backend' => 'cacheDriver']], + ], + ], + 'finalConfig' => [], + ], + 'with graphql in config' => [ + 'cacheConfig' => [ + 'cache' => [ + 'frontend' => ['frontName' => ['backend' => 'cacheDriver']], + 'graphql' => ['id_salt' => 'some salt'], + ], + ], + 'finalConfig' => [ + 'cache' => [ + 'graphql' => ['id_salt' => 'some salt'], + ], + ], + ], + ]; + } + public function testExecuteRedisService() { $this->prepareMocks();