Completed
Pull Request — master (#75)
by Jindun
02:38
created

Service::getAllImageEnvVariable()   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
    /** @return array<string, EnvVariable> */
449
    private function getAllEnvVariablesByType(string $type): array
450
    {
451
        $res = [];
452
        /**
453
         * @var string $key
454
         * @var EnvVariable $envVar
455
         */
456
        foreach ($this->environment as $key => $envVar) {
457
            if ($envVar->getType() === $type) {
458
                $res[$key] = $envVar;
459
            }
460
        }
461
        return $res;
462
    }
463
464
    /** @return array<string, EnvVariable> */
465
    public function getAllSharedEnvVariable(): array
466
    {
467
        return $this->getAllEnvVariablesByType(EnvVariableTypeEnum::SHARED_ENV_VARIABLE);
468
    }
469
470
    /** @return array<string, EnvVariable> */
471
    public function getAllSharedSecret(): array
472
    {
473
        return $this->getAllEnvVariablesByType(EnvVariableTypeEnum::SHARED_SECRET);
474
    }
475
476
    /** @return array<string, EnvVariable> */
477
    public function getAllImageEnvVariable(): array
478
    {
479
        return $this->getAllEnvVariablesByType(EnvVariableTypeEnum::IMAGE_ENV_VARIABLE);
480
    }
481
482
    /** @return array<string, EnvVariable> */
483
    public function getAllContainerEnvVariable(): array
484
    {
485
        return $this->getAllEnvVariablesByType(EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE);
486
    }
487
488
489
    /************************ volumes adders & removers **********************/
490
491
    /** @throws ServiceException */
492
    private function addVolume(string $type, string $source, string $target = '', bool $readOnly = false): void
493
    {
494
        switch ($type) {
495
            case VolumeTypeEnum::NAMED_VOLUME:
496
                $this->addNamedVolume($source, $target, $readOnly);
497
                break;
498
            case VolumeTypeEnum::BIND_VOLUME:
499
                $this->addBindVolume($source, $target, $readOnly);
500
                break;
501
            case VolumeTypeEnum::TMPFS_VOLUME:
502
                $this->addTmpfsVolume($source);
503
                break;
504
            default:
505
                throw ServiceException::unknownVolumeType($type);
506
        }
507
    }
508
509
    public function addNamedVolume(string $source, string $target, bool $readOnly = false): void
510
    {
511
        $this->volumes[] = new NamedVolume($source, $target, $readOnly);
512
    }
513
514
    public function addBindVolume(string $source, string $target, bool $readOnly = false): void
515
    {
516
        $this->volumes[] = new BindVolume($source, $target, $readOnly);
517
    }
518
519
    public function addTmpfsVolume(string $source): void
520
    {
521
        $this->volumes[] = new TmpfsVolume($source);
522
    }
523
524
    public function addDockerfileCommand(string $dockerfileCommand): void
525
    {
526
        $this->dockerfileCommands[] = $dockerfileCommand;
527
    }
528
529
    private function removeVolumesByType(string $type): void
530
    {
531
        $filterFunction = function (Volume $vol) use ($type) {
532
            return $vol->getType() !== $type;
533
        };
534
        $this->volumes = array_values(array_filter($this->volumes, $filterFunction));
535
    }
536
537
    public function removeAllBindVolumes(): void
538
    {
539
        $this->removeVolumesByType(VolumeTypeEnum::BIND_VOLUME);
540
    }
541
542
    public function removeAllNamedVolumes(): void
543
    {
544
        $this->removeVolumesByType(VolumeTypeEnum::NAMED_VOLUME);
545
    }
546
547
    public function removeAllTmpfsVolumes(): void
548
    {
549
        $this->removeVolumesByType(VolumeTypeEnum::TMPFS_VOLUME);
550
    }
551
552
    public function removeVolumesBySource(string $source): void
553
    {
554
        $filterFunction = function (Volume $vol) use ($source) {
555
            return $vol->getSource() !== $source;
556
        };
557
        $this->volumes = array_values(array_filter($this->volumes, $filterFunction));
558
    }
559
560
561
    /************************ destEnvTypes stuffs **********************/
562
563
    public function addDestEnvType(string $envType, bool $keepTheOtherEnvTypes = true): void
564
    {
565
        if (!$keepTheOtherEnvTypes) {
566
            $this->destEnvTypes = [];
567
        }
568
        $this->destEnvTypes[] = $envType;
569
    }
570
571
    public function isForDevEnvType(): bool
572
    {
573
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_DEV, $this->destEnvTypes);
574
    }
575
576
    public function isForTestEnvType(): bool
577
    {
578
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_TEST, $this->destEnvTypes);
579
    }
580
581
    public function isForProdEnvType(): bool
582
    {
583
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_PROD, $this->destEnvTypes);
584
    }
585
586
    public function isForMyEnvType(): bool
587
    {
588
        $myEnvType = Manifest::getMetadata(CommonMetadata::ENV_TYPE_KEY);
589
        return empty($this->destEnvTypes) || \in_array($myEnvType, $this->destEnvTypes, true);
590
    }
591
}
592