Completed
Push — master ( 73295c...349be7 )
by David
02:23
created

getEnvironmentVariablesForDotEnv()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
namespace TheAentMachine\AentDockerCompose\DockerCompose;
4
5
use Symfony\Component\Filesystem\Filesystem;
6
use Symfony\Component\Process\Process;
7
use Symfony\Component\Yaml\Yaml;
8
use TheAentMachine\Service\Enum\EnvVariableTypeEnum;
9
use TheAentMachine\Service\Enum\VolumeTypeEnum;
10
use TheAentMachine\Service\Environment\EnvVariable;
11
use TheAentMachine\Service\Service;
12
use TheAentMachine\Service\Volume\BindVolume;
13
use TheAentMachine\Service\Volume\NamedVolume;
14
use TheAentMachine\Service\Volume\TmpfsVolume;
15
use TheAentMachine\Service\Volume\Volume;
16
use TheAentMachine\Yaml\CommentedItem;
17
use TheAentMachine\Yaml\Dumper;
18
use TheAentMachine\YamlTools\YamlTools;
19
20
class DockerComposeService
21
{
22
    public const VERSION = '3.3';
23
24
    /**
25
     * @param Service $service
26
     * @param string $version
27
     * @return mixed[]
28
     */
29
    public static function dockerComposeServiceSerialize(Service $service, ?string $envFileName = null, string $version = self::VERSION): array
30
    {
31
        $portMap = function (array $port): string {
32
            return $port['source'] . ':' . $port['target'];
33
        };
34
        $envMapDockerCompose = self::getEnvironmentVariablesForDockerCompose($service);
35
36
        $envMap = function (EnvVariable $e) {
37
            if ($e->getComment() !== null) {
38
                return new CommentedItem($e->getValue(), $e->getComment());
39
            }
40
            return $e->getValue();
41
        };
42
        /**
43
         * @param NamedVolume|BindVolume|TmpfsVolume $v
44
         * @return array
45
         */
46
        $volumeMap = function ($v): array {
47
            $array = [
48
                'type' => $v->getType(),
49
                'source' => $v->getSource(),
50
            ];
51
            if ($v instanceof NamedVolume || $v instanceof BindVolume) {
52
                $array['target'] = $v->getTarget();
53
                $array['read_only'] = $v->isReadOnly();
54
            }
55
            return $array;
56
        };
57
        $dockerService = [
58
            'version' => $version,
59
            'services' => [
60
                $service->getServiceName() => array_filter([
61
                    'image' => $service->getImage(),
62
                    'command' => $service->getCommand(),
63
                    'depends_on' => $service->getDependsOn(),
64
                    'ports' => array_map($portMap, $service->getPorts()),
65
                    'labels' => $service->getLabels(),
66
                    'environment' => array_map($envMap, $envMapDockerCompose),
67
                    'volumes' => array_map($volumeMap, $service->getVolumes()),
68
                ]),
69
            ],
70
        ];
71
        if ($envFileName) {
72
            $dockerService['services'][$service->getServiceName()]['env_file'][] = $envFileName;
73
        }
74
75
        $namedVolumes = array();
76
        /** @var Volume $volume */
77
        foreach ($service->getVolumes() as $volume) {
78
            if ($volume->getType() === VolumeTypeEnum::NAMED_VOLUME) {
79
                // for now we just add them without any option
80
                $namedVolumes[$volume->getSource()] = null;
81
            }
82
        }
83
        if (!empty($namedVolumes)) {
84
            $dockerService['volumes'] = $namedVolumes;
85
        }
86
        return $dockerService;
87
    }
88
89
    /**
90
     * @param string $pathname
91
     */
92
    public static function checkDockerComposeFileValidity(string $pathname): void
93
    {
94
        $command = ['docker-compose', '-f', $pathname, 'config', '-q'];
95
        $process = new Process($command);
96
        $process->enableOutput();
97
        $process->setTty(true);
98
        $process->mustRun();
99
    }
100
101
102
    /**
103
     * Merge some yaml content into a docker-compose file (and check its validity, by default)
104
     * @param mixed[]|string $content
105
     * @param string $file
106
     * @param bool $checkValidity
107
     */
108
    public static function mergeContentInDockerComposeFile($content, string $file, bool $checkValidity = true): void
109
    {
110
        self::mergeContentInDockerComposeFiles($content, [$file], $checkValidity);
111
    }
112
113
    /**
114
     * Merge some yaml content into multiple docker-compose files (and check their validity, by default)
115
     * @param mixed[]|string $content
116
     * @param string[] $files
117
     * @param bool $checkValidity
118
     */
119
    public static function mergeContentInDockerComposeFiles($content, array $files, bool $checkValidity = true): void
120
    {
121
        if (\is_array($content)) {
122
            $content = YamlTools::dump($content);
123
        }
124
125
        if ($checkValidity) {
126
            $fileSystem = new Filesystem();
127
            $contentFile = $fileSystem->tempnam(sys_get_temp_dir(), 'docker-compose-content-');
128
            $fileSystem->dumpFile($contentFile, $content);
129
130
            $tmpFiles = [];
131
            foreach ($files as $file) {
132
                $tmpFile = $fileSystem->tempnam(sys_get_temp_dir(), 'docker-compose-tmp-');
133
                YamlTools::normalizeDockerCompose($file, $tmpFile);
134
                YamlTools::mergeTwoFiles($tmpFile, $contentFile);
135
                YamlTools::normalizeDockerCompose($tmpFile, $tmpFile);
136
                self::checkDockerComposeFileValidity($tmpFile);
137
                $tmpFiles[$file] = $tmpFile;
138
            }
139
140
            foreach ($files as $file) {
141
                $tmpFile = $tmpFiles[$file];
142
                $fileSystem->copy($tmpFile, $file, true);
143
            }
144
145
            $fileSystem->remove($contentFile);
146
            $fileSystem->remove($tmpFiles);
147
        } else {
148
            foreach ($files as $file) {
149
                YamlTools::mergeContentIntoFile($content, $file);
150
            }
151
        }
152
    }
153
154
    /**
155
     * @param Service $service
156
     * @return array<string,EnvVariable>
157
     */
158
    public static function getEnvironmentVariablesForDockerCompose(Service $service): array
159
    {
160
        $envMapDockerCompose = [];
161
        foreach ($service->getEnvironment() as $key => $env) {
162
            // TODO: in prod mode, the EnvVariableTypeEnum::IMAGE_ENV_VARIABLE should not add a variable in the docker-compose file
163
            if (\in_array($env->getType(), [EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE, EnvVariableTypeEnum::IMAGE_ENV_VARIABLE])) {
164
                $envMapDockerCompose[$key] = $env;
165
            }
166
        }
167
        return $envMapDockerCompose;
168
    }
169
170
    /**
171
     * @param Service $service
172
     * @return array<string,EnvVariable>
173
     */
174
    public static function getEnvironmentVariablesForDotEnv(Service $service): array
175
    {
176
        $envMapDotEnvFile = [];
177
        foreach ($service->getEnvironment() as $key => $env) {
178
            if (\in_array($env->getType(), [EnvVariableTypeEnum::SHARED_ENV_VARIABLE, EnvVariableTypeEnum::SHARED_SECRET])) {
179
                $envMapDotEnvFile[$key] = $env;
180
            }
181
        }
182
        return $envMapDotEnvFile;
183
    }
184
}
185