7
7
use App \Models \NodeTasks \CreateConfig \CreateConfigMeta ;
8
8
use App \Models \NodeTasks \CreateSecret \CreateSecretMeta ;
9
9
use App \Models \NodeTasks \CreateService \CreateServiceMeta ;
10
+ use App \Models \NodeTasks \DeleteService \DeleteServiceMeta ;
10
11
use App \Models \NodeTasks \PullDockerImage \PullDockerImageMeta ;
11
12
use App \Models \NodeTasks \UpdateService \UpdateServiceMeta ;
12
13
use App \Models \NodeTaskType ;
13
14
use App \Rules \RequiredIfArrayHas ;
15
+ use Illuminate \Support \Str ;
14
16
use Spatie \LaravelData \Attributes \DataCollectionOf ;
15
17
use Spatie \LaravelData \Attributes \Validation \Enum ;
16
18
use Spatie \LaravelData \Attributes \Validation \Rule ;
@@ -25,6 +27,9 @@ public function __construct(
25
27
public string $ dockerImage ,
26
28
public ReleaseCommand $ releaseCommand ,
27
29
public ?string $ command ,
30
+ #[DataCollectionOf(Worker::class)]
31
+ /* @var Worker[] */
32
+ public array $ workers ,
28
33
#[Enum(LaunchMode::class)]
29
34
public string $ launchMode ,
30
35
#[DataCollectionOf(EnvVar::class)]
@@ -74,6 +79,26 @@ public function asNodeTasks(Deployment $deployment): array
74
79
75
80
$ tasks = [];
76
81
82
+ $ previousWorkers = $ previous ?->workers ?? [];
83
+ foreach ($ previousWorkers as $ worker ) {
84
+ if ($ this ->findWorker ($ worker ->dockerName ) === null ) {
85
+ $ tasks [] = [
86
+ 'type ' => NodeTaskType::DeleteService,
87
+ 'meta ' => new DeleteServiceMeta ($ deployment ->service_id , $ worker ->dockerName , $ deployment ->service ->name ),
88
+ 'payload ' => [
89
+ 'ServiceName ' => $ worker ->dockerName ,
90
+ ],
91
+ ];
92
+ }
93
+ }
94
+
95
+ foreach ($ this ->workers as $ worker ) {
96
+ if (!$ worker ->dockerName ) {
97
+ // TODO: add validation - allow only unique worker commands
98
+ $ worker ->dockerName = $ this ->makeResourceName ('wkr_ ' . $ worker ->name );
99
+ }
100
+ }
101
+
77
102
foreach ($ this ->configFiles as $ configFile ) {
78
103
$ previousConfig = $ previous ?->findConfigFile($ configFile ->path );
79
104
if ($ previousConfig && $ configFile ->sameAs ($ previousConfig )) {
@@ -174,13 +199,15 @@ public function asNodeTasks(Deployment $deployment): array
174
199
175
200
$ serviceTaskMeta = [
176
201
'deploymentId ' => $ deployment ->id ,
177
- 'processName ' => $ this ->dockerName ,
202
+ 'dockerName ' => $ this ->dockerName ,
178
203
'serviceId ' => $ deployment ->service_id ,
179
204
'serviceName ' => $ deployment ->service ->name ,
180
205
];
181
206
182
207
// FIXME: this is going to work wrong if the initial deployment is pending.
183
- $ actionUpdate = $ deployment ->service ->tasks ()->ofType (NodeTaskType::CreateService)->completed ()->exists ();
208
+ // Don't allow to schedule deployments if the service has not been created yet?
209
+ // This code is duplicated in the next block
210
+ $ actionUpdate = $ deployment ->service ->tasks ()->ofType (NodeTaskType::CreateService)->where ('meta__docker_name ' , $ this ->dockerName )->completed ()->exists ();
184
211
185
212
$ tasks [] = [
186
213
'type ' => $ actionUpdate ? NodeTaskType::UpdateService : NodeTaskType::CreateService,
@@ -261,6 +288,83 @@ public function asNodeTasks(Deployment $deployment): array
261
288
],
262
289
];
263
290
291
+ foreach ($ this ->workers as $ worker ) {
292
+ $ actionUpdate = $ deployment ->service ->tasks ()->ofType (NodeTaskType::CreateService)->where ('meta__docker_name ' , $ worker ->dockerName )->completed ()->exists ();
293
+
294
+ $ workerTaskMeta = [
295
+ ...$ serviceTaskMeta ,
296
+ 'dockerName ' => $ worker ->dockerName ,
297
+ ];
298
+
299
+ $ tasks [] = [
300
+ 'type ' => $ actionUpdate ? NodeTaskType::UpdateService : NodeTaskType::CreateService,
301
+ 'meta ' => $ actionUpdate ? UpdateServiceMeta::from ($ workerTaskMeta ) : CreateServiceMeta::from ($ workerTaskMeta ),
302
+ 'payload ' => [
303
+ 'AuthConfigName ' => $ this ->dockerRegistry ,
304
+ 'ReleaseCommand ' => (object ) [],
305
+ 'SecretVars ' => (object ) $ this ->getWorkerSecretVars ($ worker , $ labels ),
306
+ 'SwarmServiceSpec ' => [
307
+ 'Name ' => $ worker ->dockerName ,
308
+ 'Labels ' => $ labels ,
309
+ 'TaskTemplate ' => [
310
+ 'ContainerSpec ' => [
311
+ 'Image ' => $ this ->dockerImage ,
312
+ 'Labels ' => $ labels ,
313
+ // 'Command' => ['sh -c "' . Str::replace('"', '\\"', $worker->command) . '"'],
314
+ 'Command ' => ['sh ' , '-c ' ],
315
+ 'Args ' => [
316
+ $ worker ->command ,
317
+ ],
318
+ 'Hostname ' => "dpl- {$ deployment ->id }. {$ worker ->name }. {$ internalDomain }" ,
319
+ 'Env ' => collect ($ this ->envVars )->map (fn (EnvVar $ var ) => "{$ var ->name }= {$ var ->value }" )->toArray (),
320
+ 'Mounts ' => [],
321
+ 'Hosts ' => [
322
+ "{$ worker ->name }. {$ internalDomain }" ,
323
+ ],
324
+ 'Secrets ' => collect ($ this ->secretFiles )->map (fn (ConfigFile $ secretFile ) => [
325
+ 'File ' => [
326
+ 'Name ' => $ secretFile ->path ,
327
+ // TODO: figure out better permissions settings (if any)
328
+ 'UID ' => "0 " ,
329
+ "GID " => "0 " ,
330
+ "Mode " => 0777
331
+ ],
332
+ 'SecretName ' => $ secretFile ->dockerName ,
333
+ ])->values ()->toArray (),
334
+ 'Configs ' => collect ($ this ->configFiles )->map (fn (ConfigFile $ configFile ) => [
335
+ 'File ' => [
336
+ 'Name ' => $ configFile ->path ,
337
+ // TODO: figure out better permissions settings (if any)
338
+ 'UID ' => "0 " ,
339
+ "GID " => "0 " ,
340
+ "Mode " => 0777
341
+ ],
342
+ 'ConfigName ' => $ configFile ->dockerName ,
343
+ ])->values ()->toArray (),
344
+ 'Placement ' => [],
345
+ ],
346
+ 'Networks ' => [
347
+ [
348
+ 'Target ' => $ deployment ->data ->networkName ,
349
+ 'Aliases ' => [
350
+ "{$ worker ->name }. {$ internalDomain }" ,
351
+ ],
352
+ ]
353
+ ],
354
+ ],
355
+ 'Mode ' => [
356
+ 'Replicated ' => [
357
+ 'Replicas ' => $ worker ->replicas ,
358
+ ],
359
+ ],
360
+ 'EndpointSpec ' => [
361
+ 'Ports ' => []
362
+ ]
363
+ ]
364
+ ],
365
+ ];
366
+ }
367
+
264
368
return $ tasks ;
265
369
}
266
370
@@ -295,6 +399,24 @@ protected function getSecretVars(?Process $previous, $labels): array|object
295
399
return $ data ;
296
400
}
297
401
402
+ private function getWorkerSecretVars (Worker $ worker , array $ labels ): array |object
403
+ {
404
+ if (empty ($ this ->data ->secretVars ->vars )) {
405
+ return (object ) [];
406
+ }
407
+
408
+ return [
409
+ 'ConfigName ' => $ this ->data ->secretVars ->dockerName . '_wkr_ ' . $ worker ->name ,
410
+ 'ConfigLabels ' => dockerize_labels ([
411
+ ...$ labels ,
412
+ 'kind ' => 'secret-env-vars ' ,
413
+ ]),
414
+ 'Values ' => [],
415
+ 'Preserve ' => collect ($ this ->data ->secretVars ->vars )->map (fn (EnvVar $ var ) => $ var ->name )->toArray (),
416
+ 'PreserveFromConfig ' => $ this ->secretVars ->dockerName ,
417
+ ];
418
+ }
419
+
298
420
299
421
public function makeResourceName (string $ name ): string
300
422
{
@@ -322,4 +444,13 @@ private function getReleaseCommandPayload(Deployment $deployment, array $labels)
322
444
'Command ' => $ this ->releaseCommand ->command ,
323
445
];
324
446
}
447
+
448
+ private function findWorker (?string $ dockerName ): ?Worker
449
+ {
450
+ if (!$ dockerName ) {
451
+ return null ;
452
+ }
453
+
454
+ return collect ($ this ->workers )->first (fn (Worker $ worker ) => $ worker ->dockerName === $ dockerName );
455
+ }
325
456
}
0 commit comments