Completed
Pull Request — master (#13)
by
unknown
02:37
created

checkDockerComposeFileValidity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace TheAentMachine\AentDockerCompose\DockerCompose;
4
5
use Psr\Log\LoggerInterface;
6
use Symfony\Component\Finder\Finder;
7
use Symfony\Component\Process\Process;
8
use TheAentMachine\AentDockerCompose\Aenthill\Enum\PheromoneEnum;
9
use TheAentMachine\AentDockerCompose\Aenthill\Exception\ContainerProjectDirEnvVariableEmptyException;
10
use TheAentMachine\Service\Enum\VolumeTypeEnum;
11
use TheAentMachine\Service\Environment\EnvVariable;
12
use TheAentMachine\Service\Service;
13
use TheAentMachine\Service\Volume\BindVolume;
14
use TheAentMachine\Service\Volume\NamedVolume;
15
use TheAentMachine\Service\Volume\TmpfsVolume;
16
use TheAentMachine\Service\Volume\Volume;
17
18
class DockerComposeService
19
{
20
    public const VERSION = '3.3';
21
22
    /** @var LoggerInterface */
23
    private $log;
24
25
    /** @var DockerComposeFile[] */
26
    private $files;
27
28
    public function __construct(LoggerInterface $log)
29
    {
30
        $this->log = $log;
31
    }
32
33
    /**
34
     * @throws ContainerProjectDirEnvVariableEmptyException
35
     */
36
    private function seekFiles(): void
37
    {
38
        $containerProjectDir = getenv(PheromoneEnum::PHEROMONE_CONTAINER_PROJECT_DIR);
39
        if (empty($containerProjectDir)) {
40
            throw new ContainerProjectDirEnvVariableEmptyException();
41
        }
42
43
        $finder = new Finder();
44
        $dockerComposeFileFilter = function (\SplFileInfo $file) {
45
            return $file->isFile() && preg_match('/^docker-compose(.)*\.(yaml|yml)$/', $file->getFilename());
46
        };
47
        $finder->files()->filter($dockerComposeFileFilter)->in($containerProjectDir)->depth('== 0');
48
49
        if (!$finder->hasResults()) {
50
            $this->log->info("no docker-compose file found, let's create it");
51
            $this->createDockerComposeFile($containerProjectDir . '/docker-compose.yml');
52
            return;
53
        }
54
55
        /** @var \SplFileInfo $file */
56
        foreach ($finder as $file) {
57
            $this->files[] = new DockerComposeFile($file);
58
            $this->log->info($file->getFilename() . ' has been found');
59
        }
60
61
        /*if (count($this->files) === 1) {
62
            $this->log->info($this->files[0]->getFilename() . ' has been found');
63
            return;
64
        }
65
66
        throw new NotImplementedException("multiple docker-compose files handling is not yet implemented");
67
        */
68
    }
69
70
    /**
71
     * @return string[]
72
     */
73
    public function getDockerComposePathnames(): array
74
    {
75
        if ($this->files === null) {
76
            $this->seekFiles();
77
        }
78
        $pathnames = array();
79
        foreach ($this->files as $file) {
80
            $pathnames[] = $file->getPathname();
81
        }
82
        return $pathnames;
83
    }
84
85
    /**
86
     * @param string $path
87
     */
88
    private function createDockerComposeFile(string $path): void
89
    {
90
        // TODO ask questions about version and so on!
91
        $fp = fopen($path, 'w+b');
92
        fwrite($fp, "version: '" . self::VERSION . "'");
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fwrite() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
        fwrite(/** @scrutinizer ignore-type */ $fp, "version: '" . self::VERSION . "'");
Loading history...
93
        fclose($fp);
0 ignored issues
show
Bug introduced by
It seems like $fp can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
        fclose(/** @scrutinizer ignore-type */ $fp);
Loading history...
94
95
        $file = new DockerComposeFile(new \SplFileInfo($path));
96
        $this->files[] = $file;
97
        $this->log->info($file->getFilename() . ' was successfully created!');
98
    }
99
100
    /**
101
     * @param Service $service
102
     * @param string $version
103
     * @return mixed[]
104
     */
105
    public static function dockerComposeServiceSerialize(Service $service, string $version = self::VERSION): array
106
    {
107
        $portMap = function (array $port): string {
108
            return $port['source'] . ':' . $port['target'];
109
        };
110
        $labelMap = function (array $label): string {
111
            return $label['value'];
112
        };
113
        $envMap = function (EnvVariable $e): string {
114
            return $e->getValue();
115
        };
116
        /**
117
         * @param NamedVolume|BindVolume|TmpfsVolume $v
118
         * @return array
119
         */
120
        $volumeMap = function ($v): array {
121
            $array = [
122
                'type' => $v->getType(),
123
                'source' => $v->getSource(),
124
            ];
125
            if ($v instanceof NamedVolume || $v instanceof BindVolume) {
126
                $array['target'] = $v->getTarget();
127
                $array['read_only'] = $v->isReadOnly();
128
            }
129
            return $array;
130
        };
131
        $dockerService = [
132
            'version' => $version,
133
            'services' => [
134
                $service->getServiceName() => array_filter([
135
                    'image' => $service->getImage(),
136
                    'command' => $service->getCommand(),
137
                    'depends_on' => $service->getDependsOn(),
138
                    'ports' => array_map($portMap, $service->getPorts()),
139
                    'labels' => array_map($labelMap, $service->getLabels()),
140
                    'environment' => array_map($envMap, $service->getEnvironment()),
141
                    'volumes' => array_map($volumeMap, $service->getVolumes()),
142
                ]),
143
            ],
144
        ];
145
        $namedVolumes = array();
146
        /** @var Volume $volume */
147
        foreach ($service->getVolumes() as $volume) {
148
            if ($volume->getType() === VolumeTypeEnum::NAMED_VOLUME) {
149
                // for now we just add them without any option
150
                $namedVolumes[$volume->getSource()] = null;
151
            }
152
        }
153
        if (!empty($namedVolumes)) {
154
            $dockerService['volumes'] = $namedVolumes;
155
        }
156
        return $dockerService;
157
    }
158
159
    /**
160
     * @param string $pathname
161
     */
162
    public static function checkDockerComposeFileValidity(string $pathname): void
163
    {
164
        $command = ['docker-compose', '-f', $pathname, 'config', '-q'];
165
        $process = new Process($command);
166
        $process->enableOutput();
167
        $process->setTty(true);
168
        $process->mustRun();
169
    }
170
}
171