dockerComposeServiceSerialize()   B
last analyzed

Complexity

Conditions 8
Paths 12

Size

Total Lines 56
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 35
nc 12
nop 3
dl 0
loc 56
rs 8.1155
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace TheAentMachine\AentDockerCompose\Helper;
4
5
use Safe\Exceptions\FilesystemException;
6
use Symfony\Component\Filesystem\Filesystem;
7
use Symfony\Component\Process\Process;
8
use Symfony\Component\Yaml\Yaml;
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\YamlTools\YamlTools;
18
use function \Safe\file_get_contents;
19
use function \Safe\file_put_contents;
20
use function \Safe\unlink;
21
22
final class DockerComposeHelper
23
{
24
    public const VERSION = '3.7';
25
26
    /**
27
     * @param Service $service
28
     * @param string[] $envFileNames
29
     * @param string $version
30
     * @return mixed[]
31
     */
32
    public static function dockerComposeServiceSerialize(Service $service, array $envFileNames = [], string $version = self::VERSION): array
33
    {
34
        $portMap = function (array $port): string {
35
            return $port['source'] . ':' . $port['target'];
36
        };
37
        $envMapDockerCompose = self::getEnvironmentVariablesForDockerCompose($service);
38
        $envMap = function (EnvVariable $e) {
39
            if ($e->getComment() !== null) {
40
                return new CommentedItem($e->getValue(), $e->getComment());
41
            }
42
            return $e->getValue();
43
        };
44
        /**
45
         * @param NamedVolume|BindVolume|TmpfsVolume $v
46
         * @return array
47
         */
48
        $volumeMap = function ($v): array {
49
            $array = [
50
                'type' => $v->getType(),
51
                'source' => $v->getSource(),
52
            ];
53
            if ($v instanceof NamedVolume || $v instanceof BindVolume) {
54
                $array['target'] = $v->getTarget();
55
                $array['read_only'] = $v->isReadOnly();
56
            }
57
            return $array;
58
        };
59
        $dockerService = [
60
            'version' => $version,
61
            'services' => [
62
                $service->getServiceName() => array_filter([
63
                    'image' => $service->getImage(),
64
                    'command' => $service->getCommand(),
65
                    'depends_on' => $service->getDependsOn(),
66
                    'ports' => \array_map($portMap, $service->getPorts()),
67
                    'labels' => $service->getLabels(),
68
                    'environment' => \array_map($envMap, $envMapDockerCompose),
69
                    'volumes' => \array_map($volumeMap, $service->getVolumes()),
70
                ]),
71
            ],
72
        ];
73
        if ($envFileNames) {
74
            $dockerService['services'][$service->getServiceName()]['env_file'] = $envFileNames;
75
        }
76
        $namedVolumes = array();
77
        /** @var Volume $volume */
78
        foreach ($service->getVolumes() as $volume) {
79
            if ($volume->getType() === VolumeTypeEnum::NAMED_VOLUME) {
80
                // for now we just add them without any option
81
                $namedVolumes[$volume->getSource()] = null;
82
            }
83
        }
84
        if (!empty($namedVolumes)) {
85
            $dockerService['volumes'] = $namedVolumes;
86
        }
87
        return $dockerService;
88
    }
89
90
    /**
91
     * @param string $pathname
92
     * @throws FilesystemException
93
     */
94
    public static function checkDockerComposeFileValidity(string $pathname): void
95
    {
96
        // We cannot check the env_file directive as it is reading env files that are not available in the temporary directory.
97
        // Plus we cannot check the depends_on with undefined classes.
98
        // Therefore, we need to remove the env_file before checking.
99
        $strFile = file_get_contents($pathname);
100
        $content = Yaml::parse($strFile);
101
        if (isset($content['services'])) {
102
            $services = $content['services'];
103
            foreach ($services as $key => &$service) {
104
                unset($service['env_file'], $service['depends_on']);
105
            }
106
            $content['services'] = $services;
107
        }
108
        file_put_contents('/tmp/docker-compose-check.yml', YamlTools::dump($content));
109
        $command = ['docker-compose', '-f', '/tmp/docker-compose-check.yml', 'config', '-q'];
110
        $process = new Process($command);
111
        $process->enableOutput();
112
        $process->setTty(true);
113
        try {
114
            $process->mustRun();
115
        } finally {
116
            unlink('/tmp/docker-compose-check.yml');
117
        }
118
    }
119
120
    /**
121
     * Merge some yaml content into a docker-compose file (and check its validity, by default)
122
     * @param mixed[]|string $content
123
     * @param string $file
124
     * @param bool $checkValidity
125
     * @throws FilesystemException
126
     */
127
    public static function mergeContentInDockerComposeFile($content, string $file, bool $checkValidity = true): void
128
    {
129
        self::mergeContentInDockerComposeFiles($content, [$file], $checkValidity);
130
    }
131
132
    /**
133
     * Merge some yaml content into multiple docker-compose files (and check their validity, by default)
134
     * @param mixed[]|string $content
135
     * @param string[] $files
136
     * @param bool $checkValidity
137
     * @throws FilesystemException
138
     */
139
    public static function mergeContentInDockerComposeFiles($content, array $files, bool $checkValidity = true): void
140
    {
141
        if (\is_array($content)) {
142
            $content = YamlTools::dump($content);
143
        }
144
        if ($checkValidity) {
145
            $fileSystem = new Filesystem();
146
            $contentFile = $fileSystem->tempnam(\sys_get_temp_dir(), 'docker-compose-content-');
147
            $fileSystem->dumpFile($contentFile, $content);
148
            $tmpFiles = [];
149
            foreach ($files as $file) {
150
                $tmpFile = $fileSystem->tempnam(\sys_get_temp_dir(), 'docker-compose-tmp-');
151
                YamlTools::normalizeDockerCompose($file, $tmpFile);
152
                YamlTools::mergeTwoFiles($tmpFile, $contentFile);
153
                YamlTools::normalizeDockerCompose($tmpFile, $tmpFile);
154
                self::checkDockerComposeFileValidity($tmpFile);
155
                $tmpFiles[$file] = $tmpFile;
156
            }
157
            foreach ($files as $file) {
158
                $tmpFile = $tmpFiles[$file];
159
                $fileSystem->copy($tmpFile, $file, true);
160
            }
161
            $fileSystem->remove($contentFile);
162
            $fileSystem->remove($tmpFiles);
163
        } else {
164
            foreach ($files as $file) {
165
                YamlTools::mergeContentIntoFile($content, $file);
166
            }
167
        }
168
    }
169
170
    /**
171
     * @param Service $service
172
     * @return array<string,EnvVariable>
173
     */
174
    public static function getEnvironmentVariablesForDockerCompose(Service $service): array
175
    {
176
        return $service->getAllContainerEnvVariable() + $service->getAllImageEnvVariable();
177
    }
178
179
    /**
180
     * @param Service $service
181
     * @return array<string,EnvVariable>
182
     */
183
    public static function getEnvironmentVariablesForDotEnv(Service $service): array
184
    {
185
        return $service->getAllSharedSecret() + $service->getAllSharedEnvVariable();
186
    }
187
}
188