Completed
Pull Request — master (#36)
by Jindun
12:33
created

Service::getLimitMemory()   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
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 $requestMemory = '';
48
    /** @var string */
49
    private $requestCpu = '';
50
    /** @var string */
51
    private $limitMemory = '';
52
    /** @var string */
53
    private $limitCpu = '';
54
55
    /** @var string[] */
56
    private $destEnvTypes = []; // empty === all env types
57
58
    /**
59
     * Service constructor.
60
     * @throws ServiceException
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
        $service->dockerfileCommands = $payload['dockerfileCommands'] ?? '';
0 ignored issues
show
Documentation Bug introduced by
It seems like $payload['dockerfileCommands'] ?? '' can also be of type string. However, the property $dockerfileCommands is declared as type string[]. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
101
102
        $service->requestMemory = $payload['requestMemory'] ?? '';
103
        $service->requestCpu = $payload['requestCpu'] ?? '';
104
        $service->limitMemory = $payload['limitMemory'] ?? '';
105
        $service->limitCpu = $payload['limitCpu'] ?? '';
106
        return $service;
107
    }
108
109
    /**
110
     * Specify data which should be serialized to JSON
111
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
112
     * @return array data which can be serialized by <b>json_encode</b>,
113
     * which is a value of any type other than a resource.
114
     * @since 5.4.0
115
     * @throws ServiceException
116
     */
117
    public function jsonSerialize(): array
118
    {
119
        $jsonSerializeMap = function (JsonSerializable $obj): array {
120
            return $obj->jsonSerialize();
121
        };
122
123
        $json = array(
124
            'serviceName' => $this->serviceName,
125
        );
126
127
        $service = array_filter([
128
            'image' => $this->image,
129
            'command' => $this->command,
130
            'internalPorts' => $this->internalPorts,
131
            'dependsOn' => $this->dependsOn,
132
            'ports' => $this->ports,
133
            'labels' => $this->labels,
134
            'environment' => array_map($jsonSerializeMap, $this->environment),
135
            'volumes' => array_map($jsonSerializeMap, $this->volumes),
136
            'needVirtualHost' => $this->needVirtualHost,
137
            'needBuild' => $this->needBuild,
138
        ]);
139
140
        if (!empty($service)) {
141
            $json['service'] = $service;
142
        }
143
144
        if (!empty($this->dockerfileCommands)) {
145
            $json['dockerfileCommands'] = $this->dockerfileCommands;
146
        }
147
148
        $json['destEnvTypes'] = $this->destEnvTypes;
149
150
        $resources = array_filter([
151
            'requestMemory' => $this->requestMemory,
152
            'requestCpu' => $this->requestCpu,
153
            'limitMemory' => $this->limitMemory,
154
            'limitCpu' => $this->limitCpu
155
        ]);
156
157
        if (!empty($resources)) {
158
            $json = array_merge($json, $resources);
159
        }
160
161
        $this->checkValidity($json);
162
        return $json;
163
    }
164
165
    /** @return mixed[] */
166
    public function imageJsonSerialize(): array
167
    {
168
        $dockerfileCommands = [];
169
        $dockerfileCommands[] = 'FROM ' . $this->image;
170
        foreach ($this->environment as $key => $env) {
171
            if ($env->getType() === EnvVariableTypeEnum::IMAGE_ENV_VARIABLE) {
172
                $dockerfileCommands[] = "ENV $key" . '=' . $env->getValue();
173
            }
174
        }
175
        foreach ($this->volumes as $volume) {
176
            if ($volume->getType() === VolumeTypeEnum::BIND_VOLUME) {
177
                $dockerfileCommands[] = 'COPY ' . $volume->getSource() . ' ' . $volume->getTarget();
178
            }
179
        }
180
181
        if (!empty($this->command)) {
182
            $dockerfileCommands[] = 'CMD ' . implode(' ', $this->command);
183
        }
184
185
        $dockerfileCommands = array_merge($dockerfileCommands, $this->dockerfileCommands);
186
187
        return [
188
            'serviceName' => $this->serviceName,
189
            'dockerfileCommands' => $dockerfileCommands,
190
            'destEnvTypes' => $this->destEnvTypes,
191
        ];
192
    }
193
194
    /**
195
     * @param \stdClass|array|string $data
196
     * @return bool
197
     * @throws ServiceException
198
     */
199
    private function checkValidity($data): bool
200
    {
201
        if (\is_array($data)) {
202
            $data = \GuzzleHttp\json_decode(\GuzzleHttp\json_encode($data), false);
203
        }
204
        $validator = new Validator();
205
        $result = $validator->dataValidation($data, $this->validatorSchema);
206
        if (!$result->isValid()) {
207
            /** @var ValidationError $vError */
208
            $vError = $result->getFirstError();
209
            throw ServiceException::invalidServiceData($vError);
210
        }
211
        return $result->isValid();
212
    }
213
214
215
    /************************ getters **********************/
216
217
    public function getServiceName(): string
218
    {
219
        return $this->serviceName;
220
    }
221
222
    public function getImage(): ?string
223
    {
224
        return $this->image;
225
    }
226
227
    /** @return string[] */
228
    public function getCommand(): array
229
    {
230
        return $this->command;
231
    }
232
233
    /** @return int[] */
234
    public function getInternalPorts(): array
235
    {
236
        return $this->internalPorts;
237
    }
238
239
    /** @return string[] */
240
    public function getDependsOn(): array
241
    {
242
        return $this->dependsOn;
243
    }
244
245
    /** @return mixed[] */
246
    public function getPorts(): array
247
    {
248
        return $this->ports;
249
    }
250
251
    /** @return mixed[] */
252
    public function getLabels(): array
253
    {
254
        return $this->labels;
255
    }
256
257
    /** @return mixed[] */
258
    public function getEnvironment(): array
259
    {
260
        return $this->environment;
261
    }
262
263
    /** @return mixed[] */
264
    public function getVolumes(): array
265
    {
266
        return $this->volumes;
267
    }
268
269
    /** @return string[] */
270
    public function getNeedVirtualHost(): ?bool
271
    {
272
        return $this->needVirtualHost;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->needVirtualHost also could return the type boolean which is incompatible with the documented return type string[].
Loading history...
273
    }
274
275
    public function getNeedBuild(): ?bool
276
    {
277
        return $this->needBuild;
278
    }
279
280
    /** @return string[] */
281
    public function getDockerfileCommands(): array
282
    {
283
        return $this->dockerfileCommands;
284
    }
285
286
    public function getRequestMemory(): string
287
    {
288
        return $this->requestMemory;
289
    }
290
291
    public function getRequestCpu(): string
292
    {
293
        return $this->requestCpu;
294
    }
295
296
    public function getLimitMemory(): string
297
    {
298
        return $this->limitMemory;
299
    }
300
301
    public function getLimitCpu(): string
302
    {
303
        return $this->limitCpu;
304
    }
305
306
    /**  @return string[] */
307
    public function getDestEnvTypes(): array
308
    {
309
        return $this->destEnvTypes;
310
    }
311
312
313
    /************************ setters **********************/
314
315
    public function setServiceName(string $serviceName): void
316
    {
317
        $this->serviceName = $serviceName;
318
    }
319
320
    public function setImage(?string $image): void
321
    {
322
        $this->image = $image;
323
    }
324
325
    /** @param string[] $command */
326
    public function setCommand(array $command): void
327
    {
328
        $this->command = $command;
329
    }
330
331
    /** @param int[] $internalPorts */
332
    public function setInternalPorts(array $internalPorts): void
333
    {
334
        $this->internalPorts = $internalPorts;
335
    }
336
337
    /** @param string[] $dependsOn */
338
    public function setDependsOn(array $dependsOn): void
339
    {
340
        $this->dependsOn = $dependsOn;
341
    }
342
343
    public function setRequestMemory(string $requestMemory): void
344
    {
345
        $this->requestMemory = $requestMemory;
346
    }
347
348
    public function setRequestCpu(string $requestCpu): void
349
    {
350
        $this->requestCpu = $requestCpu;
351
    }
352
353
    public function setLimitMemory(string $limitMemory): void
354
    {
355
        $this->limitMemory = $limitMemory;
356
    }
357
358
    public function setLimitCpu(string $limitCpu): void
359
    {
360
        $this->limitCpu = $limitCpu;
361
    }
362
363
    public function setNeedVirtualHost(?bool $needVirtualHost): void
364
    {
365
        $this->needVirtualHost = $needVirtualHost;
366
    }
367
368
    public function setNeedBuild(?bool $needBuild): void
369
    {
370
        $this->needBuild = $needBuild;
371
    }
372
373
374
    /************************ adders **********************/
375
376
    public function addCommand(string $command): void
377
    {
378
        $this->command[] = $command;
379
    }
380
381
    public function addInternalPort(int $internalPort): void
382
    {
383
        $this->internalPorts[] = $internalPort;
384
    }
385
386
    public function addDependsOn(string $dependsOn): void
387
    {
388
        $this->dependsOn[] = $dependsOn;
389
    }
390
391
    public function addPort(int $source, int $target): void
392
    {
393
        $this->ports[] = array(
394
            'source' => $source,
395
            'target' => $target,
396
        );
397
    }
398
399
    public function addLabel(string $key, string $value): void
400
    {
401
        $this->labels[$key] = array(
402
            'value' => $value,
403
        );
404
    }
405
406
407
    /************************ environment adders **********************/
408
409
    /** @throws ServiceException */
410
    private function addEnvVar(string $key, string $value, string $type): void
411
    {
412
        switch ($type) {
413
            case EnvVariableTypeEnum::SHARED_ENV_VARIABLE:
414
                $this->addSharedEnvVariable($key, $value);
415
                break;
416
            case EnvVariableTypeEnum::SHARED_SECRET:
417
                $this->addSharedSecret($key, $value);
418
                break;
419
            case EnvVariableTypeEnum::IMAGE_ENV_VARIABLE:
420
                $this->addImageEnvVariable($key, $value);
421
                break;
422
            case EnvVariableTypeEnum::CONTAINER_ENV_VARIABLE:
423
                $this->addContainerEnvVariable($key, $value);
424
                break;
425
            default:
426
                throw ServiceException::unknownEnvVariableType($type);
427
        }
428
    }
429
430
    public function addSharedEnvVariable(string $key, string $value): void
431
    {
432
        $this->environment[$key] = new EnvVariable($value, 'sharedEnvVariable');
433
    }
434
435
    public function addSharedSecret(string $key, string $value): void
436
    {
437
        $this->environment[$key] = new EnvVariable($value, 'sharedSecret');
438
    }
439
440
441
    /************************ volumes adders **********************/
442
443
    public function addImageEnvVariable(string $key, string $value): void
444
    {
445
        $this->environment[$key] = new EnvVariable($value, 'imageEnvVariable');
446
    }
447
448
    public function addContainerEnvVariable(string $key, string $value): void
449
    {
450
        $this->environment[$key] = new EnvVariable($value, 'containerEnvVariable');
451
    }
452
453
    /** @throws ServiceException */
454
    private function addVolume(string $type, string $source, string $target = '', bool $readOnly = false): void
455
    {
456
        switch ($type) {
457
            case VolumeTypeEnum::NAMED_VOLUME:
458
                $this->addNamedVolume($source, $target, $readOnly);
459
                break;
460
            case VolumeTypeEnum::BIND_VOLUME:
461
                $this->addBindVolume($source, $target, $readOnly);
462
                break;
463
            case VolumeTypeEnum::TMPFS_VOLUME:
464
                $this->addTmpfsVolume($source);
465
                break;
466
            default:
467
                throw ServiceException::unknownVolumeType($type);
468
        }
469
    }
470
471
    public function addNamedVolume(string $source, string $target, bool $readOnly = false): void
472
    {
473
        $this->volumes[] = new NamedVolume($source, $target, $readOnly);
474
    }
475
476
    public function addBindVolume(string $source, string $target, bool $readOnly = false): void
477
    {
478
        $this->volumes[] = new BindVolume($source, $target, $readOnly);
479
    }
480
481
    public function addTmpfsVolume(string $source): void
482
    {
483
        $this->volumes[] = new TmpfsVolume($source);
484
    }
485
486
    public function addDockerfileCommand(string $dockerfileCommand): void
487
    {
488
        $this->dockerfileCommands[] = $dockerfileCommand;
489
    }
490
491
492
    /************************ forSpecificEnvTypes stuffs **********************/
493
494
    public function addDestEnvType(string $envType, bool $keepTheOtherEnvTypes = true): void
495
    {
496
        if (!$keepTheOtherEnvTypes) {
497
            $this->destEnvTypes = [];
498
        }
499
        $this->destEnvTypes[] = $envType;
500
    }
501
502
    public function isForDevEnvType(): bool
503
    {
504
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_DEV, $this->destEnvTypes);
505
    }
506
507
    public function isForTestEnvType(): bool
508
    {
509
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_TEST, $this->destEnvTypes);
510
    }
511
512
    public function isForProdEnvType(): bool
513
    {
514
        return empty($this->destEnvTypes) || \in_array(CommonMetadata::ENV_TYPE_PROD, $this->destEnvTypes);
515
    }
516
517
    public function isForMyEnvType(): bool
518
    {
519
        $myEnvType = Manifest::getMetadata(CommonMetadata::ENV_TYPE_KEY);
520
        return empty($this->destEnvTypes) || \in_array($myEnvType, $this->destEnvTypes, true);
521
    }
522
}
523