Completed
Pull Request — master (#54)
by Jindun
02:24
created

Service::addLabel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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