Completed
Pull Request — master (#31)
by Jindun
02:33
created

DockerComposeService::createDockerComposeFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
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\VolumeTypeEnum;
9
use TheAentMachine\Service\Environment\EnvVariable;
10
use TheAentMachine\Service\Service;
11
use TheAentMachine\Service\Volume\BindVolume;
12
use TheAentMachine\Service\Volume\NamedVolume;
13
use TheAentMachine\Service\Volume\TmpfsVolume;
14
use TheAentMachine\Service\Volume\Volume;
15
use TheAentMachine\YamlTools\YamlTools;
16
17
class DockerComposeService
18
{
19
    public const VERSION = '3.3';
20
21
    /**
22
     * @param Service $service
23
     * @param string $version
24
     * @return mixed[]
25
     */
26
    public static function dockerComposeServiceSerialize(Service $service, string $version = self::VERSION): array
27
    {
28
        $portMap = function (array $port): string {
29
            return $port['source'] . ':' . $port['target'];
30
        };
31
        $labelMap = function (array $label): string {
32
            return $label['value'];
33
        };
34
        $envMap = function (EnvVariable $e): string {
35
            return $e->getValue();
36
        };
37
        /**
38
         * @param NamedVolume|BindVolume|TmpfsVolume $v
39
         * @return array
40
         */
41
        $volumeMap = function ($v): array {
42
            $array = [
43
                'type' => $v->getType(),
44
                'source' => $v->getSource(),
45
            ];
46
            if ($v instanceof NamedVolume || $v instanceof BindVolume) {
47
                $array['target'] = $v->getTarget();
48
                $array['read_only'] = $v->isReadOnly();
49
            }
50
            return $array;
51
        };
52
        $dockerService = [
53
            'version' => $version,
54
            'services' => [
55
                $service->getServiceName() => array_filter([
56
                    'image' => $service->getImage(),
57
                    'command' => $service->getCommand(),
58
                    'depends_on' => $service->getDependsOn(),
59
                    'ports' => array_map($portMap, $service->getPorts()),
60
                    'labels' => array_map($labelMap, $service->getLabels()),
61
                    'environment' => array_map($envMap, $service->getEnvironment()),
62
                    'volumes' => array_map($volumeMap, $service->getVolumes()),
63
                ]),
64
            ],
65
        ];
66
        $namedVolumes = array();
67
        /** @var Volume $volume */
68
        foreach ($service->getVolumes() as $volume) {
69
            if ($volume->getType() === VolumeTypeEnum::NAMED_VOLUME) {
70
                // for now we just add them without any option
71
                $namedVolumes[$volume->getSource()] = null;
72
            }
73
        }
74
        if (!empty($namedVolumes)) {
75
            $dockerService['volumes'] = $namedVolumes;
76
        }
77
        return $dockerService;
78
    }
79
80
    /**
81
     * @param string $pathname
82
     */
83
    public static function checkDockerComposeFileValidity(string $pathname): void
84
    {
85
        $command = ['docker-compose', '-f', $pathname, 'config', '-q'];
86
        $process = new Process($command);
87
        $process->enableOutput();
88
        $process->setTty(true);
89
        $process->mustRun();
90
    }
91
92
93
    /**
94
     * Merge some yaml content into a docker-compose file (and check its validity, by default)
95
     * @param mixed[]|string $content
96
     * @param string $file
97
     * @param bool $checkValidity
98
     */
99
    public static function mergeContentInDockerComposeFile($content, string $file, bool $checkValidity = true): void
100
    {
101
        self::mergeContentInDockerComposeFiles($content, [$file], $checkValidity);
102
    }
103
104
    /**
105
     * Merge some yaml content into multiple docker-compose files (and check their validity, by default)
106
     * @param mixed[]|string $content
107
     * @param string[] $files
108
     * @param bool $checkValidity
109
     */
110
    public static function mergeContentInDockerComposeFiles($content, array $files, bool $checkValidity = true): void
111
    {
112
        if (\is_array($content)) {
113
            $content = Yaml::dump($content, 256, 2, Yaml::DUMP_OBJECT_AS_MAP);
114
        }
115
116
        if ($checkValidity) {
117
            $fileSystem = new Filesystem();
118
            $contentFile = $fileSystem->tempnam(sys_get_temp_dir(), 'docker-compose-content-');
119
            $fileSystem->dumpFile($contentFile, $content);
120
121
            $tmpFiles = [];
122
            foreach ($files as $file) {
123
                $tmpFile = $fileSystem->tempnam(sys_get_temp_dir(), 'docker-compose-tmp-');
124
                YamlTools::normalizeDockerCompose($file, $tmpFile);
125
                YamlTools::mergeTwoFiles($tmpFile, $contentFile);
126
                YamlTools::normalizeDockerCompose($tmpFile, $tmpFile);
127
                self::checkDockerComposeFileValidity($tmpFile);
128
                $tmpFiles[$file] = $tmpFile;
129
            }
130
131
            foreach ($files as $file) {
132
                $tmpFile = $tmpFiles[$file];
133
                $fileSystem->copy($tmpFile, $file, true);
134
            }
135
136
            $fileSystem->remove($contentFile);
137
            $fileSystem->remove($tmpFiles);
138
        } else {
139
            foreach ($files as $file) {
140
                YamlTools::mergeContentIntoFile($content, $file);
141
            }
142
        }
143
    }
144
}
145