Completed
Pull Request — master (#73)
by Jindun
03:51
created

Service::containsContainerEnvVariable()   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 0
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
use TheAentMachine\Service\Volume\Volume;
18
19
class Service implements JsonSerializable
20
{
21
    /** @var string */
22
    private $serviceName = '';
23
    /** @var string|null */
24
    private $image;
25
    /** @var string[] */
26
    private $command = [];
27
    /** @var int[] */
28
    private $internalPorts = [];
29
    /** @var string[] */
30
    private $dependsOn = [];
31
    /** @var mixed[] */
32
    private $ports = [];
33
    /** @var mixed[] */
34
    private $labels = [];
35
    /** @var mixed[] */
36
    private $environment = [];
37
    /** @var mixed[] */
38
    private $volumes = [];
39
    /** @var null|bool */
40
    private $needVirtualHost;
41
    /** @var null|bool */
42
    private $needBuild;
43
    /** @var \stdClass */
44
    private $validatorSchema;
45
    /** @var string[] */
46
    private $dockerfileCommands = [];
47
    /** @var string */
48
    private $requestMemory = '';
49
    /** @var string */
50
    private $requestCpu = '';
51
    /** @var string */
52
    private $limitMemory = '';
53
    /** @var string */
54
    private $limitCpu = '';
55
56
    /** @var string[] */
57
    private $destEnvTypes = []; // empty === all env types
58
59
    /**
60
     * Service constructor.
61
     */
62
    public function __construct()
63
    {
64
        $this->validatorSchema = \GuzzleHttp\json_decode((string)file_get_contents(__DIR__ . '/ServiceJsonSchema.json'), false);
65
    }
66
67
    /**
68
     * @param mixed[] $payload
69
     * @return Service
70
     * @throws ServiceException
71
     */
72
    public static function parsePayload(array $payload): Service
73
    {
74
        $service = new self();
75
        $service->checkValidity($payload);
76
        $service->serviceName = $payload['serviceName'] ?? '';
77
        $s = $payload['service'] ?? [];
78
        if (!empty($s)) {
79
            $service->image = $s['image'] ?? null;
80
            $service->command = $s['command'] ?? [];
81
            $service->internalPorts = $s['internalPorts'] ?? [];
82
            $service->dependsOn = $s['dependsOn'] ?? [];
83
            $service->ports = $s['ports'] ?? [];
84
            $service->labels = $s['labels'] ?? [];
85
            if (!empty($s['environment'])) {
86
                foreach ($s['environment'] as $key => $env) {
87
                    $service->addEnvVar($key, $env['value'], $env['type']);
88
                }
89
            }
90
            if (!empty($s['volumes'])) {
91
                foreach ($s['volumes'] as $vol) {
92
                    $service->addVolume($vol['type'], $vol['source'], $vol['target'] ?? '', $vol['readOnly'] ?? false);
93
                }
94
            }
95
            $service->needVirtualHost = $s['needVirtualHost'] ?? null;
96
            $service->needBuild = $s['needBuild'] ?? null;
97
        }
98
        $service->dockerfileCommands = $payload['dockerfileCommands'] ?? [];
99
        $service->destEnvTypes = $payload['destEnvTypes'] ?? [];
100
101
        $service->requestMemory = $payload['requestMemory'] ?? '';
102
        $service->requestCpu = $payload['requestCpu'] ?? '';
103
        $service->limitMemory = $payload['limitMemory'] ?? '';
104
        $service->limitCpu = $payload['limitCpu'] ?? '';
105
        return $service;
106
    }
107
108
    /**
109
     * Specify data which should be serialized to JSON
110
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
111
     * @return array data which can be serialized by <b>json_encode</b>,
112
     * which is a value of any type other than a resource.
113
     * @since 5.4.0
114
     * @throws ServiceException
115
     */
116
    public function jsonSerialize(): array
117
    {
118
        $jsonSerializeMap = function (JsonSerializable $obj): array {
119
            return $obj->jsonSerialize();
120
        };
121
122
        $json = array(
123
            'serviceName' => $this->serviceName,
124
        );
125
126
        $service = array_filter([
127
            'image' => $this->image,
128
            'command' => $this->command,
129
            'internalPorts' => $this->internalPorts,
130
            'dependsOn' => $this->dependsOn,
131
            'ports' => $this->ports,
132
            'labels' => $this->labels,
133
            'environment' => array_map($jsonSerializeMap, $this->environment),
134
            'volumes' => array_map($jsonSerializeMap, $this->volumes),
135
            'needVirtualHost' => $this->needVirtualHost,
136
            'needBuild' => $this->needBuild,
137
        ]);
138
139
        if (!empty($service)) {
140
            $json['service'] = $service;
141
        }
142
143
        if (!empty($this->dockerfileCommands)) {
144
            $json['dockerfileCommands'] = $this->dockerfileCommands;
145
        }
146
147
        $json['destEnvTypes'] = $this->destEnvTypes;
148
149
        $resources = array_filter([
150
            'requestMemory' => $this->requestMemory,
151
            'requestCpu' => $this->requestCpu,
152
            'limitMemory' => $this->limitMemory,
153
            'limitCpu' => $this->limitCpu
154
        ]);
155
156
        if (!empty($resources)) {
157
            $json = array_merge($json, $resources);
158
        }
159
160
        $this->checkValidity($json);
161
        return $json;
162
    }
163
164
    /** @return mixed[] */
165
    public function imageJsonSerialize(): array
166
    {
167
        $dockerfileCommands = [];
168
        $dockerfileCommands[] = 'FROM ' . $this->image;
169
        foreach ($this->environment as $key => $env) {
170
            if ($env->getType() === EnvVariableTypeEnum::IMAGE_ENV_VARIABLE) {
171
                $dockerfileCommands[] = "ENV $key" . '=' . $env->getValue();
172
            }
173
        }
174
        foreach ($this->volumes as $volume) {
175
            if ($volume->getType() === VolumeTypeEnum::BIND_VOLUME) {
176
                $dockerfileCommands[] = 'COPY ' . $volume->getSource() . ' ' . $volume->getTarget();
177
            }
178
        }
179
180
        if (!empty($this->command)) {
181
            $dockerfileCommands[] = 'CMD ' . implode(' ', $this->command);
182
        }
183
184
        $dockerfileCommands = array_merge($dockerfileCommands, $this->dockerfileCommands);
185
186
        return [
187
            'serviceName' => $this->serviceName,
188
            'dockerfileCommands' => $dockerfileCommands,
189
            'destEnvTypes' => $this->destEnvTypes,
190
        ];
191
    }
192
193
    /**
194
     * @param \stdClass|array|string $data
195
     * @return bool
196
     * @throws ServiceException
197
     */
198
    private function checkValidity($data): bool
199
    {
200
        if (\is_array($data)) {
201
            $data = \GuzzleHttp\json_decode(\GuzzleHttp\json_encode($data), false);
202
        }
203
        $validator = new Validator();
204
        $result = $validator->dataValidation($data, $this->validatorSchema);
205
        if (!$result->isValid()) {
206
            /** @var ValidationError $vError */
207
            $vError = $result->getFirstError();
208
            throw ServiceException::invalidServiceData($vError);
209
        }
210
        return $result->isValid();
211
    }
212
213
214
    /************************ getters **********************/
215
216
    public function getServiceName(): string
217
    {
218
        return $this->serviceName;
219
    }
220
221
    public function getImage(): ?string
222
    {
223
        return $this->image;
224
    }
225
226
    /** @return string[] */
227
    public function getCommand(): array
228
    {
229
        return $this->command;
230
    }
231
232
    /** @return int[] */
233
    public function getInternalPorts(): array
234
    {
235
        return $this->internalPorts;
236
    }
237
238
    /** @return string[] */
239
    public function getDependsOn(): array
240
    {
241
        return $this->dependsOn;
242
    }
243
244
    /** @return mixed[] */
245
    public function getPorts(): array
246
    {
247
        return $this->ports;
248
    }
249
250
    /** @return mixed[] */
251
    public function getLabels(): array
252
    {
253
        return $this->labels;
254
    }
255
256
    /** @return mixed[] */
257
    public function getEnvironment(): array
258
    {
259
        return $this->environment;
260
    }
261
262
    /** @return mixed[] */
263
    public function getVolumes(): array
264
    {
265
        return $this->volumes;
266
    }
267
268
    public function getNeedVirtualHost(): ?bool
269
    {
270
        return $this->needVirtualHost;
271
    }
272
273
    public function getNeedBuild(): ?bool
274
    {
275
        return $this->needBuild;
276
    }
277
278
    /** @return string[] */
279
    public function getDockerfileCommands(): array
280
    {
281
        return $this->dockerfileCommands;
282
    }
283
284
    public function getRequestMemory(): string
285
    {
286
        return $this->requestMemory;
287
    }
288
289
    public function getRequestCpu(): string
290
    {
291
        return $this->requestCpu;
292
    }
293
294
    public function getLimitMemory(): string
295
    {
296
        return $this->limitMemory;
297
    }
298
299
    public function getLimitCpu(): string
300
    {
301
        return $this->limitCpu;
302
    }
303
304
    /**  @return string[] */
305
    public function getDestEnvTypes(): array
306
    {
307
        return $this->destEnvTypes;
308
    }
309
310
311
    /************************ setters **********************/
312
313
    public function setServiceName(string $serviceName): void
314
    {
315
        $this->serviceName = $serviceName;
316
    }
317
318
    public function setImage(?string $image): void
319
    {
320
        $this->image = $image;
321
    }
322
323
    /** @param string[] $command */
324
    public function setCommand(array $command): void
325
    {
326
        $this->command = $command;
327
    }
328
329
    /** @param int[] $internalPorts */
330
    public function setInternalPorts(array $internalPorts): void
331
    {
332
        $this->internalPorts = $internalPorts;
333
    }
334
335
    /** @param string[] $dependsOn */
336
    public function setDependsOn(array $dependsOn): void
337
    {
338
        $this->dependsOn = $dependsOn;
339
    }
340
341
    public function setRequestMemory(string $requestMemory): void
342
    {
343
        $this->requestMemory = $requestMemory;
344
    }
345
346
    public function setRequestCpu(string $requestCpu): void
347
    {
348
        $this->requestCpu = $requestCpu;
349
    }
350
351
    public function setLimitMemory(string $limitMemory): void
352
    {
353
        $this->limitMemory = $limitMemory;
354
    }
355
356
    public function setLimitCpu(string $limitCpu): void
357
    {
358
        $this->limitCpu = $limitCpu;
359
    }
360
361
    public function setNeedVirtualHost(?bool $needVirtualHost): void
362
    {
363
        $this->needVirtualHost = $needVirtualHost;
364
    }
365
366
    public function setNeedBuild(?bool $needBuild): void
367
    {
368
        $this->needBuild = $needBuild;
369
    }
370
371
372
    /************************ adders **********************/
373
374
    public function addCommand(string $command): void
375
    {
376
        $this->command[] = $command;
377
    }
378
379
    public function addInternalPort(int $internalPort): void
380
    {
381
        $this->internalPorts[] = $internalPort;
382
    }
383
384
    public function addDependsOn(string $dependsOn): void
385
    {
386
        $this->dependsOn[] = $dependsOn;
387
    }
388
389
    public function addPort(int $source, int $target): void
390
    {
391
        $this->ports[] = array(
392
            'source' => $source,
393
            'target' => $target,
394
        );
395
    }
396
397
    public function addLabel(string $key, string $value): void
398
    {
399
        $this->labels[$key] = array(
400
            'value' => $value,
401
        );
402
    }
403
404
405
    /************************ environment adders & contains **********************/
406
407
    /** @throws ServiceException */
408
    private function addEnvVar(string $key, string $value, string $type): void
409
    {
410
        switch ($type) {
411
            case EnvVariableTypeEnum::SHARED_ENV_VARIABLE:
412
                $this->addSharedEnvVariable($key, $value);
413
                break;
414
            case EnvVariableTypeEnum::SHARED_SECRET:
415
                $this->addSharedSecret($key, $value);
416
                break;
417
            case EnvVariableTypeEnum::IMAGE_ENV_VARIABLE:
418
                $this->addImageEnvVariable($key, $value);
419
                break;
420
            case EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE:
421
                $this->addContainerEnvVariable($key, $value);
422
                break;
423
            default:
424
                throw ServiceException::unknownEnvVariableType($type);
425
        }
426
    }
427
428
    public function addSharedEnvVariable(string $key, string $value): void
429
    {
430
        $this->environment[$key] = new EnvVariable($value, EnvVariableTypeEnum::SHARED_ENV_VARIABLE);
431
    }
432
433
    public function addSharedSecret(string $key, string $value): void
434
    {
435
        $this->environment[$key] = new EnvVariable($value, EnvVariableTypeEnum::SHARED_SECRET);
436
    }
437
438
    public function addImageEnvVariable(string $key, string $value): void
439
    {
440
        $this->environment[$key] = new EnvVariable($value, EnvVariableTypeEnum::IMAGE_ENV_VARIABLE);
441
    }
442
443
    public function addContainerEnvVariable(string $key, string $value): void
444
    {
445
        $this->environment[$key] = new EnvVariable($value, EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE);
446
    }
447
448
    private function containsEnvVariableByType(string $type): bool
449
    {
450
        /** @var EnvVariable $envVar */
451
        foreach ($this->environment as $key => $envVar) {
452
            if ($envVar->getType() === $type) {
453
                return true;
454
            }
455
        }
456
        return false;
457
    }
458
459
    public function containsSharedEnvVariable(): bool
460
    {
461
        return $this->containsEnvVariableByType(EnvVariableTypeEnum::SHARED_ENV_VARIABLE);
462
    }
463
464
    public function containsSharedSecret(): bool
465
    {
466
        return $this->containsEnvVariableByType(EnvVariableTypeEnum::SHARED_SECRET);
467
    }
468
469
    public function containsImageEnvVariable(): bool
470
    {
471
        return $this->containsEnvVariableByType(EnvVariableTypeEnum::IMAGE_ENV_VARIABLE);
472
    }
473
474
    public function containsContainerEnvVariable(): bool
475
    {
476
        return $this->containsEnvVariableByType(EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE);
477
    }
478
479
480
    /************************ volumes adders & removers **********************/
481
482
    /** @throws ServiceException */
483
    private function addVolume(string $type, string $source, string $target = '', bool $readOnly = false): void
484
    {
485
        switch ($type) {
486
            case VolumeTypeEnum::NAMED_VOLUME:
487
                $this->addNamedVolume($source, $target, $readOnly);
488
                break;
489
            case VolumeTypeEnum::BIND_VOLUME:
490
                $this->addBindVolume($source, $target, $readOnly);
491
                break;
492
            case VolumeTypeEnum::TMPFS_VOLUME:
493
                $this->addTmpfsVolume($source);
494
                break;
495
            default:
496
                throw ServiceException::unknownVolumeType($type);
497
        }
498
    }
499
500
    public function addNamedVolume(string $source, string $target, bool $readOnly = false): void
501
    {
502
        $this->volumes[] = new NamedVolume($source, $target, $readOnly);
503
    }
504
505
    public function addBindVolume(string $source, string $target, bool $readOnly = false): void
506
    {
507
        $this->volumes[] = new BindVolume($source, $target, $readOnly);
508
    }
509
510
    public function addTmpfsVolume(string $source): void
511
    {
512
        $this->volumes[] = new TmpfsVolume($source);
513
    }
514
515
    public function addDockerfileCommand(string $dockerfileCommand): void
516
    {
517
        $this->dockerfileCommands[] = $dockerfileCommand;
518
    }
519
520
    private function removeVolumesByType(string $type): void
521
    {
522
        $filterFunction = function (Volume $vol) use ($type) {
523
            return $vol->getType() !== $type;
524
        };
525
        $this->volumes = array_values(array_filter($this->volumes, $filterFunction));
526
    }
527
528
    public function removeAllBindVolumes(): void
529
    {
530
        $this->removeVolumesByType(VolumeTypeEnum::BIND_VOLUME);
531
    }
532
533
    public function removeAllNamedVolumes(): void
534
    {
535
        $this->removeVolumesByType(VolumeTypeEnum::NAMED_VOLUME);
536
    }
537
538
    public function removeAllTmpfsVolumes(): void
539
    {
540
        $this->removeVolumesByType(VolumeTypeEnum::TMPFS_VOLUME);
541
    }
542
543
    public function removeVolumesBySource(string $source): void
544
    {
545
        $filterFunction = function (Volume $vol) use ($source) {
546
            return $vol->getSource() !== $source;
547
        };
548
        $this->volumes = array_values(array_filter($this->volumes, $filterFunction));
549
    }
550
551
552
    /************************ destEnvTypes stuffs **********************/
553
554
    public function addDestEnvType(string $envType, bool $keepTheOtherEnvTypes = true): void
555
    {
556
        if (!$keepTheOtherEnvTypes) {
557
            $this->destEnvTypes = [];
558
        }
559
        $this->destEnvTypes[] = $envType;
560
    }
561
562
    public function isForDevEnvType(): bool
563
    {
564
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_DEV, $this->destEnvTypes);
565
    }
566
567
    public function isForTestEnvType(): bool
568
    {
569
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_TEST, $this->destEnvTypes);
570
    }
571
572
    public function isForProdEnvType(): bool
573
    {
574
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_PROD, $this->destEnvTypes);
575
    }
576
577
    public function isForMyEnvType(): bool
578
    {
579
        $myEnvType = Manifest::getMetadata(CommonMetadata::ENV_TYPE_KEY);
580
        return empty($this->destEnvTypes) || \in_array($myEnvType, $this->destEnvTypes, true);
581
    }
582
}
583