Skip to content

Commit e82a044

Browse files
oshmyheliukshiftedreality
authored andcommitted
MAGECLOUD-2574: Add Support for Environment-specific .magento.env.yaml Settings (#350)
1 parent 4406ac8 commit e82a044

File tree

8 files changed

+383
-5
lines changed

8 files changed

+383
-5
lines changed

src/Config/Environment.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,14 @@ public function isMasterBranch(): bool
262262
return isset($_ENV['MAGENTO_CLOUD_ENVIRONMENT'])
263263
&& preg_match(self::GIT_MASTER_BRANCH_RE, $_ENV['MAGENTO_CLOUD_ENVIRONMENT']);
264264
}
265+
266+
/**
267+
* Returns branch name of the current environment.
268+
*
269+
* @return string
270+
*/
271+
public function getBranchName(): string
272+
{
273+
return $_ENV['MAGENTO_CLOUD_BRANCH'] ?? '';
274+
}
265275
}

src/Config/Environment/Reader.php

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
*/
66
namespace Magento\MagentoCloud\Config\Environment;
77

8+
use Magento\MagentoCloud\Config\Environment;
89
use Magento\MagentoCloud\Filesystem\ConfigFileList;
910
use Magento\MagentoCloud\Filesystem\FileSystemException;
1011
use Magento\MagentoCloud\Filesystem\Reader\ReaderInterface;
1112
use Magento\MagentoCloud\Filesystem\Driver\File;
13+
use Magento\MagentoCloud\Filesystem\SystemList;
1214
use Symfony\Component\Yaml\Yaml;
1315
use Symfony\Component\Yaml\Exception\ParseException;
1416

@@ -17,6 +19,21 @@
1719
*/
1820
class Reader implements ReaderInterface
1921
{
22+
/**
23+
* @string
24+
*/
25+
const DIR_ENV_CONFIG = '.magento.env';
26+
27+
/**
28+
* @var SystemList
29+
*/
30+
private $systemList;
31+
32+
/**
33+
* @var Environment
34+
*/
35+
private $environment;
36+
2037
/**
2138
* @var ConfigFileList
2239
*/
@@ -35,11 +52,19 @@ class Reader implements ReaderInterface
3552
private $config;
3653

3754
/**
55+
* @param SystemList $systemList
56+
* @param Environment $environment
3857
* @param ConfigFileList $configFileList
3958
* @param File $file
4059
*/
41-
public function __construct(ConfigFileList $configFileList, File $file)
42-
{
60+
public function __construct(
61+
SystemList $systemList,
62+
Environment $environment,
63+
ConfigFileList $configFileList,
64+
File $file
65+
) {
66+
$this->systemList = $systemList;
67+
$this->environment = $environment;
4368
$this->configFileList = $configFileList;
4469
$this->file = $file;
4570
}
@@ -52,12 +77,73 @@ public function __construct(ConfigFileList $configFileList, File $file)
5277
public function read(): array
5378
{
5479
if ($this->config === null) {
55-
$path = $this->configFileList->getEnvConfig();
80+
$mainConfigPath = $this->configFileList->getEnvConfig();
5681

57-
$this->config = !$this->file->isExists($path) ?
58-
[] : (array)Yaml::parse($this->file->fileGetContents($path));
82+
$branchConfigPath = sprintf(
83+
'%s/%s/%s.yaml',
84+
$this->systemList->getMagentoRoot(),
85+
self::DIR_ENV_CONFIG,
86+
$this->environment->getBranchName()
87+
);
88+
89+
$this->config = $this->mergeConfigs(
90+
$this->parseConfig($mainConfigPath),
91+
$this->parseConfig($branchConfigPath)
92+
);
5993
}
6094

6195
return $this->config;
6296
}
97+
98+
/**
99+
* Returns parsed yaml config from file.
100+
* Returns an empty array if file doesn't exists
101+
*
102+
* @param string $path
103+
* @return array
104+
* @throws FileSystemException
105+
*/
106+
private function parseConfig(string $path): array
107+
{
108+
return !$this->file->isExists($path) ?
109+
[] : (array)Yaml::parse($this->file->fileGetContents($path));
110+
}
111+
112+
/**
113+
* Merges configuration from $branchConfig into $mainConfig by each stage or by each logger configuration.
114+
*
115+
* Separately merges each stage and logger configuration to avoid cases when the variable has an array value
116+
* and it should be replaced instead of recursive merging.
117+
*
118+
* @param array $mainConfig
119+
* @param array $branchConfig
120+
* @return array
121+
*/
122+
private function mergeConfigs(array $mainConfig, array $branchConfig): array
123+
{
124+
$newConfig = $mainConfig;
125+
126+
foreach ($branchConfig as $sectionName => $sectionConfig) {
127+
if (!is_array($sectionConfig)) {
128+
continue;
129+
}
130+
131+
foreach ($sectionConfig as $stageName => $stageConfig) {
132+
if (!is_array($stageConfig)) {
133+
continue;
134+
}
135+
136+
if (isset($newConfig[$sectionName][$stageName])) {
137+
$newConfig[$sectionName][$stageName] = array_merge(
138+
$newConfig[$sectionName][$stageName],
139+
$stageConfig
140+
);
141+
} else {
142+
$newConfig[$sectionName][$stageName] = $stageConfig;
143+
}
144+
}
145+
}
146+
147+
return $newConfig;
148+
}
63149
}
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\MagentoCloud\Test\Unit\Config\Environment;
7+
8+
use Magento\MagentoCloud\Config\Environment;
9+
use Magento\MagentoCloud\Config\Environment\Reader;
10+
use Magento\MagentoCloud\Filesystem\ConfigFileList;
11+
use Magento\MagentoCloud\Filesystem\Driver\File;
12+
use Magento\MagentoCloud\Filesystem\SystemList;
13+
use phpmock\phpunit\PHPMock;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use PHPUnit\Framework\TestCase;
16+
17+
/**
18+
* @inheritdoc
19+
*/
20+
class ReaderTest extends TestCase
21+
{
22+
use PHPMock;
23+
24+
/**
25+
* @var SystemList|MockObject
26+
*/
27+
private $systemListMock;
28+
29+
/**
30+
* @var Environment|MockObject
31+
*/
32+
private $environmentMock;
33+
34+
/**
35+
* @var ConfigFileList|MockObject
36+
*/
37+
private $configFileListMock;
38+
39+
/**
40+
* @var File|MockObject
41+
*/
42+
private $fileMock;
43+
44+
/**
45+
* @var Reader
46+
*/
47+
private $reader;
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
protected function setUp()
53+
{
54+
/**
55+
* This lines are required for proper running of Magento\MagentoCloud\Test\Unit\Filesystem\Driver\FileTest
56+
*/
57+
self::defineFunctionMock('Magento\MagentoCloud\Filesystem\Driver', 'file_get_contents');
58+
self::defineFunctionMock('Magento\MagentoCloud\Filesystem\Driver', 'file_exists');
59+
60+
$this->systemListMock = $this->createMock(SystemList::class);
61+
$this->environmentMock = $this->createMock(Environment::class);
62+
$this->configFileListMock = $this->createMock(ConfigFileList::class);
63+
$this->fileMock = $this->createPartialMock(File::class, []);
64+
65+
$this->reader = new Reader(
66+
$this->systemListMock,
67+
$this->environmentMock,
68+
$this->configFileListMock,
69+
$this->fileMock
70+
);
71+
}
72+
73+
public function testRead()
74+
{
75+
$baseDir = __DIR__ . '/_file/';
76+
77+
$this->configFileListMock->expects($this->once())
78+
->method('getEnvConfig')
79+
->willReturn($baseDir . '/.magento.env.yaml');
80+
$this->systemListMock->expects($this->once())
81+
->method('getMagentoRoot')
82+
->willReturn($baseDir);
83+
$this->environmentMock->expects($this->once())
84+
->method('getBranchName')
85+
->willReturn('test-branch');
86+
87+
$this->reader->read();
88+
$this->assertEquals(
89+
[
90+
'stage' => [
91+
'global' => ['SCD_ON_DEMAND' => false, 'UPDATE_URLS' => false],
92+
'deploy' => ['DATABASE_CONFIGURATION' => ['host' => 'localhost'], 'SCD_THREADS' => 3],
93+
'build' => ['SCD_THREADS' => 2],
94+
],
95+
'log' => [
96+
'gelf' => [
97+
'min_level' => 'info',
98+
'use_default_formatter' => true,
99+
'additional' => ['project' => 'project'],
100+
],
101+
'syslog' => ['ident' => 'ident-branch', 'facility' => 7],
102+
],
103+
],
104+
$this->reader->read()
105+
);
106+
}
107+
108+
public function testReadBranchConfigNotExists()
109+
{
110+
$baseDir = __DIR__ . '/_file/';
111+
112+
$this->configFileListMock->expects($this->once())
113+
->method('getEnvConfig')
114+
->willReturn($baseDir . '/.magento.env.yaml');
115+
$this->systemListMock->expects($this->once())
116+
->method('getMagentoRoot')
117+
->willReturn($baseDir);
118+
$this->environmentMock->expects($this->once())
119+
->method('getBranchName')
120+
->willReturn('not-exist');
121+
122+
$this->assertEquals(
123+
[
124+
'stage' => [
125+
'global' => ['SCD_ON_DEMAND' => true, 'UPDATE_URLS' => false],
126+
'deploy' => [
127+
'DATABASE_CONFIGURATION' => [
128+
'host' => '127.0.0.1',
129+
'port' => '3306',
130+
'schema' => 'test_schema',
131+
],
132+
'SCD_THREADS' => 5,
133+
],
134+
],
135+
'log' => [
136+
'gelf' => [
137+
'min_level' => 'info',
138+
'use_default_formatter' => true,
139+
'additional' => ['project' => 'project', 'app_id' => 'app'],
140+
],
141+
],
142+
],
143+
$this->reader->read()
144+
);
145+
}
146+
147+
public function testReadBranchConfigWithEmptySectionAndStage()
148+
{
149+
$baseDir = __DIR__ . '/_file/';
150+
151+
$this->configFileListMock->expects($this->once())
152+
->method('getEnvConfig')
153+
->willReturn($baseDir . '/.magento.env.yaml');
154+
$this->systemListMock->expects($this->once())
155+
->method('getMagentoRoot')
156+
->willReturn($baseDir);
157+
$this->environmentMock->expects($this->once())
158+
->method('getBranchName')
159+
->willReturn('test-branch-emty');
160+
161+
$this->assertEquals(
162+
[
163+
'stage' => [
164+
'global' => ['SCD_ON_DEMAND' => true, 'UPDATE_URLS' => false],
165+
'deploy' => [
166+
'DATABASE_CONFIGURATION' => [
167+
'host' => '127.0.0.1',
168+
'port' => '3306',
169+
'schema' => 'test_schema',
170+
],
171+
'SCD_THREADS' => 5,
172+
],
173+
],
174+
'log' => [
175+
'gelf' => [
176+
'min_level' => 'info',
177+
'use_default_formatter' => true,
178+
'additional' => ['project' => 'project', 'app_id' => 'app'],
179+
],
180+
],
181+
],
182+
$this->reader->read()
183+
);
184+
}
185+
186+
public function testReadMainConfigWithEmptySectionAndStage()
187+
{
188+
$baseDir = __DIR__ . '/_file/';
189+
190+
$this->configFileListMock->expects($this->once())
191+
->method('getEnvConfig')
192+
->willReturn($baseDir . '/.magento-with-empty-sections.env.yaml');
193+
$this->systemListMock->expects($this->once())
194+
->method('getMagentoRoot')
195+
->willReturn($baseDir);
196+
$this->environmentMock->expects($this->once())
197+
->method('getBranchName')
198+
->willReturn('test-branch');
199+
200+
$this->reader->read();
201+
$this->assertEquals(
202+
[
203+
'stage' => [
204+
'global' => ['SCD_ON_DEMAND' => false],
205+
'deploy' => ['DATABASE_CONFIGURATION' => ['host' => 'localhost'], 'SCD_THREADS' => 3],
206+
'build' => ['SCD_THREADS' => 2],
207+
],
208+
'log' => [
209+
'gelf' => [
210+
'min_level' => 'info',
211+
'use_default_formatter' => true,
212+
'additional' => ['project' => 'project'],
213+
],
214+
'syslog' => ['ident' => 'ident-branch', 'facility' => 7],
215+
],
216+
],
217+
$this->reader->read()
218+
);
219+
}
220+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
stage:
2+
global:
3+
SCD_ON_DEMAND: true
4+
build:
5+
deploy:
6+
7+
log:
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
stage:
2+
global:
3+
SCD_ON_DEMAND: true
4+
UPDATE_URLS: false
5+
deploy:
6+
DATABASE_CONFIGURATION:
7+
host: 127.0.0.1
8+
port: 3306
9+
schema: test_schema
10+
SCD_THREADS: 5
11+
12+
log:
13+
gelf:
14+
min_level: "info"
15+
use_default_formatter: true
16+
additional:
17+
project: "project"
18+
app_id: "app"

0 commit comments

Comments
 (0)