diff --git a/src/App/Container.php b/src/App/Container.php index b871bf068f..95717faa56 100644 --- a/src/App/Container.php +++ b/src/App/Container.php @@ -148,10 +148,12 @@ function () { 'validators' => [ ValidatorInterface::LEVEL_CRITICAL => [ $this->container->make(ConfigValidator\Build\StageConfig::class), + $this->container->make(ConfigValidator\Build\BuildOptionsIni::class), ], ValidatorInterface::LEVEL_WARNING => [ $this->container->make(ConfigValidator\Build\ConfigFileExists::class), $this->container->make(ConfigValidator\Build\ConfigFileStructure::class), + $this->container->make(ConfigValidator\Build\DeprecatedBuildOptionsIni::class), $this->container->make(ConfigValidator\Build\ModulesExists::class), ], ], @@ -198,8 +200,8 @@ function () { 'validators' => [ ValidatorInterface::LEVEL_CRITICAL => [ $this->container->make(ConfigValidator\Deploy\AdminEmail::class), - $this->container->make(ConfigValidator\Build\StageConfig::class), - $this->container->make(ConfigValidator\Deploy\Variables::class), + $this->container->make(ConfigValidator\Deploy\RawEnvVariable::class), + $this->container->make(ConfigValidator\Deploy\MagentoCloudVariables::class), $this->container->make(ConfigValidator\Deploy\AdminCredentials::class), ], ValidatorInterface::LEVEL_WARNING => [ diff --git a/src/Config/Environment.php b/src/Config/Environment.php index dc459341cf..bec1e9f842 100755 --- a/src/Config/Environment.php +++ b/src/Config/Environment.php @@ -85,7 +85,6 @@ public function __construct( * returns false if not found * * @param string $key - * @param string|int|null $default * @return array|string|int|null */ public function getEnv(string $key) diff --git a/src/Config/GlobalSection.php b/src/Config/GlobalSection.php index 808bb7df20..b363c6c9c7 100644 --- a/src/Config/GlobalSection.php +++ b/src/Config/GlobalSection.php @@ -24,12 +24,19 @@ class GlobalSection implements StageConfigInterface */ private $mergedConfig; + /** + * @var Schema + */ + private $schema; + /** * @param EnvironmentReader $environmentReader + * @param Schema $schema */ - public function __construct(EnvironmentReader $environmentReader) + public function __construct(EnvironmentReader $environmentReader, Schema $schema) { $this->environmentReader = $environmentReader; + $this->schema = $schema; } /** @@ -37,7 +44,7 @@ public function __construct(EnvironmentReader $environmentReader) */ public function get(string $name) { - if (!array_key_exists($name, $this->getDefault())) { + if (!array_key_exists($name, $this->schema->getDefaults(StageConfigInterface::STAGE_GLOBAL))) { throw new \RuntimeException(sprintf( 'Config %s was not defined.', $name @@ -66,26 +73,11 @@ private function mergeConfig(): array $envConfig = $this->environmentReader->read()[self::SECTION_STAGE] ?? []; $this->mergedConfig = array_replace( - $this->getDefault(), + $this->schema->getDefaults(StageConfigInterface::STAGE_GLOBAL), $envConfig[self::STAGE_GLOBAL] ?? [] ); } return $this->mergedConfig; } - - /** - * Resolves default configuration value if other was not provided. - * - * @return array - */ - private function getDefault(): array - { - return [ - self::VAR_SCD_ON_DEMAND => false, - self::VAR_SKIP_HTML_MINIFICATION => false, - self::VAR_DEPLOYED_MAGENTO_VERSION_FROM_GIT => false, - self::VAR_DEPLOY_FROM_GIT_OPTIONS => [], - ]; - } } diff --git a/src/Config/Schema.php b/src/Config/Schema.php new file mode 100644 index 0000000000..021b3633b5 --- /dev/null +++ b/src/Config/Schema.php @@ -0,0 +1,316 @@ +defaults[$stage])) { + return $this->defaults[$stage]; + } + + foreach ($this->getSchema() as $itemName => $itemOptions) { + if (isset($itemOptions[self::SCHEMA_DEFAULT_VALUE][$stage])) { + $this->defaults[$stage][$itemName] = $itemOptions[self::SCHEMA_DEFAULT_VALUE][$stage]; + } + } + + return $this->defaults[$stage]; + } + + /** + * Returns configuration schema. + * + * Each configuration item can have next options: + * 'type' - possible types (string, integer, array, etc..) + * 'value_validation' - array of possible values or callback validation function + * 'stage' - possible stages in which item can be configured + * 'default_values' - array of default values + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getSchema() + { + return [ + StageConfigInterface::VAR_VERBOSE_COMMANDS => [ + self::SCHEMA_TYPE => ['string'], + self::SCHEMA_VALUE_VALIDATION => ['', '-v', '-vv', '-vvv'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => '', + StageConfigInterface::STAGE_DEPLOY => '', + ], + ], + StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL => [ + self::SCHEMA_TYPE => ['integer'], + self::SCHEMA_VALUE_VALIDATION => function (string $key, $value) { + if (!in_array($value, range(0, 9))) { + return sprintf( + 'The SCD_COMPRESSION_LEVEL variable contains an invalid value of type string. ' . + 'Use an integer value from 0 to 9.', + $key, + $value + ); + } + }, + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => 6, + StageConfigInterface::STAGE_DEPLOY => 4, + ], + ], + StageConfigInterface::VAR_SCD_STRATEGY => [ + self::SCHEMA_TYPE => ['string'], + self::SCHEMA_VALUE_VALIDATION => ['compact', 'quick', 'standard'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => '', + StageConfigInterface::STAGE_DEPLOY => '', + ], + ], + StageConfigInterface::VAR_SCD_THREADS => [ + self::SCHEMA_TYPE => ['integer'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => 1, + StageConfigInterface::STAGE_DEPLOY => 1, + ], + ], + StageConfigInterface::VAR_SCD_EXCLUDE_THEMES => [ + self::SCHEMA_TYPE => ['string'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => '', + StageConfigInterface::STAGE_DEPLOY => '', + ], + ], + StageConfigInterface::VAR_SCD_MATRIX => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => [], + StageConfigInterface::STAGE_DEPLOY => [], + ], + ], + StageConfigInterface::VAR_SKIP_SCD => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_BUILD, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_BUILD => false, + StageConfigInterface::STAGE_DEPLOY => false, + ], + ], + StageConfigInterface::VAR_SKIP_HTML_MINIFICATION => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_GLOBAL => false, + ], + ], + StageConfigInterface::VAR_SCD_ON_DEMAND => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_GLOBAL => false, + ], + ], + StageConfigInterface::VAR_DEPLOYED_MAGENTO_VERSION_FROM_GIT => [ + self::SCHEMA_TYPE => ['array', 'boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_GLOBAL => false, + ], + ], + StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_GLOBAL => [], + ], + ], + DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => false, + ], + ], + DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => false, + ], + ], + DeployInterface::VAR_UPDATE_URLS => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => true, + ], + ], + DeployInterface::VAR_STATIC_CONTENT_SYMLINK => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => true, + ], + ], + DeployInterface::VAR_CLEAN_STATIC_FILES => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => true, + ], + ], + DeployInterface::VAR_SEARCH_CONFIGURATION => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => [], + ], + ], + DeployInterface::VAR_QUEUE_CONFIGURATION => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => [], + ], + ], + DeployInterface::VAR_CACHE_CONFIGURATION => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => [], + ], + ], + DeployInterface::VAR_SESSION_CONFIGURATION => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => [], + ], + ], + DeployInterface::VAR_CRON_CONSUMERS_RUNNER => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => [], + ], + ], + DeployInterface::VAR_GENERATED_CODE_SYMLINK => [ + self::SCHEMA_TYPE => ['boolean'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_DEPLOY => true, + ], + ], + PostDeployInterface::VAR_WARM_UP_PAGES => [ + self::SCHEMA_TYPE => ['array'], + self::SCHEMA_STAGE => [ + StageConfigInterface::STAGE_GLOBAL, + StageConfigInterface::STAGE_POST_DEPLOY + ], + self::SCHEMA_DEFAULT_VALUE => [ + StageConfigInterface::STAGE_POST_DEPLOY => ['index.php'], + ], + ] + ]; + } +} diff --git a/src/Config/Stage/Build.php b/src/Config/Stage/Build.php index bfaa12dd6d..ef2767017a 100644 --- a/src/Config/Stage/Build.php +++ b/src/Config/Stage/Build.php @@ -7,6 +7,8 @@ use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; use Magento\MagentoCloud\Config\Build\Reader as BuildReader; +use Magento\MagentoCloud\Config\Schema; +use Magento\MagentoCloud\Config\StageConfigInterface; use Magento\MagentoCloud\Filesystem\FileSystemException; use Symfony\Component\Yaml\Exception\ParseException; @@ -30,16 +32,24 @@ class Build implements BuildInterface */ private $mergedConfig; + /** + * @var Schema + */ + private $schema; + /** * @param EnvironmentReader $environmentReader * @param BuildReader $buildReader + * @param Schema $schema */ public function __construct( EnvironmentReader $environmentReader, - BuildReader $buildReader + BuildReader $buildReader, + Schema $schema ) { $this->environmentReader = $environmentReader; $this->buildReader = $buildReader; + $this->schema = $schema; } /** @@ -47,7 +57,7 @@ public function __construct( */ public function get(string $name) { - if (!array_key_exists($name, $this->getDefault())) { + if (!array_key_exists($name, $this->schema->getDefaults(StageConfigInterface::STAGE_BUILD))) { throw new \RuntimeException(sprintf( 'Config %s was not defined.', $name @@ -76,7 +86,7 @@ private function mergeConfig(): array $envConfig = $this->environmentReader->read()[self::SECTION_STAGE] ?? []; $this->mergedConfig = array_replace( - $this->getDefault(), + $this->schema->getDefaults(StageConfigInterface::STAGE_BUILD), $envConfig[self::STAGE_GLOBAL] ?? [], $envConfig[self::STAGE_BUILD] ?? [], $this->getDeprecatedConfig() @@ -86,24 +96,6 @@ private function mergeConfig(): array return $this->mergedConfig; } - /** - * Resolves default configuration value if other was not provided. - * - * @return array - */ - private function getDefault(): array - { - return [ - self::VAR_SCD_STRATEGY => '', - self::VAR_SKIP_SCD => false, - self::VAR_SCD_COMPRESSION_LEVEL => 6, - self::VAR_SCD_THREADS => 1, - self::VAR_SCD_EXCLUDE_THEMES => '', - self::VAR_VERBOSE_COMMANDS => '', - self::VAR_SCD_MATRIX => [], - ]; - } - /** * Resolves configuration from deprecated build configuration file build_options.ini * diff --git a/src/Config/Stage/Deploy.php b/src/Config/Stage/Deploy.php index a917113ebe..2c58a9c564 100644 --- a/src/Config/Stage/Deploy.php +++ b/src/Config/Stage/Deploy.php @@ -5,8 +5,11 @@ */ namespace Magento\MagentoCloud\Config\Stage; +use Magento\MagentoCloud\Config\Environment; use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; -use Magento\MagentoCloud\Config\Environment as EnvironmentConfig; +use Magento\MagentoCloud\Config\Schema; +use Magento\MagentoCloud\Config\Stage\Deploy\EnvironmentConfig; +use Magento\MagentoCloud\Config\StageConfigInterface; use Magento\MagentoCloud\Filesystem\FileSystemException; use Symfony\Component\Yaml\Exception\ParseException; @@ -31,15 +34,31 @@ class Deploy implements DeployInterface private $mergedConfig; /** + * @var Environment + */ + private $environment; + + /** + * @var Schema + */ + private $schema; + + /** + * @param Environment $environment * @param EnvironmentReader $environmentReader * @param EnvironmentConfig $environmentConfig + * @param Schema $schema */ public function __construct( + Environment $environment, EnvironmentReader $environmentReader, - EnvironmentConfig $environmentConfig + EnvironmentConfig $environmentConfig, + Schema $schema ) { + $this->environment = $environment; $this->environmentReader = $environmentReader; $this->environmentConfig = $environmentConfig; + $this->schema = $schema; } /** @@ -47,7 +66,7 @@ public function __construct( */ public function get(string $name) { - if (!array_key_exists($name, $this->getDefault())) { + if (!array_key_exists($name, $this->schema->getDefaults(StageConfigInterface::STAGE_DEPLOY))) { throw new \RuntimeException(sprintf( 'Config %s was not defined.', $name @@ -87,10 +106,11 @@ private function mergeConfig(): array $envConfig = $this->environmentReader->read()[self::SECTION_STAGE] ?? []; $this->mergedConfig = array_replace( - $this->getDefault(), + $this->schema->getDefaults(StageConfigInterface::STAGE_DEPLOY), + $this->getDeployConfiguration(), $envConfig[self::STAGE_GLOBAL] ?? [], $envConfig[self::STAGE_DEPLOY] ?? [], - $this->getEnvironmentConfig() + $this->environmentConfig->getAll() ); } @@ -98,116 +118,20 @@ private function mergeConfig(): array } /** - * Resolves environment values with and adds custom mappings. - * - * @return array - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - private function getEnvironmentConfig(): array - { - $variables = $this->environmentConfig->getVariables(); - - if (isset($variables[self::VAR_VERBOSE_COMMANDS]) - && $variables[self::VAR_VERBOSE_COMMANDS] === EnvironmentConfig::VAL_ENABLED - ) { - $variables[self::VAR_VERBOSE_COMMANDS] = '-vvv'; - } - - $disabledFlow = [ - self::VAR_CLEAN_STATIC_FILES, - self::VAR_STATIC_CONTENT_SYMLINK, - self::VAR_UPDATE_URLS, - self::VAR_GENERATED_CODE_SYMLINK, - ]; - - foreach ($disabledFlow as $disabledVar) { - if (isset($variables[$disabledVar]) && $variables[$disabledVar] === EnvironmentConfig::VAL_DISABLED) { - $variables[$disabledVar] = false; - } - } - - if (isset($variables['DO_DEPLOY_STATIC_CONTENT']) && - $variables['DO_DEPLOY_STATIC_CONTENT'] === EnvironmentConfig::VAL_DISABLED - ) { - $variables[self::VAR_SKIP_SCD] = true; - } - - if ($scdThreads = $this->getEnvScdThreads()) { - $variables[self::VAR_SCD_THREADS] = $scdThreads; - } - - if (isset($variables['STATIC_CONTENT_EXCLUDE_THEMES'])) { - $variables[self::VAR_SCD_EXCLUDE_THEMES] = $variables['STATIC_CONTENT_EXCLUDE_THEMES']; - } - - return $variables; - } - - /** - * Retrieves SCD threads configuration from MAGENTO_CLOUD_VARIABLES or from raw environment data. - * STATIC_CONTENT_THREADS from MAGENTO_CLOUD_VARIABLES has higher priority then $_ENV['STATIC_CONTENT_THREADS'] - * - * Raw $_ENV['STATIC_CONTENT_THREADS'] is deprecated. + * Resolves default configuration value for deploy stage. * - * @return int - */ - private function getEnvScdThreads(): int - { - $variables = $this->environmentConfig->getVariables(); - $staticDeployThreads = 0; - - if (isset($variables['STATIC_CONTENT_THREADS'])) { - $staticDeployThreads = (int)$variables['STATIC_CONTENT_THREADS']; - } elseif (isset($_ENV['STATIC_CONTENT_THREADS'])) { - $staticDeployThreads = (int)$this->environmentConfig->getEnv('STATIC_CONTENT_THREADS'); - } - - return $staticDeployThreads; - } - - /** - * Resolves default configuration value if other was not provided. + * SCD_THREADS = 3 for production environment. * * @return array */ - private function getDefault(): array + private function getDeployConfiguration(): array { - return [ - self::VAR_SCD_STRATEGY => '', - self::VAR_SCD_COMPRESSION_LEVEL => 4, - self::VAR_SEARCH_CONFIGURATION => [], - self::VAR_QUEUE_CONFIGURATION => [], - self::VAR_CACHE_CONFIGURATION => [], - self::VAR_SESSION_CONFIGURATION => [], - self::VAR_VERBOSE_COMMANDS => '', - self::VAR_CRON_CONSUMERS_RUNNER => [], - self::VAR_CLEAN_STATIC_FILES => true, - self::VAR_STATIC_CONTENT_SYMLINK => true, - self::VAR_UPDATE_URLS => true, - self::VAR_SKIP_SCD => false, - self::VAR_SCD_THREADS => $this->getDefaultScdThreads(), - self::VAR_GENERATED_CODE_SYMLINK => true, - self::VAR_SCD_EXCLUDE_THEMES => '', - self::VAR_REDIS_USE_SLAVE_CONNECTION => false, - self::VAR_MYSQL_USE_SLAVE_CONNECTION => false, - self::VAR_SCD_MATRIX => [], - ]; - } + $config = []; - /** - * Retrieves default scd threads value. - * 3 if production environment otherwise 1 - * - * @return int - */ - private function getDefaultScdThreads() - { - if (isset($_ENV['MAGENTO_CLOUD_MODE']) - && $_ENV['MAGENTO_CLOUD_MODE'] === EnvironmentConfig::CLOUD_MODE_ENTERPRISE - ) { - return 3; + if ($this->environment->getEnv('MAGENTO_CLOUD_MODE') === Environment::CLOUD_MODE_ENTERPRISE) { + $config[self::VAR_SCD_THREADS] = 3; } - return 1; + return $config; } } diff --git a/src/Config/Stage/Deploy/EnvironmentConfig.php b/src/Config/Stage/Deploy/EnvironmentConfig.php new file mode 100644 index 0000000000..a995f4d109 --- /dev/null +++ b/src/Config/Stage/Deploy/EnvironmentConfig.php @@ -0,0 +1,95 @@ +environment = $environment; + } + + /** + * Resolves environment values with and adds custom mappings. + * + * STATIC_CONTENT_THREADS from MAGENTO_CLOUD_VARIABLES has higher priority then $_ENV['STATIC_CONTENT_THREADS'] + * Raw $_ENV['STATIC_CONTENT_THREADS'] is deprecated. + * + * @return array + */ + public function getAll(): array + { + $variables = $this->convertEnabledDisabledVariables($this->environment->getVariables()); + + if (isset($variables[DeployInterface::VAR_STATIC_CONTENT_THREADS])) { + $envScdThreads = $variables[DeployInterface::VAR_STATIC_CONTENT_THREADS]; + unset($variables[DeployInterface::VAR_STATIC_CONTENT_THREADS]); + } else { + $envScdThreads = $this->environment->getEnv(DeployInterface::VAR_STATIC_CONTENT_THREADS); + } + + if (ctype_digit($envScdThreads)) { + $variables[DeployInterface::VAR_SCD_THREADS] = (int)$envScdThreads; + } + + if (isset($variables['STATIC_CONTENT_EXCLUDE_THEMES'])) { + $variables[DeployInterface::VAR_SCD_EXCLUDE_THEMES] = $variables['STATIC_CONTENT_EXCLUDE_THEMES']; + unset($variables['STATIC_CONTENT_EXCLUDE_THEMES']); + } + + return $variables; + } + + /** + * Converts all existence variables with disabled/enabled values to appropriate format. + * + * @param array $variables + * @return array + */ + private function convertEnabledDisabledVariables(array $variables): array + { + if (isset($variables[DeployInterface::VAR_VERBOSE_COMMANDS]) + && $variables[DeployInterface::VAR_VERBOSE_COMMANDS] === Environment::VAL_ENABLED + ) { + $variables[DeployInterface::VAR_VERBOSE_COMMANDS] = '-vvv'; + } + + $disabledFlow = [ + DeployInterface::VAR_CLEAN_STATIC_FILES, + DeployInterface::VAR_STATIC_CONTENT_SYMLINK, + DeployInterface::VAR_UPDATE_URLS, + DeployInterface::VAR_GENERATED_CODE_SYMLINK, + ]; + + foreach ($disabledFlow as $disabledVar) { + if (isset($variables[$disabledVar]) && $variables[$disabledVar] === Environment::VAL_DISABLED) { + $variables[$disabledVar] = false; + } + } + + if (isset($variables[DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT])) { + $variables[DeployInterface::VAR_SKIP_SCD] = + $variables[DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT] === Environment::VAL_DISABLED; + unset($variables[DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT]); + } + + return $variables; + } +} diff --git a/src/Config/Stage/DeployInterface.php b/src/Config/Stage/DeployInterface.php index 6b38558c62..b8ac1e4869 100644 --- a/src/Config/Stage/DeployInterface.php +++ b/src/Config/Stage/DeployInterface.php @@ -20,7 +20,6 @@ interface DeployInterface extends StageConfigInterface const VAR_CLEAN_STATIC_FILES = 'CLEAN_STATIC_FILES'; const VAR_STATIC_CONTENT_SYMLINK = 'STATIC_CONTENT_SYMLINK'; const VAR_UPDATE_URLS = 'UPDATE_URLS'; - const VAR_STATIC_CONTENT_EXCLUDE_THEMES = 'STATIC_CONTENT_EXCLUDE_THEMES'; /** * The variable responsible to set Redis slave connection when it has true value. @@ -36,4 +35,14 @@ interface DeployInterface extends StageConfigInterface * @deprecated 2.1 specific variable. */ const VAR_GENERATED_CODE_SYMLINK = 'GENERATED_CODE_SYMLINK'; + + /** + * @deprecated use SCD_THREADS instead + */ + const VAR_STATIC_CONTENT_THREADS = 'STATIC_CONTENT_THREADS'; + + /** + * @deprecated use SKIP_SCD instead + */ + const VAR_DO_DEPLOY_STATIC_CONTENT = 'DO_DEPLOY_STATIC_CONTENT'; } diff --git a/src/Config/Stage/PostDeploy.php b/src/Config/Stage/PostDeploy.php index 75fac4c8ce..2de8589ed0 100644 --- a/src/Config/Stage/PostDeploy.php +++ b/src/Config/Stage/PostDeploy.php @@ -6,6 +6,8 @@ namespace Magento\MagentoCloud\Config\Stage; use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; +use Magento\MagentoCloud\Config\Schema; +use Magento\MagentoCloud\Config\StageConfigInterface; /** * @inheritdoc @@ -22,12 +24,18 @@ class PostDeploy implements PostDeployInterface */ private $mergedConfig; + /** + * @var Schema + */ + private $schema; + /** * @param EnvironmentReader $environmentReader */ - public function __construct(EnvironmentReader $environmentReader) + public function __construct(EnvironmentReader $environmentReader, Schema $schema) { $this->environmentReader = $environmentReader; + $this->schema = $schema; } /** @@ -35,7 +43,7 @@ public function __construct(EnvironmentReader $environmentReader) */ public function get(string $name) { - if (!array_key_exists($name, $this->getDefault())) { + if (!array_key_exists($name, $this->schema->getDefaults(StageConfigInterface::STAGE_POST_DEPLOY))) { throw new \RuntimeException(sprintf( 'Config %s was not defined.', $name @@ -63,7 +71,7 @@ private function mergeConfig(): array $envConfig = $this->environmentReader->read()[self::SECTION_STAGE] ?? []; $this->mergedConfig = array_replace( - $this->getDefault(), + $this->schema->getDefaults(StageConfigInterface::STAGE_POST_DEPLOY), $envConfig[self::STAGE_GLOBAL] ?? [], $envConfig[self::STAGE_POST_DEPLOY] ?? [] ); @@ -71,18 +79,4 @@ private function mergeConfig(): array return $this->mergedConfig; } - - /** - * Resolves default configuration value if other was not provided. - * - * @return array - */ - private function getDefault(): array - { - return [ - self::VAR_WARM_UP_PAGES => [ - 'index.php', - ], - ]; - } } diff --git a/src/Config/Validator/Build/BuildOptionsIni.php b/src/Config/Validator/Build/BuildOptionsIni.php new file mode 100644 index 0000000000..cda447fcdc --- /dev/null +++ b/src/Config/Validator/Build/BuildOptionsIni.php @@ -0,0 +1,123 @@ + StageConfigInterface::VAR_SCD_STRATEGY, + 'exclude_themes' => StageConfigInterface::VAR_SCD_EXCLUDE_THEMES, + 'SCD_COMPRESSION_LEVEL' => StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, + 'scd_threads' => StageConfigInterface::VAR_SCD_THREADS, + 'skip_scd' => StageConfigInterface::VAR_SKIP_SCD, + 'VERBOSE_COMMANDS' => StageConfigInterface::VAR_VERBOSE_COMMANDS, + ]; + + /** + * @param ResultFactory $resultFactory + * @param SchemaValidator $schemaValidator + * @param BuildReader $buildReader + */ + public function __construct( + ResultFactory $resultFactory, + SchemaValidator $schemaValidator, + BuildReader $buildReader + ) { + $this->resultFactory = $resultFactory; + $this->schemaValidator = $schemaValidator; + $this->buildReader = $buildReader; + } + + /** + * @return Validator\ResultInterface + */ + public function validate(): Validator\ResultInterface + { + $buildOptionsConfig = $this->buildReader->read(); + $errors = []; + + foreach ($buildOptionsConfig as $name => $value) { + if (!isset($this->buildOptionsMap[$name])) { + $errors[] = sprintf('Option %s is not allowed', $name); + continue; + } + + $value = $this->prepareValue($name, $value); + $error = $this->schemaValidator->validate( + $this->buildOptionsMap[$name], + StageConfigInterface::STAGE_BUILD, + $value + ); + if ($error) { + $error = str_replace($this->buildOptionsMap[$name], $name, $error); + $errors[] = $error; + } + } + + if ($errors) { + return $this->resultFactory->create(Validator\Result\Error::ERROR, [ + 'error' => 'The build_options.ini file contains an unexpected value', + 'suggestion' => implode(PHP_EOL, $errors), + ]); + } + + return $this->resultFactory->create(Validator\Result\Success::SUCCESS); + } + + /** + * Convert option values in the same way as it made in \Magento\MagentoCloud\Config\Stage\Build::getDeprecatedConfig + * + * @param string $name + * @param $value + * @return bool|int|string + */ + private function prepareValue(string $name, $value) + { + if (in_array($name, ['SCD_COMPRESSION_LEVEL', 'scd_threads']) && ctype_digit($value)) { + return (int)$value; + } + + if ($name === 'skip_scd') { + return $value === '1'; + } + + if ($name === 'VERBOSE_COMMANDS') { + return $value === 'enabled' ? '-vv' : ''; + } + + return $value; + } +} diff --git a/src/Config/Validator/Build/DeprecatedBuildOptionsIni.php b/src/Config/Validator/Build/DeprecatedBuildOptionsIni.php new file mode 100644 index 0000000000..b18875b0d7 --- /dev/null +++ b/src/Config/Validator/Build/DeprecatedBuildOptionsIni.php @@ -0,0 +1,65 @@ +file = $file; + $this->fileList = $fileList; + $this->resultFactory = $resultFactory; + } + + /** + * Validates file build_options.ini existence. + * + * @return Validator\ResultInterface + */ + public function validate(): Validator\ResultInterface + { + if ($this->file->isExists($this->fileList->getBuildConfig())) { + return $this->resultFactory->error( + sprintf('The %s file has been deprecated', basename($this->fileList->getBuildConfig())), + sprintf( + 'Modify your configuration to use the %s file', + basename($this->fileList->getEnvConfig()) + ) + ); + } + + return $this->resultFactory->success(); + } +} diff --git a/src/Config/Validator/Build/StageConfig.php b/src/Config/Validator/Build/StageConfig.php index 11726f870a..f36fe3ac78 100644 --- a/src/Config/Validator/Build/StageConfig.php +++ b/src/Config/Validator/Build/StageConfig.php @@ -54,13 +54,12 @@ public function validate(): Validator\ResultInterface $config = $this->environmentReader->read()[StageConfigInterface::SECTION_STAGE] ?? []; $errors = []; - foreach ($config as $stageConfig) { - if (!\is_array($stageConfig)) { + foreach ($config as $stage => $stageConfig) { + if (!is_array($stageConfig)) { continue; } - foreach ($stageConfig as $key => $value) { - if ($error = $this->schemaValidator->validate($key, $value)) { + if ($error = $this->schemaValidator->validate($key, $stage, $value)) { $errors[] = $error; } } @@ -68,8 +67,9 @@ public function validate(): Validator\ResultInterface if ($errors) { return $this->resultFactory->create(Validator\Result\Error::ERROR, [ - 'error' => 'Environment configuration is not valid', - 'suggestion' => implode(PHP_EOL, $errors), + 'error' => 'Environment configuration is not valid. ' . + 'Please correct .magento.env.yaml file with next suggestions:', + 'suggestion' => PHP_EOL . "\t" .implode(PHP_EOL . "\t", $errors) . PHP_EOL, ]); } diff --git a/src/Config/Validator/Deploy/MagentoCloudVariables.php b/src/Config/Validator/Deploy/MagentoCloudVariables.php new file mode 100644 index 0000000000..fcff813a06 --- /dev/null +++ b/src/Config/Validator/Deploy/MagentoCloudVariables.php @@ -0,0 +1,132 @@ +environment = $environment; + $this->resultFactory = $resultFactory; + } + + /** + * Validates variables configured through MAGENTO_CLOUD_VARIABLES. + */ + public function validate(): Validator\ResultInterface + { + $variables = $this->environment->getVariables(); + $errors = $this->validateIntegers($variables); + $errors = array_merge($errors, $this->validateEnableDisableValues($variables)); + + $possibleVerboseValues = ['-v', '-vv', '-vvv', Environment::VAL_ENABLED]; + if (isset($variables[DeployInterface::VAR_VERBOSE_COMMANDS]) + && !in_array($variables[DeployInterface::VAR_VERBOSE_COMMANDS], $possibleVerboseValues, true) + ) { + $errors[] = sprintf( + 'Variable %s has wrong value "%s", please use one of possible values: %s', + DeployInterface::VAR_VERBOSE_COMMANDS, + $variables[DeployInterface::VAR_VERBOSE_COMMANDS], + implode(', ', $possibleVerboseValues) + ); + } + + if ($errors) { + return $this->resultFactory->create(Validator\Result\Error::ERROR, [ + 'error' => 'Environment configuration is not valid', + 'suggestion' => implode(PHP_EOL, $errors), + ]); + } + + return $this->resultFactory->create(Validator\Result\Success::SUCCESS); + } + + /** + * Validates all integer variables. + * + * @param array $variables + * @return array List of errors + */ + private function validateIntegers(array $variables): array + { + $errors = []; + + $intVariables = [ + DeployInterface::VAR_STATIC_CONTENT_THREADS, + DeployInterface::VAR_SCD_COMPRESSION_LEVEL, + DeployInterface::VAR_SCD_THREADS, + ]; + + foreach ($intVariables as $intVarName) { + if (isset($variables[$intVarName]) + && !is_int($variables[$intVarName]) + && !ctype_digit($variables[$intVarName]) + ) { + $errors[] = sprintf( + 'Variable "%s" has wrong value: "%s". Please use only integer values.', + $intVarName, + $variables[$intVarName] + ); + } + } + + return $errors; + } + + /** + * @param array $variables + * @return array + */ + private function validateEnableDisableValues(array $variables): array + { + $errors = []; + $enableDisableVariables = [ + DeployInterface::VAR_CLEAN_STATIC_FILES, + DeployInterface::VAR_STATIC_CONTENT_SYMLINK, + DeployInterface::VAR_UPDATE_URLS, + DeployInterface::VAR_GENERATED_CODE_SYMLINK, + DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT + ]; + + $possibleValues = [Environment::VAL_DISABLED, Environment::VAL_ENABLED]; + foreach ($enableDisableVariables as $varName) { + if (isset($variables[$varName]) && !in_array($variables[$varName], $possibleValues, true)) { + $errors[] = sprintf( + 'Variable "%s" has wrong value: "%s". Please use only %s.', + $varName, + $variables[$varName], + implode(' or ', $possibleValues) + ); + } + } + + return $errors; + } +} diff --git a/src/Config/Validator/Deploy/RawEnvVariable.php b/src/Config/Validator/Deploy/RawEnvVariable.php new file mode 100644 index 0000000000..fdebf733d5 --- /dev/null +++ b/src/Config/Validator/Deploy/RawEnvVariable.php @@ -0,0 +1,56 @@ +resultFactory = $resultFactory; + } + + /** + * Validates variables configured through raw ENV variables. + * + * @return Validator\ResultInterface + */ + public function validate(): Validator\ResultInterface + { + if (isset($_ENV[DeployInterface::VAR_STATIC_CONTENT_THREADS]) + && !ctype_digit($_ENV[DeployInterface::VAR_STATIC_CONTENT_THREADS]) + ) { + return $this->resultFactory->error( + sprintf( + 'The %s variable value "%s" is an invalid value type', + DeployInterface::VAR_STATIC_CONTENT_THREADS, + $_ENV[DeployInterface::VAR_STATIC_CONTENT_THREADS] + ), + 'Use an integer value' + ); + } + + return $this->resultFactory->success(); + } +} diff --git a/src/Config/Validator/Deploy/SessionCredentials.php b/src/Config/Validator/Deploy/SessionCredentials.php new file mode 100644 index 0000000000..eb48ee4b88 --- /dev/null +++ b/src/Config/Validator/Deploy/SessionCredentials.php @@ -0,0 +1,74 @@ +resultFactory = $resultFactory; + $this->sessionConfig = $sessionConfig; + } + + /** + * Validates that session configuration contain required 'save' option. + * If session save into redis then checks that host option presents. + * + * @return Validator\ResultInterface + */ + public function validate(): Validator\ResultInterface + { + $sessionConfig = $this->sessionConfig->get(); + if (empty($sessionConfig)) { + return $this->resultFactory->create(Validator\ResultInterface::SUCCESS); + } + + if (!isset($sessionConfig['save'])) { + return $this->resultFactory->create(Validator\ResultInterface::ERROR, [ + 'error' => 'Missed required parameter \'save\' in session configuration' + ]); + } + + if ($sessionConfig['save'] === 'redis') { + if (!isset($sessionConfig['redis'])) { + return $this->resultFactory->create(Validator\ResultInterface::ERROR, [ + 'error' => 'Missed redis options in session configuration' + ]); + } + + if (!isset($sessionConfig['redis']['host'])) { + return $this->resultFactory->create(Validator\ResultInterface::ERROR, [ + 'error' => 'Missed host option for redis in session configuration' + ]); + } + } + + return $this->resultFactory->create(Validator\ResultInterface::SUCCESS); + } +} diff --git a/src/Config/Validator/Deploy/Variables.php b/src/Config/Validator/Deploy/Variables.php deleted file mode 100644 index 16d6b2fbc3..0000000000 --- a/src/Config/Validator/Deploy/Variables.php +++ /dev/null @@ -1,71 +0,0 @@ -environment = $environment; - $this->schemaValidator = $schema; - $this->resultFactory = $resultFactory; - } - - /** - * @inheritdoc - */ - public function validate(): Validator\ResultInterface - { - $variables = $this->environment->getVariables(); - $errors = []; - - foreach ($variables as $key => $value) { - if ($error = $this->schemaValidator->validate($key, $value)) { - $errors[] = $error; - } - } - - if ($errors) { - return $this->resultFactory->create(Validator\Result\Error::ERROR, [ - 'error' => 'Environment configuration is not valid', - 'suggestion' => implode(PHP_EOL, $errors), - ]); - } - - return $this->resultFactory->create(Validator\Result\Success::SUCCESS); - } -} diff --git a/src/Config/Validator/SchemaValidator.php b/src/Config/Validator/SchemaValidator.php index d8e5dfba0b..11c47d0131 100644 --- a/src/Config/Validator/SchemaValidator.php +++ b/src/Config/Validator/SchemaValidator.php @@ -5,8 +5,7 @@ */ namespace Magento\MagentoCloud\Config\Validator; -use Magento\MagentoCloud\Config\Environment; -use Magento\MagentoCloud\Config\StageConfigInterface; +use Magento\MagentoCloud\Config\Schema; /** * Validates configuration types and values by schema. @@ -16,39 +15,70 @@ class SchemaValidator const SCHEMA_TYPE = 'type'; const SCHEMA_VALUE = 'value'; + const SCHEMA_STAGE = 'stage'; + + /** + * @var Schema + */ + private $schema; + /** - * @var array + * @param Schema $schema */ - private $schema = [ - StageConfigInterface::VAR_VERBOSE_COMMANDS => [ - self::SCHEMA_TYPE => ['string'], - self::SCHEMA_VALUE => ['', '-v', '-vv', '-vvv', Environment::VAL_ENABLED], - ], - ]; + public function __construct(Schema $schema) + { + $this->schema = $schema; + } /** + * Validates configuration item: + * - item exists in configuration schema + * - item configured in correct stage + * - item has correct type (integer, string, etc) + * - item value is correct + * * @param string $key + * @param string $stage * @param mixed $value - * @return string|null + * @return null|string */ - public function validate(string $key, $value) + public function validate(string $key, string $stage, $value) { + $schema = $this->schema->getSchema(); + if (!isset($schema[$key])) { + return sprintf('The %s variable is not allowed in configuration.', $key); + } + $type = gettype($value); - $allowedTypes = $this->schema[$key][self::SCHEMA_TYPE] ?? []; - $allowedValues = $this->schema[$key][self::SCHEMA_VALUE] ?? []; + $allowedTypes = $schema[$key][Schema::SCHEMA_TYPE] ?? []; + $allowedValues = $schema[$key][Schema::SCHEMA_VALUE_VALIDATION] ?? []; + $allowedStages = $schema[$key][Schema::SCHEMA_STAGE] ?? []; if ($allowedTypes && !in_array($type, $allowedTypes)) { return sprintf( - 'Item %s has unexpected type %s. Please use one of next types: %s', + 'The %s variable contains an invalid value of type %s. Use one of the next types: %s.', $key, $type, implode(', ', $allowedTypes) ); } + if (!in_array($stage, $allowedStages)) { + return sprintf( + 'The %s variable is not supposed to be in stage %s. Move it to one of the possible stages: %s.', + $key, + $stage, + implode(', ', $allowedStages) + ); + } + + if (is_callable($allowedValues)) { + return $allowedValues($key, $value); + } + if ($allowedValues && !in_array($value, $allowedValues)) { return sprintf( - 'Item %s has unexpected value %s. Please use one of next values: %s', + 'The %s variable contains an invalid value %s. Use one of the available value options: %s.', $key, $value, implode(', ', array_filter($allowedValues)) diff --git a/src/Test/Unit/Config/GlobalSectionTest.php b/src/Test/Unit/Config/GlobalSectionTest.php index f5e7d4651e..88e562da42 100644 --- a/src/Test/Unit/Config/GlobalSectionTest.php +++ b/src/Test/Unit/Config/GlobalSectionTest.php @@ -6,6 +6,7 @@ namespace Magento\MagentoCloud\Test\Unit\Config; use Magento\MagentoCloud\Config\GlobalSection; +use Magento\MagentoCloud\Config\Schema; use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as Mock; use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; @@ -26,14 +27,29 @@ class GlobalSectionTest extends TestCase */ private $environmentReaderMock; + /** + * @var Schema|Mock + */ + private $schemaMock; + /** * @inheritdoc */ public function setUp() { $this->environmentReaderMock = $this->createMock(EnvironmentReader::class); + $this->schemaMock = $this->createMock(Schema::class); + $this->schemaMock->expects($this->any()) + ->method('getDefaults') + ->with(StageConfigInterface::STAGE_GLOBAL) + ->willReturn([ + StageConfigInterface::VAR_SCD_ON_DEMAND => false, + StageConfigInterface::VAR_SKIP_HTML_MINIFICATION => false, + StageConfigInterface::VAR_DEPLOYED_MAGENTO_VERSION_FROM_GIT => false, + StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS => [], + ]); - $this->config = new GlobalSection($this->environmentReaderMock); + $this->config = new GlobalSection($this->environmentReaderMock, $this->schemaMock); } /** diff --git a/src/Test/Unit/Config/SchemaTest.php b/src/Test/Unit/Config/SchemaTest.php new file mode 100644 index 0000000000..d9fb474ae3 --- /dev/null +++ b/src/Test/Unit/Config/SchemaTest.php @@ -0,0 +1,133 @@ +schema = new Schema(); + } + + public function testGetDefaultsForBuild() + { + $this->assertEquals( + [ + BuildInterface::VAR_SCD_STRATEGY => '', + BuildInterface::VAR_SKIP_SCD => false, + BuildInterface::VAR_SCD_COMPRESSION_LEVEL => 6, + BuildInterface::VAR_SCD_THREADS => 1, + BuildInterface::VAR_SCD_EXCLUDE_THEMES => '', + BuildInterface::VAR_VERBOSE_COMMANDS => '', + BuildInterface::VAR_SCD_MATRIX => [], + ], + $this->schema->getDefaults(StageConfigInterface::STAGE_BUILD) + ); + } + + public function testGetDefaultsForDeploy() + { + $this->assertEquals( + [ + DeployInterface::VAR_SCD_STRATEGY => '', + DeployInterface::VAR_SCD_COMPRESSION_LEVEL => 4, + DeployInterface::VAR_SEARCH_CONFIGURATION => [], + DeployInterface::VAR_QUEUE_CONFIGURATION => [], + DeployInterface::VAR_CACHE_CONFIGURATION => [], + DeployInterface::VAR_SESSION_CONFIGURATION => [], + DeployInterface::VAR_VERBOSE_COMMANDS => '', + DeployInterface::VAR_CRON_CONSUMERS_RUNNER => [], + DeployInterface::VAR_CLEAN_STATIC_FILES => true, + DeployInterface::VAR_STATIC_CONTENT_SYMLINK => true, + DeployInterface::VAR_UPDATE_URLS => true, + DeployInterface::VAR_SKIP_SCD => false, + DeployInterface::VAR_SCD_THREADS => 1, + DeployInterface::VAR_GENERATED_CODE_SYMLINK => true, + DeployInterface::VAR_SCD_EXCLUDE_THEMES => '', + DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION => false, + DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION => false, + DeployInterface::VAR_SCD_MATRIX => [], + ], + $this->schema->getDefaults(StageConfigInterface::STAGE_DEPLOY) + ); + } + + public function testGetDefaultsForPostDeploy() + { + $this->assertEquals( + [ + PostDeployInterface::VAR_WARM_UP_PAGES => [ + 'index.php', + ] + ], + $this->schema->getDefaults(StageConfigInterface::STAGE_POST_DEPLOY) + ); + } + + public function testGetDefaultsForGlobalSection() + { + $this->assertEquals( + [ + StageConfigInterface::VAR_SCD_ON_DEMAND => false, + StageConfigInterface::VAR_SKIP_HTML_MINIFICATION => false, + StageConfigInterface::VAR_DEPLOYED_MAGENTO_VERSION_FROM_GIT => false, + StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS => [], + ], + $this->schema->getDefaults(StageConfigInterface::STAGE_GLOBAL) + ); + } + + public function testGetSchemaItemsExists() + { + $requiredItems = [ + StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, + StageConfigInterface::VAR_SCD_STRATEGY, + StageConfigInterface::VAR_SCD_THREADS, + StageConfigInterface::VAR_SCD_EXCLUDE_THEMES, + StageConfigInterface::VAR_SKIP_SCD, + StageConfigInterface::VAR_VERBOSE_COMMANDS, + StageConfigInterface::VAR_SCD_ON_DEMAND, + StageConfigInterface::VAR_SKIP_HTML_MINIFICATION, + StageConfigInterface::VAR_SCD_MATRIX, + StageConfigInterface::VAR_DEPLOYED_MAGENTO_VERSION_FROM_GIT, + StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS, + DeployInterface::VAR_QUEUE_CONFIGURATION, + DeployInterface::VAR_SEARCH_CONFIGURATION, + DeployInterface::VAR_CACHE_CONFIGURATION, + DeployInterface::VAR_SESSION_CONFIGURATION, + DeployInterface::VAR_CRON_CONSUMERS_RUNNER, + DeployInterface::VAR_CLEAN_STATIC_FILES, + DeployInterface::VAR_STATIC_CONTENT_SYMLINK, + DeployInterface::VAR_UPDATE_URLS, + DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION, + DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION, + DeployInterface::VAR_GENERATED_CODE_SYMLINK, + PostDeployInterface::VAR_WARM_UP_PAGES, + ]; + + foreach ($requiredItems as $item) { + $this->assertArrayHasKey($item, $this->schema->getSchema()); + } + } +} diff --git a/src/Test/Unit/Config/Stage/BuildTest.php b/src/Test/Unit/Config/Stage/BuildTest.php index f922a3959f..b72d08329c 100644 --- a/src/Test/Unit/Config/Stage/BuildTest.php +++ b/src/Test/Unit/Config/Stage/BuildTest.php @@ -5,7 +5,9 @@ */ namespace Magento\MagentoCloud\Test\Unit\Config\Stage; +use Magento\MagentoCloud\Config\Schema; use Magento\MagentoCloud\Config\Stage\Build; +use Magento\MagentoCloud\Config\Stage\BuildInterface; use Magento\MagentoCloud\Config\StageConfigInterface; use PHPUnit\Framework\TestCase; use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; @@ -32,6 +34,11 @@ class BuildTest extends TestCase */ private $buildReaderMock; + /** + * @var Schema|Mock + */ + private $schemaMock; + /** * @inheritdoc */ @@ -39,10 +46,24 @@ protected function setUp() { $this->environmentReaderMock = $this->createMock(EnvironmentReader::class); $this->buildReaderMock = $this->createMock(BuildReader::class); + $this->schemaMock = $this->createMock(Schema::class); + $this->schemaMock->expects($this->any()) + ->method('getDefaults') + ->with(StageConfigInterface::STAGE_BUILD) + ->willReturn([ + BuildInterface::VAR_SCD_STRATEGY => '', + BuildInterface::VAR_SKIP_SCD => false, + BuildInterface::VAR_SCD_COMPRESSION_LEVEL => 6, + BuildInterface::VAR_SCD_THREADS => 1, + BuildInterface::VAR_SCD_EXCLUDE_THEMES => '', + BuildInterface::VAR_VERBOSE_COMMANDS => '', + BuildInterface::VAR_SCD_MATRIX => [], + ]); $this->config = new Build( $this->environmentReaderMock, - $this->buildReaderMock + $this->buildReaderMock, + $this->schemaMock ); } diff --git a/src/Test/Unit/Config/Stage/Deploy/EnvironmentConfigTest.php b/src/Test/Unit/Config/Stage/Deploy/EnvironmentConfigTest.php new file mode 100644 index 0000000000..4d5772a2d9 --- /dev/null +++ b/src/Test/Unit/Config/Stage/Deploy/EnvironmentConfigTest.php @@ -0,0 +1,157 @@ +environmentMock = $this->createMock(Environment::class); + + $this->environmentConfig = new EnvironmentConfig($this->environmentMock); + } + + /** + * @param array $expectedVariables + * @param array $envVariables + * @dataProvider getAllDataProvider + */ + public function testGetAll(array $expectedVariables, array $envVariables) + { + $this->environmentMock->expects($this->any()) + ->method('getVariables') + ->willReturn($envVariables); + + $this->assertSame( + $expectedVariables, + $this->environmentConfig->getAll() + ); + } + + public function getAllDataProvider() + { + return [ + [ + [], + [] + ], + [ + [DeployInterface::VAR_VERBOSE_COMMANDS => '-vvv'], + [DeployInterface::VAR_VERBOSE_COMMANDS => Environment::VAL_ENABLED] + ], + [ + [DeployInterface::VAR_VERBOSE_COMMANDS => '-vv'], + [DeployInterface::VAR_VERBOSE_COMMANDS => '-vv'] + ], + [ + [DeployInterface::VAR_CLEAN_STATIC_FILES => false], + [DeployInterface::VAR_CLEAN_STATIC_FILES => Environment::VAL_DISABLED] + ], + [ + [DeployInterface::VAR_STATIC_CONTENT_SYMLINK => false], + [DeployInterface::VAR_STATIC_CONTENT_SYMLINK => Environment::VAL_DISABLED] + ], + [ + [DeployInterface::VAR_UPDATE_URLS => false], + [DeployInterface::VAR_UPDATE_URLS => Environment::VAL_DISABLED] + ], + [ + [DeployInterface::VAR_GENERATED_CODE_SYMLINK => false], + [DeployInterface::VAR_GENERATED_CODE_SYMLINK => Environment::VAL_DISABLED] + ], + [ + [DeployInterface::VAR_SCD_EXCLUDE_THEMES => 'theme'], + ['STATIC_CONTENT_EXCLUDE_THEMES' => 'theme'] + ], + [ + [DeployInterface::VAR_SCD_THREADS => 3], + [DeployInterface::VAR_STATIC_CONTENT_THREADS => '3'] + ], + [ + [DeployInterface::VAR_SCD_THREADS => 0], + [DeployInterface::VAR_STATIC_CONTENT_THREADS => '0'] + ], + [ + [], + [DeployInterface::VAR_STATIC_CONTENT_THREADS => 'test'] + ], + [ + [DeployInterface::VAR_SKIP_SCD => true], + [DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT => Environment::VAL_DISABLED] + ], + [ + [DeployInterface::VAR_SKIP_SCD => false], + [DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT => 0] + ], + ]; + } + + /** + * @param string $staticContentThreads + * @param array $expectedResult + * @dataProvider getEnvScdDataProvider + */ + public function testGetEnvScd(string $staticContentThreads, array $expectedResult) + { + $this->environmentMock->expects($this->any()) + ->method('getVariables') + ->willReturn([]); + $this->environmentMock->expects($this->once()) + ->method('getEnv') + ->with(DeployInterface::VAR_STATIC_CONTENT_THREADS) + ->willReturn($staticContentThreads); + + $this->assertSame($expectedResult, $this->environmentConfig->getAll()); + } + + /** + * @return array + */ + public function getEnvScdDataProvider(): array + { + return [ + [ + '5', + ['SCD_THREADS' => 5] + ], + [ + '0', + ['SCD_THREADS' => 0] + ], + [ + 'test', + [] + ], + [ + '', + [] + ], + [ + false, + [] + ] + ]; + } +} diff --git a/src/Test/Unit/Config/Stage/DeployTest.php b/src/Test/Unit/Config/Stage/DeployTest.php index 871f232c32..f45e57e042 100644 --- a/src/Test/Unit/Config/Stage/DeployTest.php +++ b/src/Test/Unit/Config/Stage/DeployTest.php @@ -5,10 +5,14 @@ */ namespace Magento\MagentoCloud\Test\Unit\Config\Stage; -use Magento\MagentoCloud\Config\Environment as EnvironmentConfig; +use Magento\MagentoCloud\Config\Environment; use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; +use Magento\MagentoCloud\Config\Schema; use Magento\MagentoCloud\Config\Stage\Deploy; +use Magento\MagentoCloud\Config\Stage\Deploy\EnvironmentConfig; +use Magento\MagentoCloud\Config\Stage\DeployInterface; use Magento\MagentoCloud\Config\StageConfigInterface; +use Magento\MagentoCloud\Filesystem\FileSystemException; use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as Mock; @@ -32,23 +36,54 @@ class DeployTest extends TestCase */ private $environmentConfigMock; + /** + * @var Environment|Mock + */ + private $environmentMock; + + /** + * @var Schema|Mock + */ + private $schemaMock; + /** * @inheritdoc */ protected function setUp() { + $this->environmentMock = $this->createMock(Environment::class); $this->environmentReaderMock = $this->createMock(EnvironmentReader::class); - $this->environmentConfigMock = $this->getMockBuilder(EnvironmentConfig::class) - ->setMethods(array('getVariables')) - ->setConstructorArgs([ - $this->createMock('\Psr\Log\LoggerInterface'), - $this->createMock('\Magento\MagentoCloud\Filesystem\Driver\File'), - $this->createMock('\Magento\MagentoCloud\Filesystem\DirectoryList'), - $this->createMock('\Magento\MagentoCloud\Filesystem\Flag\Manager'), - ])->getMock(); + $this->environmentConfigMock = $this->createMock(EnvironmentConfig::class); + $this->schemaMock = $this->createMock(Schema::class); + $this->schemaMock->expects($this->any()) + ->method('getDefaults') + ->with(StageConfigInterface::STAGE_DEPLOY) + ->willReturn([ + DeployInterface::VAR_SCD_STRATEGY => '', + DeployInterface::VAR_SCD_COMPRESSION_LEVEL => 4, + DeployInterface::VAR_SEARCH_CONFIGURATION => [], + DeployInterface::VAR_QUEUE_CONFIGURATION => [], + DeployInterface::VAR_CACHE_CONFIGURATION => [], + DeployInterface::VAR_SESSION_CONFIGURATION => [], + DeployInterface::VAR_VERBOSE_COMMANDS => '', + DeployInterface::VAR_CRON_CONSUMERS_RUNNER => [], + DeployInterface::VAR_CLEAN_STATIC_FILES => true, + DeployInterface::VAR_STATIC_CONTENT_SYMLINK => true, + DeployInterface::VAR_UPDATE_URLS => true, + DeployInterface::VAR_SKIP_SCD => false, + DeployInterface::VAR_SCD_THREADS => 1, + DeployInterface::VAR_GENERATED_CODE_SYMLINK => true, + DeployInterface::VAR_SCD_EXCLUDE_THEMES => '', + DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION => false, + DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION => false, + DeployInterface::VAR_SCD_MATRIX => [], + ]); + $this->config = new Deploy( + $this->environmentMock, $this->environmentReaderMock, - $this->environmentConfigMock + $this->environmentConfigMock, + $this->schemaMock ); } @@ -65,12 +100,25 @@ public function testGet(string $name, array $envConfig, array $envVarConfig, $ex ->method('read') ->willReturn([Deploy::SECTION_STAGE => $envConfig]); $this->environmentConfigMock->expects($this->any()) - ->method('getVariables') + ->method('getAll') ->willReturn($envVarConfig); $this->assertSame($expectedValue, $this->config->get($name)); } + /** + * @expectedExceptionMessage File system error + * @expectedException \RuntimeException + */ + public function testGetWithFileSystemException() + { + $this->environmentReaderMock->expects($this->once()) + ->method('read') + ->willThrowException(new FileSystemException('File system error')); + + $this->config->get(Deploy::VAR_SCD_STRATEGY); + } + /** * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -152,7 +200,7 @@ public function getDataProvider(): array Deploy::VAR_UPDATE_URLS, [], [ - Deploy::VAR_UPDATE_URLS => EnvironmentConfig::VAL_DISABLED, + Deploy::VAR_UPDATE_URLS => false, ], false, ], @@ -160,9 +208,9 @@ public function getDataProvider(): array Deploy::VAR_SKIP_SCD, [], [ - 'DO_DEPLOY_STATIC_CONTENT' => EnvironmentConfig::VAL_DISABLED, + Deploy::VAR_SKIP_SCD => false, ], - true, + false, ], 'do deploy scd' => [ Deploy::VAR_SKIP_SCD, @@ -174,7 +222,7 @@ public function getDataProvider(): array Deploy::VAR_VERBOSE_COMMANDS, [], [ - Deploy::VAR_VERBOSE_COMMANDS => EnvironmentConfig::VAL_ENABLED, + Deploy::VAR_VERBOSE_COMMANDS => '-vvv', ], '-vvv', ], @@ -190,14 +238,6 @@ public function getDataProvider(): array [], 1, ], - 'threads deprecated' => [ - Deploy::VAR_SCD_THREADS, - [], - [ - 'STATIC_CONTENT_THREADS' => 4, - ], - 4, - ], 'scd strategy default' => [ Deploy::VAR_SCD_STRATEGY, [], @@ -206,12 +246,13 @@ public function getDataProvider(): array ], 'exclude themes deprecated' => [ Deploy::VAR_SCD_EXCLUDE_THEMES, - [], [ - 'STATIC_CONTENT_EXCLUDE_THEMES' => 'some theme', + Deploy::VAR_SCD_EXCLUDE_THEMES => 'some theme' + ], + [ Deploy::VAR_SCD_EXCLUDE_THEMES => 'some theme 2', ], - 'some theme', + 'some theme 2', ], 'exclude themes' => [ Deploy::VAR_SCD_EXCLUDE_THEMES, @@ -254,9 +295,9 @@ public function getDataProvider(): array /** * @param string $name - * @param array $envConfig - * @param array $envVarConfig - * @param array $rawEnv + * @param array $envConfig Deploy config from .magento.env.yaml + * @param array $envVarConfig Cloud variables configuration + * @param string $cloudMode * @param int $expectedValue * @dataProvider getDeprecatedScdThreadsDataProvider */ @@ -264,16 +305,19 @@ public function testGetDeprecatedScdThreads( string $name, array $envConfig, array $envVarConfig, - array $rawEnv, + string $cloudMode, int $expectedValue ) { $this->environmentReaderMock->expects($this->any()) ->method('read') ->willReturn([Deploy::SECTION_STAGE => $envConfig]); - $this->environmentConfigMock->expects($this->any()) - ->method('getVariables') + $this->environmentConfigMock->expects($this->once()) + ->method('getAll') ->willReturn($envVarConfig); - $_ENV = $rawEnv; + $this->environmentMock->expects($this->any()) + ->method('getEnv') + ->with('MAGENTO_CLOUD_MODE') + ->willReturn($cloudMode); $this->assertSame($expectedValue, $this->config->get($name)); } @@ -289,82 +333,32 @@ public function getDeprecatedScdThreadsDataProvider(): array Deploy::VAR_SCD_THREADS, [], [ - 'STATIC_CONTENT_THREADS' => 4, - ], - [], - 4, - ], - 'threads ENV raw' => [ - Deploy::VAR_SCD_THREADS, - [], - [ - - ], - [ - 'STATIC_CONTENT_THREADS' => 5, - ], - 5, - ], - 'threads ENV raw and magento cloud variable' => [ - Deploy::VAR_SCD_THREADS, - [], - [ - 'STATIC_CONTENT_THREADS' => 4, - ], - [ - 'STATIC_CONTENT_THREADS' => 3, + Deploy::VAR_SCD_THREADS => 4, ], + 'develop', 4, ], 'threads mode none' => [ Deploy::VAR_SCD_THREADS, [], [], - [ - 'MAGENTO_CLOUD_MODE' => '', - ], + '', 1, ], 'threads mode enterprise' => [ Deploy::VAR_SCD_THREADS, [], [], - [ - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, - ], + Environment::CLOUD_MODE_ENTERPRISE, 3, ], - 'threads mode enterprise and ENV raw' => [ - Deploy::VAR_SCD_THREADS, - [], - [], - [ - 'STATIC_CONTENT_THREADS' => 5, - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, - ], - 5, - ], - 'threads mode enterprise and ENV raw and magento cloud variable' => [ - Deploy::VAR_SCD_THREADS, - [], - [ - 'STATIC_CONTENT_THREADS' => 4 - ], - [ - 'STATIC_CONTENT_THREADS' => 5, - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, - ], - 4, - ], 'threads mode enterprise and magento cloud variable' => [ Deploy::VAR_SCD_THREADS, [], [ - 'STATIC_CONTENT_THREADS' => 5, - ], - [ - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, + Deploy::VAR_SCD_THREADS => 5, ], + Environment::CLOUD_MODE_ENTERPRISE, 5, ], 'mode enterprise with global and deploy scd_threads in .magento.env.yaml' => [ @@ -378,9 +372,7 @@ public function getDeprecatedScdThreadsDataProvider(): array ], ], [], - [ - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, - ], + Environment::CLOUD_MODE_ENTERPRISE, 4, ], 'threads mode enterprise with global scd_threads in .magento.env.yaml' => [ @@ -391,12 +383,10 @@ public function getDeprecatedScdThreadsDataProvider(): array ], ], [], - [ - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, - ], + Environment::CLOUD_MODE_ENTERPRISE, 5, ], - 'threads mode enterprise with global and deploy scd_threads in .magento.env.yaml and ENV variable' => [ + 'threads mode enterprise with global and deploy scd_threads in .magento.env.yaml and cloud variable' => [ Deploy::VAR_SCD_THREADS, [ StageConfigInterface::STAGE_GLOBAL => [ @@ -406,33 +396,12 @@ public function getDeprecatedScdThreadsDataProvider(): array StageConfigInterface::VAR_SCD_THREADS => 4 ], ], - [], [ - 'STATIC_CONTENT_THREADS' => 7, - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, + Deploy::VAR_SCD_THREADS => 7, ], + Environment::CLOUD_MODE_ENTERPRISE, 7, ], - 'threads mode enterprise with global and deploy scd_threads in .magento.env.yaml ' . - 'and magento cloud variable and ENV variable' => [ - Deploy::VAR_SCD_THREADS, - [ - StageConfigInterface::STAGE_GLOBAL => [ - StageConfigInterface::VAR_SCD_THREADS => 5 - ], - StageConfigInterface::STAGE_DEPLOY => [ - StageConfigInterface::VAR_SCD_THREADS => 4 - ], - ], - [ - 'STATIC_CONTENT_THREADS' => 6, - ], - [ - 'STATIC_CONTENT_THREADS' => 7, - 'MAGENTO_CLOUD_MODE' => EnvironmentConfig::CLOUD_MODE_ENTERPRISE, - ], - 6, - ], ]; } @@ -446,7 +415,7 @@ public function testNotExists() ->method('read') ->willReturn([]); $this->environmentConfigMock->expects($this->any()) - ->method('getVariables') + ->method('getAll') ->willReturn([]); $this->config->get('NOT_EXISTS_VALUE'); diff --git a/src/Test/Unit/Config/Stage/PostDeployTest.php b/src/Test/Unit/Config/Stage/PostDeployTest.php index b6b033e650..9c9d1c383a 100644 --- a/src/Test/Unit/Config/Stage/PostDeployTest.php +++ b/src/Test/Unit/Config/Stage/PostDeployTest.php @@ -5,7 +5,10 @@ */ namespace Magento\MagentoCloud\Test\Unit\Config\Stage; +use Magento\MagentoCloud\Config\Schema; use Magento\MagentoCloud\Config\Stage\PostDeploy; +use Magento\MagentoCloud\Config\Stage\PostDeployInterface; +use Magento\MagentoCloud\Config\StageConfigInterface; use PHPUnit\Framework\TestCase; use Magento\MagentoCloud\Config\Environment\Reader as EnvironmentReader; use PHPUnit_Framework_MockObject_MockObject as Mock; @@ -25,15 +28,28 @@ class PostDeployTest extends TestCase */ private $environmentReaderMock; + /** + * @var Schema|Mock + */ + private $schemaMock; + /** * @inheritdoc */ protected function setUp() { $this->environmentReaderMock = $this->createMock(EnvironmentReader::class); + $this->schemaMock = $this->createMock(Schema::class); + $this->schemaMock->expects($this->any()) + ->method('getDefaults') + ->with(StageConfigInterface::STAGE_POST_DEPLOY) + ->willReturn([ + PostDeployInterface::VAR_WARM_UP_PAGES => ['index.php'] + ]); $this->config = new PostDeploy( - $this->environmentReaderMock + $this->environmentReaderMock, + $this->schemaMock ); } diff --git a/src/Test/Unit/Config/Validator/Build/BuildOptionsIniTest.php b/src/Test/Unit/Config/Validator/Build/BuildOptionsIniTest.php new file mode 100644 index 0000000000..a125d65aec --- /dev/null +++ b/src/Test/Unit/Config/Validator/Build/BuildOptionsIniTest.php @@ -0,0 +1,103 @@ +resultFactoryMock = $this->createMock(ResultFactory::class); + $this->schemaValidatorMock = $this->createMock(SchemaValidator::class); + $this->buildReaderMock = $this->createMock(BuildReader::class); + + $this->validator = new BuildOptionsIni( + $this->resultFactoryMock, + $this->schemaValidatorMock, + $this->buildReaderMock + ); + } + + public function testValidateWithError() + { + $this->buildReaderMock->expects($this->once()) + ->method('read') + ->willReturn([ + 'scd_strategy' => 'quik', + 'scd_threads' => 'two', + 'exclude_themes' => 'some_theme', + 'some_wrong_option' => 'someValue' + ]); + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultInterface::ERROR, [ + 'error' => 'The build_options.ini file contains an unexpected value', + 'suggestion' => 'scd_strategy error1' . PHP_EOL . + 'scd_threads error2' . PHP_EOL . + 'exclude_themes error3' . PHP_EOL . + 'Option some_wrong_option is not allowed' + ]); + $this->schemaValidatorMock->expects($this->exactly(3)) + ->method('validate') + ->withConsecutive( + [StageConfigInterface::VAR_SCD_STRATEGY, StageConfigInterface::STAGE_BUILD, 'quik'], + [StageConfigInterface::VAR_SCD_THREADS, StageConfigInterface::STAGE_BUILD, 'two'], + [StageConfigInterface::VAR_SCD_EXCLUDE_THEMES, StageConfigInterface::STAGE_BUILD, 'some_theme'] + )->willReturnOnConsecutiveCalls( + StageConfigInterface::VAR_SCD_STRATEGY . ' error1', + StageConfigInterface::VAR_SCD_THREADS . ' error2', + StageConfigInterface::VAR_SCD_EXCLUDE_THEMES . ' error3' + ); + + $this->validator->validate(); + } + + public function testValidateEmptyBuildOptionsIni() + { + $this->buildReaderMock->expects($this->once()) + ->method('read') + ->willReturn([]); + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with(ResultInterface::SUCCESS); + + $this->validator->validate(); + } +} diff --git a/src/Test/Unit/Config/Validator/Build/DeprecatedBuildOptionsIniTest.php b/src/Test/Unit/Config/Validator/Build/DeprecatedBuildOptionsIniTest.php new file mode 100644 index 0000000000..90a8277012 --- /dev/null +++ b/src/Test/Unit/Config/Validator/Build/DeprecatedBuildOptionsIniTest.php @@ -0,0 +1,97 @@ +fileMock = $this->createMock(File::class); + $this->fileListMock = $this->createMock(FileList::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->validator = new DeprecatedBuildOptionsIni( + $this->fileMock, + $this->fileListMock, + $this->resultFactoryMock + ); + } + + public function testValidateSuccess() + { + $path = '/path/to/build_option.ini'; + + $this->fileListMock->expects($this->once()) + ->method('getBuildConfig') + ->willReturn($path); + $this->fileMock->expects($this->once()) + ->method('isExists') + ->with($path) + ->willReturn(false); + $this->resultFactoryMock->expects($this->once()) + ->method('success'); + + $this->validator->validate(); + } + + public function testValidateError() + { + $path = '/path/to/build_options.ini'; + + $this->fileListMock->expects($this->exactly(2)) + ->method('getBuildConfig') + ->willReturn($path); + $this->fileListMock->expects($this->once()) + ->method('getEnvConfig') + ->willReturn('/path/to/magento.env.yaml'); + $this->fileMock->expects($this->once()) + ->method('isExists') + ->with($path) + ->willReturn(true); + $this->resultFactoryMock->expects($this->once()) + ->method('error') + ->with( + 'The build_options.ini file has been deprecated', + 'Modify your configuration to use the magento.env.yaml file' + ); + + $this->validator->validate(); + } +} diff --git a/src/Test/Unit/Config/Validator/Deploy/MagentoCloudVariablesTest.php b/src/Test/Unit/Config/Validator/Deploy/MagentoCloudVariablesTest.php new file mode 100644 index 0000000000..f3135bd51c --- /dev/null +++ b/src/Test/Unit/Config/Validator/Deploy/MagentoCloudVariablesTest.php @@ -0,0 +1,194 @@ +environmentMock = $this->createMock(Environment::class); + $this->resultFactoryMock = $this->createMock(Validator\ResultFactory::class); + + $this->validator = new MagentoCloudVariables( + $this->environmentMock, + $this->resultFactoryMock + ); + } + + /** + * @param array $magentoCloudVariables + * @param string $expectedResultType + * @param string|null $suggestionMessage + * @dataProvider validateDataProvider + */ + public function testValidate( + array $magentoCloudVariables, + string $expectedResultType, + string $suggestionMessage = null + ) { + $this->environmentMock->expects($this->once()) + ->method('getVariables') + ->willReturn($magentoCloudVariables); + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with($expectedResultType, $suggestionMessage ? [ + 'error' => 'Environment configuration is not valid', + 'suggestion' => $suggestionMessage + ] : $this->anything()); + + $this->validator->validate(); + } + + /** + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function validateDataProvider(): array + { + return [ + [ + [DeployInterface::VAR_STATIC_CONTENT_THREADS => '3'], + ResultInterface::SUCCESS + ], + [ + [DeployInterface::VAR_STATIC_CONTENT_THREADS => 3], + ResultInterface::SUCCESS + ], + [ + [DeployInterface::VAR_STATIC_CONTENT_THREADS => '3a'], + ResultInterface::ERROR + ], + [ + [DeployInterface::VAR_SCD_COMPRESSION_LEVEL => '3'], + ResultInterface::SUCCESS + ], + [ + [DeployInterface::VAR_SCD_COMPRESSION_LEVEL => '3a'], + ResultInterface::ERROR, + 'Variable "SCD_COMPRESSION_LEVEL" has wrong value: "3a". Please use only integer values.' + ], + [ + [DeployInterface::VAR_SCD_THREADS => '3'], + ResultInterface::SUCCESS + ], + [ + [DeployInterface::VAR_SCD_THREADS => 3], + ResultInterface::SUCCESS + ], + [ + [DeployInterface::VAR_SCD_THREADS => '3a'], + ResultInterface::ERROR, + 'Variable "SCD_THREADS" has wrong value: "3a". Please use only integer values.' + ], + [ + [DeployInterface::VAR_VERBOSE_COMMANDS => '1'], + ResultInterface::ERROR, + 'Variable VERBOSE_COMMANDS has wrong value "1", please use one of possible values: ' . + '-v, -vv, -vvv, enabled' + ], + [ + [DeployInterface::VAR_VERBOSE_COMMANDS => 'true'], + ResultInterface::ERROR, + 'Variable VERBOSE_COMMANDS has wrong value "true", please use one of possible values: ' . + '-v, -vv, -vvv, enabled' + ], + [ + [DeployInterface::VAR_VERBOSE_COMMANDS => '-v'], + ResultInterface::SUCCESS, + ], + [ + [DeployInterface::VAR_VERBOSE_COMMANDS => 'enabled'], + ResultInterface::SUCCESS, + ], + [ + [DeployInterface::VAR_CLEAN_STATIC_FILES => '1'], + ResultInterface::ERROR, + 'Variable "CLEAN_STATIC_FILES" has wrong value: "1". Please use only disabled or enabled.' + ], + [ + [DeployInterface::VAR_STATIC_CONTENT_SYMLINK => '1'], + ResultInterface::ERROR, + 'Variable "STATIC_CONTENT_SYMLINK" has wrong value: "1". Please use only disabled or enabled.' + ], + [ + [DeployInterface::VAR_UPDATE_URLS => '1'], + ResultInterface::ERROR, + 'Variable "UPDATE_URLS" has wrong value: "1". Please use only disabled or enabled.' + ], + [ + [DeployInterface::VAR_GENERATED_CODE_SYMLINK => '1'], + ResultInterface::ERROR, + 'Variable "GENERATED_CODE_SYMLINK" has wrong value: "1". Please use only disabled or enabled.' + ], + [ + [DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT => '1'], + ResultInterface::ERROR, + 'Variable "DO_DEPLOY_STATIC_CONTENT" has wrong value: "1". Please use only disabled or enabled.' + ], + [ + [DeployInterface::VAR_CLEAN_STATIC_FILES => 'enabled'], + ResultInterface::SUCCESS, + ], + [ + [DeployInterface::VAR_STATIC_CONTENT_SYMLINK => 'enabled'], + ResultInterface::SUCCESS, + ], + [ + [DeployInterface::VAR_UPDATE_URLS => 'enabled'], + ResultInterface::SUCCESS, + ], + [ + [DeployInterface::VAR_GENERATED_CODE_SYMLINK => 'enabled'], + ResultInterface::SUCCESS, + ], + [ + [DeployInterface::VAR_DO_DEPLOY_STATIC_CONTENT => 'enabled'], + ResultInterface::SUCCESS, + ], + [ + [ + DeployInterface::VAR_CLEAN_STATIC_FILES => '1', + DeployInterface::VAR_SCD_COMPRESSION_LEVEL => '3a', + DeployInterface::VAR_VERBOSE_COMMANDS => '1' + ], + ResultInterface::ERROR, + 'Variable "SCD_COMPRESSION_LEVEL" has wrong value: "3a". Please use only integer values.' . PHP_EOL . + 'Variable "CLEAN_STATIC_FILES" has wrong value: "1". Please use only disabled or enabled.' . PHP_EOL . + 'Variable VERBOSE_COMMANDS has wrong value "1", ' . + 'please use one of possible values: -v, -vv, -vvv, enabled' + ], + ]; + } +} diff --git a/src/Test/Unit/Config/Validator/Deploy/RawEnvVariableTest.php b/src/Test/Unit/Config/Validator/Deploy/RawEnvVariableTest.php new file mode 100644 index 0000000000..bc275d7e24 --- /dev/null +++ b/src/Test/Unit/Config/Validator/Deploy/RawEnvVariableTest.php @@ -0,0 +1,92 @@ +resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->validator = new RawEnvVariable($this->resultFactoryMock); + } + + /** + * @dataProvider executeDataProvider + * @param string $scdThreadsValue + * @param string $expectedResultMethodName + */ + public function testExecute(string $scdThreadsValue, string $expectedResultMethodName) + { + $_ENV[DeployInterface::VAR_STATIC_CONTENT_THREADS] = $scdThreadsValue; + + $this->resultFactoryMock->expects($this->once()) + ->method($expectedResultMethodName); + + $this->validator->validate(); + } + + /** + * @return array + */ + public function executeDataProvider(): array + { + return [ + [ + '3', + ResultInterface::SUCCESS + ], + [ + '3123', + ResultInterface::SUCCESS + ], + [ + '-2', + ResultInterface::ERROR + ], + [ + '3a', + ResultInterface::ERROR + ], + [ + 'two', + ResultInterface::ERROR + ], + [ + '', + ResultInterface::ERROR + ] + ]; + } + + public function tearDown() + { + $_ENV = []; + } +} diff --git a/src/Test/Unit/Config/Validator/Deploy/SessionCredentialsTest.php b/src/Test/Unit/Config/Validator/Deploy/SessionCredentialsTest.php new file mode 100644 index 0000000000..e5a38abff3 --- /dev/null +++ b/src/Test/Unit/Config/Validator/Deploy/SessionCredentialsTest.php @@ -0,0 +1,88 @@ +sessionConfigMock = $this->createMock(Config::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->sessionCredentials = new SessionCredentials( + $this->resultFactoryMock, + $this->sessionConfigMock + ); + } + + /** + * @param array $sessionConfig + * @param string $expectedResultType + * @param string|null $expectedErrorMessage + * @dataProvider validateDataProvider + */ + public function testValidate(array $sessionConfig, string $expectedResultType, string $expectedErrorMessage = null) + { + $this->sessionConfigMock->expects($this->once()) + ->method('get') + ->willReturn($sessionConfig); + $this->resultFactoryMock->expects($this->once()) + ->method('create') + ->with($expectedResultType, $expectedErrorMessage ? ['error' => $expectedErrorMessage] : $this->anything()); + + $this->sessionCredentials->validate(); + } + + public function validateDataProvider() + { + return [ + [ + [], + ResultInterface::SUCCESS + ], + [ + ['some' => 'option'], + ResultInterface::ERROR, + 'Missed required parameter \'save\' in session configuration' + ], + [ + ['save' => 'redis'], + ResultInterface::ERROR, + 'Missed redis options in session configuration' + ], + [ + ['save' => 'redis', 'redis' => []], + ResultInterface::ERROR, + 'Missed host option for redis in session configuration' + ] + ]; + } +} diff --git a/src/Test/Unit/Config/Validator/Deploy/VariablesTest.php b/src/Test/Unit/Config/Validator/Deploy/VariablesTest.php deleted file mode 100644 index 109fec093d..0000000000 --- a/src/Test/Unit/Config/Validator/Deploy/VariablesTest.php +++ /dev/null @@ -1,92 +0,0 @@ -environmentMock = $this->createMock(Environment::class); - $this->resultFactoryMock = $this->createMock(Validator\ResultFactory::class); - $this->schemaValidatorMock = $this->createMock(Validator\SchemaValidator::class); - - $this->validator = new Variables( - $this->environmentMock, - $this->schemaValidatorMock, - $this->resultFactoryMock - ); - } - - public function testValidate() - { - $this->environmentMock->expects($this->once()) - ->method('getVariables') - ->willReturn([ - StageConfigInterface::VAR_VERBOSE_COMMANDS => '-v', - ]); - $this->schemaValidatorMock->expects($this->once()) - ->method('validate') - ->willReturn(null); - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(Validator\Result\Success::SUCCESS) - ->willReturn(new Validator\Result\Success()); - - $this->assertInstanceOf(Validator\Result\Success::class, $this->validator->validate()); - } - - public function testValidateWithError() - { - $this->environmentMock->expects($this->once()) - ->method('getVariables') - ->willReturn([ - StageConfigInterface::VAR_VERBOSE_COMMANDS => 'error', - ]); - $this->schemaValidatorMock->expects($this->once()) - ->method('validate') - ->willReturn('Some error'); - $this->resultFactoryMock->expects($this->once()) - ->method('create') - ->with(Validator\Result\Error::ERROR) - ->willReturn(new Validator\Result\Error('Some error')); - - $this->assertInstanceOf(Validator\Result\Error::class, $this->validator->validate()); - } -} diff --git a/src/Test/Unit/Config/Validator/SchemaValidatorTest.php b/src/Test/Unit/Config/Validator/SchemaValidatorTest.php index 7fbe67c636..dbb03a5a0a 100644 --- a/src/Test/Unit/Config/Validator/SchemaValidatorTest.php +++ b/src/Test/Unit/Config/Validator/SchemaValidatorTest.php @@ -5,6 +5,8 @@ */ namespace Magento\MagentoCloud\Test\Unit\Config\Validator; +use Magento\MagentoCloud\Config\Schema; +use Magento\MagentoCloud\Config\Stage\DeployInterface; use Magento\MagentoCloud\Config\StageConfigInterface; use Magento\MagentoCloud\Config\Validator\SchemaValidator; use PHPUnit\Framework\TestCase; @@ -24,42 +26,221 @@ class SchemaValidatorTest extends TestCase */ protected function setUp() { - $this->validator = new SchemaValidator(); + $this->validator = new SchemaValidator(new Schema()); } /** * @param string $key * @param $value * @param $expected + * @param string $stage * @dataProvider validateDataProvider */ - public function testValidate(string $key, $value, $expected) + public function testValidate(string $key, $value, $expected, string $stage = StageConfigInterface::STAGE_DEPLOY) { $this->assertSame( $expected, - $this->validator->validate($key, $value) + $this->validator->validate($key, $stage, $value) ); } /** * @return array + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function validateDataProvider(): array { return [ + ['keyNotExist', 'someValue', 'The keyNotExist variable is not allowed in configuration.'], [StageConfigInterface::VAR_VERBOSE_COMMANDS, '-v', null], [StageConfigInterface::VAR_VERBOSE_COMMANDS, '-vv', null], [StageConfigInterface::VAR_VERBOSE_COMMANDS, '-vvv', null], [StageConfigInterface::VAR_VERBOSE_COMMANDS, '', null], + [StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, 0, null], + [StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, 9, null], + [StageConfigInterface::VAR_SCD_STRATEGY, 'quick', null], + [StageConfigInterface::VAR_SCD_STRATEGY, 'compact', null], + [StageConfigInterface::VAR_SCD_STRATEGY, 'standard', null], + [StageConfigInterface::VAR_SCD_THREADS, 3, null], + [StageConfigInterface::VAR_SCD_EXCLUDE_THEMES, 'someTheme', null], + [StageConfigInterface::VAR_SKIP_SCD, true, null], + [StageConfigInterface::VAR_SKIP_SCD, false, null], + [StageConfigInterface::VAR_SKIP_HTML_MINIFICATION, true, null, StageConfigInterface::STAGE_GLOBAL], + [StageConfigInterface::VAR_SKIP_HTML_MINIFICATION, false, null, StageConfigInterface::STAGE_GLOBAL], + [StageConfigInterface::VAR_SCD_ON_DEMAND, true, null, StageConfigInterface::STAGE_GLOBAL], + [StageConfigInterface::VAR_SCD_ON_DEMAND, false, null, StageConfigInterface::STAGE_GLOBAL], + [ + StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS, + ['someOptions' => 'someValue'], + null, + StageConfigInterface::STAGE_GLOBAL + ], + [DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION, true, null], + [DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION, false, null], + [DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION, true, null], + [DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION, false, null], + [DeployInterface::VAR_UPDATE_URLS, true, null], + [DeployInterface::VAR_UPDATE_URLS, false, null], + [DeployInterface::VAR_STATIC_CONTENT_SYMLINK, true, null], + [DeployInterface::VAR_STATIC_CONTENT_SYMLINK, false, null], + [DeployInterface::VAR_CLEAN_STATIC_FILES, true, null], + [DeployInterface::VAR_CLEAN_STATIC_FILES, false, null], + [DeployInterface::VAR_SEARCH_CONFIGURATION, ['someOptions' => 'someValue'], null], + [DeployInterface::VAR_QUEUE_CONFIGURATION, ['someOptions' => 'someValue'], null], + [DeployInterface::VAR_SESSION_CONFIGURATION, ['someOptions' => 'someValue'], null], + [DeployInterface::VAR_CRON_CONSUMERS_RUNNER, ['someOptions' => 'someValue'], null], [ StageConfigInterface::VAR_VERBOSE_COMMANDS, 1, - 'Item VERBOSE_COMMANDS has unexpected type integer. Please use one of next types: string', + 'The VERBOSE_COMMANDS variable contains an invalid value of type integer. ' . + 'Use one of the next types: string.', ], [ StageConfigInterface::VAR_VERBOSE_COMMANDS, '1', - 'Item VERBOSE_COMMANDS has unexpected value 1. Please use one of next values: -v, -vv, -vvv, enabled', + 'The VERBOSE_COMMANDS variable contains an invalid value 1. ' . + 'Use one of the available value options: -v, -vv, -vvv.', + ], + [StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, 0, null], + [ + StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, + 10, + 'The SCD_COMPRESSION_LEVEL variable contains an invalid value of type string. ' . + 'Use an integer value from 0 to 9.' + ], + [ + StageConfigInterface::VAR_SCD_COMPRESSION_LEVEL, + '1', + 'The SCD_COMPRESSION_LEVEL variable contains an invalid value of type string. '. + 'Use one of the next types: integer.', + ], + [ + StageConfigInterface::VAR_SCD_STRATEGY, + 12, + 'The SCD_STRATEGY variable contains an invalid value of type integer. ' . + 'Use one of the next types: string.', + ], + [ + StageConfigInterface::VAR_SCD_STRATEGY, + 'quickk', + 'The SCD_STRATEGY variable contains an invalid value quickk. ' . + 'Use one of the available value options: compact, quick, standard.', + ], + [ + StageConfigInterface::VAR_SCD_STRATEGY, + 'standart', + 'The SCD_STRATEGY variable contains an invalid value standart. ' . + 'Use one of the available value options: compact, quick, standard.' + ], + [ + StageConfigInterface::VAR_SCD_THREADS, + 'test', + 'The SCD_THREADS variable contains an invalid value of type string. Use one of the next types: integer.' + ], + [ + StageConfigInterface::VAR_SCD_EXCLUDE_THEMES, + 123, + 'The SCD_EXCLUDE_THEMES variable contains an invalid value of type integer. ' . + 'Use one of the next types: string.' + ], + [ + StageConfigInterface::VAR_SKIP_SCD, + 0, + 'The SKIP_SCD variable contains an invalid value of type integer. Use one of the next types: boolean.' + ], + [ + StageConfigInterface::VAR_SKIP_SCD, + 'enable', + 'The SKIP_SCD variable contains an invalid value of type string. Use one of the next types: boolean.' + ], + [ + StageConfigInterface::VAR_SKIP_HTML_MINIFICATION, + 0, + 'The SKIP_HTML_MINIFICATION variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.', + StageConfigInterface::STAGE_GLOBAL, + ], + [ + StageConfigInterface::VAR_SKIP_HTML_MINIFICATION, + true, + 'The SKIP_HTML_MINIFICATION variable is not supposed to be in stage deploy. ' . + 'Move it to one of the possible stages: global.', + StageConfigInterface::STAGE_DEPLOY, + ], + [ + StageConfigInterface::VAR_SCD_ON_DEMAND, + 0, + 'The SCD_ON_DEMAND variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.', + StageConfigInterface::STAGE_GLOBAL + ], + [ + StageConfigInterface::VAR_DEPLOY_FROM_GIT_OPTIONS, + 'someOption', + 'The DEPLOY_FROM_GIT_OPTIONS variable contains an invalid value of type string. ' . + 'Use one of the next types: array.' + ], + [ + DeployInterface::VAR_REDIS_USE_SLAVE_CONNECTION, + 0, + 'The REDIS_USE_SLAVE_CONNECTION variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.' + ], + [ + DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION, + 0, + 'The MYSQL_USE_SLAVE_CONNECTION variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.' + ], + [ + DeployInterface::VAR_MYSQL_USE_SLAVE_CONNECTION, + true, + 'The MYSQL_USE_SLAVE_CONNECTION variable is not supposed to be in stage build. ' . + 'Move it to one of the possible stages: global, deploy.', + StageConfigInterface::STAGE_BUILD + ], + [ + DeployInterface::VAR_UPDATE_URLS, + 0, + 'The UPDATE_URLS variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.' + ], + [ + DeployInterface::VAR_STATIC_CONTENT_SYMLINK, + 0, + 'The STATIC_CONTENT_SYMLINK variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.' + ], + [ + DeployInterface::VAR_CLEAN_STATIC_FILES, + 0, + 'The CLEAN_STATIC_FILES variable contains an invalid value of type integer. ' . + 'Use one of the next types: boolean.' + ], + [ + DeployInterface::VAR_SEARCH_CONFIGURATION, + 'someOption', + 'The SEARCH_CONFIGURATION variable contains an invalid value of type string. ' . + 'Use one of the next types: array.' + ], + [ + DeployInterface::VAR_CACHE_CONFIGURATION, + 'someOption', + 'The CACHE_CONFIGURATION variable contains an invalid value of type string. ' . + 'Use one of the next types: array.' + ], + [ + DeployInterface::VAR_SESSION_CONFIGURATION, + 'someOption', + 'The SESSION_CONFIGURATION variable contains an invalid value of type string. ' . + 'Use one of the next types: array.' + ], + [ + DeployInterface::VAR_CRON_CONSUMERS_RUNNER, + 'someOption', + 'The CRON_CONSUMERS_RUNNER variable contains an invalid value of type string. ' . + 'Use one of the next types: array.' ], ]; } diff --git a/tests/integration/etc/build_options.ini.dist b/tests/integration/etc/build_options.ini.dist index 8bfec6ae1c..5a0c3c3f8f 100644 --- a/tests/integration/etc/build_options.ini.dist +++ b/tests/integration/etc/build_options.ini.dist @@ -1 +1 @@ -#VERBOSE_COMMANDS=enabled +; VERBOSE_COMMANDS=enabled