Completed
Push — master ( dd2202...b300ec )
by Julien
13s
created

Service::setInternalPorts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace TheAentMachine\Service;
4
5
use JsonSerializable;
6
use Opis\JsonSchema\ValidationError;
7
use Opis\JsonSchema\Validator;
8
use TheAentMachine\Aenthill\Manifest;
9
use TheAentMachine\Aenthill\CommonMetadata;
10
use TheAentMachine\Service\Enum\EnvVariableTypeEnum;
11
use TheAentMachine\Service\Enum\VolumeTypeEnum;
12
use TheAentMachine\Service\Environment\EnvVariable;
13
use TheAentMachine\Service\Exception\ServiceException;
14
use TheAentMachine\Service\Volume\BindVolume;
15
use TheAentMachine\Service\Volume\NamedVolume;
16
use TheAentMachine\Service\Volume\TmpfsVolume;
17
18
class Service implements JsonSerializable
19
{
20
    /** @var string */
21
    private $serviceName = '';
22
    /** @var string|null */
23
    private $image;
24
    /** @var string[] */
25
    private $command = [];
26
    /** @var int[] */
27
    private $internalPorts = [];
28
    /** @var string[] */
29
    private $dependsOn = [];
30
    /** @var mixed[] */
31
    private $ports = [];
32
    /** @var mixed[] */
33
    private $labels = [];
34
    /** @var mixed[] */
35
    private $environment = [];
36
    /** @var mixed[] */
37
    private $volumes = [];
38
    /** @var null|bool */
39
    private $needVirtualHost;
40
    /** @var null|bool */
41
    private $needBuild;
42
    /** @var \stdClass */
43
    private $validatorSchema;
44
    /** @var string[] */
45
    private $dockerfileCommands = [];
46
    /** @var string[] */
47
    private $destEnvTypes = []; // empty === all env types
48
49
    public function __construct()
50
    {
51
        $this->validatorSchema = \GuzzleHttp\json_decode((string)file_get_contents(__DIR__ . '/ServiceJsonSchema.json'), false);
52
    }
53
54
    /**
55
     * @param mixed[] $payload
56
     * @return Service
57
     * @throws ServiceException
58
     */
59
    public static function parsePayload(array $payload): Service
60
    {
61
        $service = new self();
62
        $service->checkValidity($payload);
63
        $service->serviceName = $payload['serviceName'] ?? '';
64
        $s = $payload['service'] ?? [];
65
        if (!empty($s)) {
66
            $service->image = $s['image'] ?? null;
67
            $service->command = $s['command'] ?? [];
68
            $service->internalPorts = $s['internalPorts'] ?? [];
69
            $service->dependsOn = $s['dependsOn'] ?? [];
70
            $service->ports = $s['ports'] ?? [];
71
            $service->labels = $s['labels'] ?? [];
72
            if (!empty($s['environment'])) {
73
                foreach ($s['environment'] as $key => $env) {
74
                    $service->addEnvVar($key, $env['value'], $env['type']);
75
                }
76
            }
77
            if (!empty($s['volumes'])) {
78
                foreach ($s['volumes'] as $vol) {
79
                    $service->addVolume($vol['type'], $vol['source'], $vol['target'] ?? '', $vol['readOnly'] ?? false);
80
                }
81
            }
82
            $service->needVirtualHost = $s['needVirtualHost'] ?? null;
83
            $service->needBuild = $s['needBuild'] ?? null;
84
        }
85
        $service->dockerfileCommands = $payload['dockerfileCommands'] ?? [];
86
        $service->destEnvTypes = $payload['destEnvTypes'] ?? [];
87
        return $service;
88
    }
89
90
    /**
91
     * Specify data which should be serialized to JSON
92
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
93
     * @return array data which can be serialized by <b>json_encode</b>,
94
     * which is a value of any type other than a resource.
95
     * @since 5.4.0
96
     * @throws ServiceException
97
     */
98
    public function jsonSerialize(): array
99
    {
100
        $jsonSerializeMap = function (JsonSerializable $obj): array {
101
            return $obj->jsonSerialize();
102
        };
103
104
        $json = array(
105
            'serviceName' => $this->serviceName,
106
        );
107
108
        $service = array_filter([
109
            'image' => $this->image,
110
            'command' => $this->command,
111
            'internalPorts' => $this->internalPorts,
112
            'dependsOn' => $this->dependsOn,
113
            'ports' => $this->ports,
114
            'labels' => $this->labels,
115
            'environment' => array_map($jsonSerializeMap, $this->environment),
116
            'volumes' => array_map($jsonSerializeMap, $this->volumes),
117
            'needVirtualHost' => $this->needVirtualHost,
118
            'needBuild' => $this->needBuild,
119
        ]);
120
121
        if (!empty($service)) {
122
            $json['service'] = $service;
123
        }
124
125
        if (!empty($this->dockerfileCommands)) {
126
            $json['dockerfileCommands'] = $this->dockerfileCommands;
127
        }
128
129
        $json['destEnvTypes'] = $this->destEnvTypes;
130
131
        $this->checkValidity($json);
132
        return $json;
133
    }
134
135
    /**
136
     * @return mixed[]
137
     */
138
    public function imageJsonSerialize(): array
139
    {
140
        $dockerfileCommands = [];
141
        $dockerfileCommands[] = 'FROM ' . $this->image;
142
        foreach ($this->environment as $key => $env) {
143
            if ($env->getType() === EnvVariableTypeEnum::IMAGE_ENV_VARIABLE) {
144
                $dockerfileCommands[] = "ENV $key" . '='. $env->getValue();
145
            }
146
        }
147
        foreach ($this->volumes as $volume) {
148
            if ($volume->getType() === VolumeTypeEnum::BIND_VOLUME) {
149
                $dockerfileCommands[] = 'COPY ' . $volume->getSource() . ' ' . $volume->getTarget();
150
            }
151
        }
152
153
        if (!empty($this->command)) {
154
            $dockerfileCommands[] = 'CMD ' . implode(' ', $this->command);
155
        }
156
157
        $dockerfileCommands = array_merge($dockerfileCommands, $this->dockerfileCommands);
158
159
        return [
160
            'serviceName' => $this->serviceName,
161
            'dockerfileCommands' => $dockerfileCommands,
162
            'destEnvTypes' => $this->destEnvTypes,
163
        ];
164
    }
165
166
    /**
167
     * @param \stdClass|array|string $data
168
     * @return bool
169
     * @throws ServiceException
170
     */
171
    private function checkValidity($data): bool
172
    {
173
        if (\is_array($data)) {
174
            $data = \GuzzleHttp\json_decode(\GuzzleHttp\json_encode($data), false);
175
        }
176
        $validator = new Validator();
177
        $result = $validator->dataValidation($data, $this->validatorSchema);
178
        if (!$result->isValid()) {
179
            /** @var ValidationError $vError */
180
            $vError = $result->getFirstError();
181
            throw ServiceException::invalidServiceData($vError);
182
        }
183
        return $result->isValid();
184
    }
185
186
187
    /************************ getters **********************/
188
189
    public function getServiceName(): string
190
    {
191
        return $this->serviceName;
192
    }
193
194
    public function getImage(): ?string
195
    {
196
        return $this->image;
197
    }
198
199
    /** @return string[] */
200
    public function getCommand(): array
201
    {
202
        return $this->command;
203
    }
204
205
    /** @return int[] */
206
    public function getInternalPorts(): array
207
    {
208
        return $this->internalPorts;
209
    }
210
211
    /** @return string[] */
212
    public function getDependsOn(): array
213
    {
214
        return $this->dependsOn;
215
    }
216
217
    /** @return mixed[] */
218
    public function getPorts(): array
219
    {
220
        return $this->ports;
221
    }
222
223
    /** @return mixed[] */
224
    public function getLabels(): array
225
    {
226
        return $this->labels;
227
    }
228
229
    /** @return mixed[] */
230
    public function getEnvironment(): array
231
    {
232
        return $this->environment;
233
    }
234
235
    /** @return mixed[] */
236
    public function getVolumes(): array
237
    {
238
        return $this->volumes;
239
    }
240
241
    public function getNeedVirtualHost(): ?bool
242
    {
243
        return $this->needVirtualHost;
244
    }
245
246
    public function getNeedBuild(): ?bool
247
    {
248
        return $this->needBuild;
249
    }
250
251
    /** @return string[] */
252
    public function getDockerfileCommands(): array
253
    {
254
        return $this->dockerfileCommands;
255
    }
256
257
    /**  @return string[] */
258
    public function getDestEnvTypes(): array
259
    {
260
        return $this->destEnvTypes;
261
    }
262
263
264
    /************************ setters **********************/
265
266
    public function setServiceName(string $serviceName): void
267
    {
268
        $this->serviceName = $serviceName;
269
    }
270
271
    public function setImage(?string $image): void
272
    {
273
        $this->image = $image;
274
    }
275
276
    /** @param string[] $command  */
277
    public function setCommand(array $command): void
278
    {
279
        $this->command = $command;
280
    }
281
282
    /**
283
     * @param int[] $internalPorts
284
     */
285
    public function setInternalPorts(array $internalPorts): void
286
    {
287
        $this->internalPorts = $internalPorts;
288
    }
289
290
    /** @param string[] $dependsOn */
291
    public function setDependsOn(array $dependsOn): void
292
    {
293
        $this->dependsOn = $dependsOn;
294
    }
295
296
    public function setNeedVirtualHost(?bool $needVirtualHost): void
297
    {
298
        $this->needVirtualHost = $needVirtualHost;
299
    }
300
301
    public function setNeedBuild(?bool $needBuild): void
302
    {
303
        $this->needBuild = $needBuild;
304
    }
305
306
307
    /************************ adders **********************/
308
309
    public function addCommand(string $command): void
310
    {
311
        $this->command[] = $command;
312
    }
313
314
    public function addInternalPort(int $internalPort): void
315
    {
316
        $this->internalPorts[] = $internalPort;
317
    }
318
319
    public function addDependsOn(string $dependsOn): void
320
    {
321
        $this->dependsOn[] = $dependsOn;
322
    }
323
324
    public function addPort(int $source, int $target): void
325
    {
326
        $this->ports[] = array(
327
            'source' => $source,
328
            'target' => $target,
329
        );
330
    }
331
332
    public function addLabel(string $key, string $value): void
333
    {
334
        $this->labels[$key] = array(
335
            'value' => $value,
336
        );
337
    }
338
339
340
    /************************ environment adders **********************/
341
342
    /** @throws ServiceException */
343
    private function addEnvVar(string $key, string $value, string $type): void
344
    {
345
        switch ($type) {
346
            case EnvVariableTypeEnum::SHARED_ENV_VARIABLE:
347
                $this->addSharedEnvVariable($key, $value);
348
                break;
349
            case EnvVariableTypeEnum::SHARED_SECRET:
350
                $this->addSharedSecret($key, $value);
351
                break;
352
            case EnvVariableTypeEnum::IMAGE_ENV_VARIABLE:
353
                $this->addImageEnvVariable($key, $value);
354
                break;
355
            case EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE:
356
                $this->addContainerEnvVariable($key, $value);
357
                break;
358
            default:
359
                throw ServiceException::unknownEnvVariableType($type);
360
        }
361
    }
362
363
    public function addSharedEnvVariable(string $key, string $value): void
364
    {
365
        $this->environment[$key] = new EnvVariable($value, 'sharedEnvVariable');
366
    }
367
368
    public function addSharedSecret(string $key, string $value): void
369
    {
370
        $this->environment[$key] = new EnvVariable($value, 'sharedSecret');
371
    }
372
373
374
    /************************ volumes adders **********************/
375
376
    public function addImageEnvVariable(string $key, string $value): void
377
    {
378
        $this->environment[$key] = new EnvVariable($value, 'imageEnvVariable');
379
    }
380
381
    public function addContainerEnvVariable(string $key, string $value): void
382
    {
383
        $this->environment[$key] = new EnvVariable($value, 'containerEnvVariable');
384
    }
385
386
    /** @throws ServiceException */
387
    private function addVolume(string $type, string $source, string $target = '', bool $readOnly = false): void
388
    {
389
        switch ($type) {
390
            case VolumeTypeEnum::NAMED_VOLUME:
391
                $this->addNamedVolume($source, $target, $readOnly);
392
                break;
393
            case VolumeTypeEnum::BIND_VOLUME:
394
                $this->addBindVolume($source, $target, $readOnly);
395
                break;
396
            case VolumeTypeEnum::TMPFS_VOLUME:
397
                $this->addTmpfsVolume($source);
398
                break;
399
            default:
400
                throw ServiceException::unknownVolumeType($type);
401
        }
402
    }
403
404
    public function addNamedVolume(string $source, string $target, bool $readOnly = false): void
405
    {
406
        $this->volumes[] = new NamedVolume($source, $target, $readOnly);
407
    }
408
409
    public function addBindVolume(string $source, string $target, bool $readOnly = false): void
410
    {
411
        $this->volumes[] = new BindVolume($source, $target, $readOnly);
412
    }
413
414
    public function addTmpfsVolume(string $source): void
415
    {
416
        $this->volumes[] = new TmpfsVolume($source);
417
    }
418
419
    public function addDockerfileCommand(string $dockerfileCommand): void
420
    {
421
        $this->dockerfileCommands[] = $dockerfileCommand;
422
    }
423
424
425
    /************************ forSpecificEnvTypes stuffs **********************/
426
427
    public function addDestEnvType(string $envType, bool $keepTheOtherEnvTypes = true): void
428
    {
429
        if (!$keepTheOtherEnvTypes) {
430
            $this->destEnvTypes = [];
431
        }
432
        $this->destEnvTypes[] = $envType;
433
    }
434
435
    public function isForDevEnvType(): bool
436
    {
437
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_DEV, $this->destEnvTypes);
438
    }
439
440
    public function isForTestEnvType(): bool
441
    {
442
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_TEST, $this->destEnvTypes);
443
    }
444
445
    public function isForProdEnvType(): bool
446
    {
447
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_PROD, $this->destEnvTypes);
448
    }
449
450
    public function isForMyEnvType(): bool
451
    {
452
        $myEnvType = Manifest::getMetadata(CommonMetadata::ENV_TYPE_KEY);
453
        return empty($this->destEnvTypes) || \in_array($myEnvType, $this->destEnvTypes, true);
454
    }
455
}
456