@localctl/cli 0.5.0-beta.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +32 -82
  2. package/package.json +3 -10
  3. package/schema/app.schema.json +678 -44
  4. package/src/commands/doctor.js +1 -16
  5. package/src/commands/down.js +3 -1
  6. package/src/commands/generateManifests.js +4 -0
  7. package/src/commands/logs.js +1 -0
  8. package/src/commands/new.js +42 -20
  9. package/src/commands/prune.js +4 -0
  10. package/src/commands/reload.js +11 -3
  11. package/src/commands/secrets.js +3 -3
  12. package/src/commands/setup.js +1 -11
  13. package/src/commands/tiltConfig.js +11 -0
  14. package/src/commands/uninstall.js +5 -6
  15. package/src/commands/up.js +9 -1
  16. package/src/commands/validate.js +48 -0
  17. package/src/index.js +2 -0
  18. package/src/lib/addonDirs.js +1 -2
  19. package/src/lib/addonLoader.js +38 -17
  20. package/src/lib/addons/mongo/addon.yaml +2 -0
  21. package/src/lib/addons/postgres/addon.yaml +5 -1
  22. package/src/lib/addons/redis/addon.yaml +2 -0
  23. package/src/lib/appSecrets.js +107 -36
  24. package/src/lib/clusterProvision.js +45 -211
  25. package/src/lib/configSchema.js +412 -41
  26. package/src/lib/engine.js +3 -13
  27. package/src/lib/envFile.js +58 -0
  28. package/src/lib/exec.js +6 -7
  29. package/src/lib/hostGuard.js +107 -0
  30. package/src/lib/hostsFile.js +9 -8
  31. package/src/lib/kubeErrors.js +22 -0
  32. package/src/lib/loadConfig.js +183 -9
  33. package/src/lib/manifestGen.js +96 -32
  34. package/src/lib/namespaces.js +83 -0
  35. package/src/lib/pathGuard.js +79 -0
  36. package/src/lib/paths.js +2 -6
  37. package/src/lib/podman.js +3 -93
  38. package/src/lib/projectContext.js +2 -2
  39. package/src/lib/quantities.js +20 -0
  40. package/src/lib/safeRead.js +41 -0
  41. package/src/lib/tiltConfig.js +25 -0
  42. package/src/lib/toolInstall.js +2 -141
  43. package/src/lib/yamlLoad.js +48 -0
  44. package/templates/Tiltfile.tmpl +22 -14
  45. package/src/lib/linuxDistro.js +0 -122
@@ -2,61 +2,185 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "localctl app config",
4
4
  "type": "object",
5
- "required": ["name", "port", "build"],
5
+ "required": [
6
+ "name",
7
+ "port",
8
+ "build"
9
+ ],
6
10
  "additionalProperties": false,
7
11
  "properties": {
8
- "$schema": { "type": "string" },
12
+ "$schema": {
13
+ "type": "string"
14
+ },
9
15
  "name": {
10
16
  "type": "string",
11
- "pattern": "^[a-z0-9-]+$",
12
- "description": "Lowercase alphanumeric + dashes. Used as Deployment/Service name and Docker image name."
17
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
18
+ "description": "Lowercase alphanumeric + dashes. Used as Deployment/Service name and Docker image name.",
19
+ "maxLength": 63
13
20
  },
14
21
  "subdomain": {
15
22
  "type": "string",
16
- "pattern": "^[a-z0-9-]+$",
17
- "description": "Defaults to name. App is reachable at <subdomain>.local.test"
23
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
24
+ "description": "Defaults to name. App is reachable at <subdomain>.local.test",
25
+ "maxLength": 63
18
26
  },
19
27
  "namespace": {
20
28
  "type": "string",
21
- "description": "Defaults to name."
29
+ "description": "Defaults to name. Not default, kube-system, kube-public, kube-node-lease or observability. An existing namespace that localctl did not create is refused.",
30
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$",
31
+ "maxLength": 63
32
+ },
33
+ "port": {
34
+ "type": "integer",
35
+ "minimum": 1,
36
+ "maximum": 65535,
37
+ "description": "Port the app listens on inside the container."
22
38
  },
23
- "port": { "type": "integer", "description": "Port the app listens on inside the container." },
24
39
  "build": {
25
40
  "type": "object",
26
41
  "properties": {
27
- "context": { "type": "string", "default": "." },
28
- "dockerfile": { "type": "string", "default": "Dockerfile" }
42
+ "context": {
43
+ "default": ".",
44
+ "type": "string",
45
+ "minLength": 1,
46
+ "maxLength": 1000,
47
+ "pattern": "^(?![\\\\/~]|[A-Za-z]:)[^\\u0000-\\u001f\\u007f]*$",
48
+ "description": "Relative path inside the git repository (no absolute path, no ~, no control characters)."
49
+ },
50
+ "dockerfile": {
51
+ "default": "Dockerfile",
52
+ "type": "string",
53
+ "minLength": 1,
54
+ "maxLength": 1000,
55
+ "pattern": "^(?![\\\\/~]|[A-Za-z]:)[^\\u0000-\\u001f\\u007f]*$",
56
+ "description": "Relative path inside the git repository (no absolute path, no ~, no control characters)."
57
+ },
58
+ "target": {
59
+ "type": "string",
60
+ "description": "Multi-stage build target (docker build --target).",
61
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
62
+ },
63
+ "args": {
64
+ "type": "object",
65
+ "additionalProperties": {
66
+ "type": [
67
+ "string",
68
+ "number",
69
+ "boolean"
70
+ ]
71
+ },
72
+ "description": "Build arguments (docker build --build-arg KEY=value).",
73
+ "propertyNames": {
74
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
75
+ },
76
+ "maxProperties": 100
77
+ },
78
+ "ignore": {
79
+ "type": "array",
80
+ "items": {
81
+ "type": "string"
82
+ },
83
+ "description": "Paths left out of the build context and not watched by Tilt.",
84
+ "maxItems": 100
85
+ }
29
86
  }
30
87
  },
31
88
  "env": {
32
89
  "type": "object",
33
- "additionalProperties": { "type": "string" }
90
+ "additionalProperties": {
91
+ "type": "string"
92
+ },
93
+ "propertyNames": {
94
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
95
+ "maxLength": 253
96
+ },
97
+ "maxProperties": 200
34
98
  },
35
99
  "secrets": {
36
100
  "type": "array",
37
101
  "description": "Names only - values are never stored here. Set with \"localctl app secrets set <KEY>\"; injected via secretKeyRef, same as an addon's credentials. A name can't also appear in \"env\".",
38
- "items": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" }
102
+ "items": {
103
+ "type": "string",
104
+ "pattern": "^[A-Z][A-Z0-9_]*$"
105
+ },
106
+ "maxItems": 100
107
+ },
108
+ "replicas": {
109
+ "type": "integer",
110
+ "minimum": 1,
111
+ "default": 1,
112
+ "maximum": 20
39
113
  },
40
- "replicas": { "type": "integer", "minimum": 1, "default": 1 },
41
114
  "resources": {
42
115
  "type": "object",
43
116
  "properties": {
44
117
  "requests": {
45
118
  "type": "object",
46
- "properties": { "cpu": { "type": "string" }, "memory": { "type": "string" } }
119
+ "properties": {
120
+ "cpu": {
121
+ "type": [
122
+ "string",
123
+ "number"
124
+ ],
125
+ "pattern": "^[0-9]{1,7}(\\.[0-9]{1,3})?m?$",
126
+ "description": "500m, 1 or 0.5 (max 1024 cores)."
127
+ },
128
+ "memory": {
129
+ "type": [
130
+ "string",
131
+ "number"
132
+ ],
133
+ "pattern": "^[0-9]{1,13}(\\.[0-9]{1,3})?(k|Ki|Mi|Gi|Ti|M|G|T)?$",
134
+ "description": "256Mi, 2Gi or 1G (max 1 TiB). No milli suffix."
135
+ }
136
+ }
47
137
  },
48
138
  "limits": {
49
139
  "type": "object",
50
- "properties": { "cpu": { "type": "string" }, "memory": { "type": "string" } }
140
+ "properties": {
141
+ "cpu": {
142
+ "type": [
143
+ "string",
144
+ "number"
145
+ ],
146
+ "pattern": "^[0-9]{1,7}(\\.[0-9]{1,3})?m?$",
147
+ "description": "500m, 1 or 0.5 (max 1024 cores)."
148
+ },
149
+ "memory": {
150
+ "type": [
151
+ "string",
152
+ "number"
153
+ ],
154
+ "pattern": "^[0-9]{1,13}(\\.[0-9]{1,3})?(k|Ki|Mi|Gi|Ti|M|G|T)?$",
155
+ "description": "256Mi, 2Gi or 1G (max 1 TiB). No milli suffix."
156
+ }
157
+ }
51
158
  }
52
- }
159
+ },
160
+ "description": "A request cannot exceed its limit."
53
161
  },
54
162
  "debug": {
55
163
  "type": "object",
56
164
  "properties": {
57
- "enabled": { "type": "boolean", "default": false },
58
- "port": { "type": "integer" },
59
- "type": { "type": "string", "enum": ["node", "python", "go", "java", "dotnet"] }
165
+ "enabled": {
166
+ "type": "boolean",
167
+ "default": false
168
+ },
169
+ "port": {
170
+ "type": "integer",
171
+ "minimum": 1,
172
+ "maximum": 65535
173
+ },
174
+ "type": {
175
+ "type": "string",
176
+ "enum": [
177
+ "node",
178
+ "python",
179
+ "go",
180
+ "java",
181
+ "dotnet"
182
+ ]
183
+ }
60
184
  }
61
185
  },
62
186
  "probes": {
@@ -65,63 +189,573 @@
65
189
  "properties": {
66
190
  "readiness": {
67
191
  "type": "object",
192
+ "additionalProperties": false,
68
193
  "properties": {
69
- "path": { "type": "string" },
70
- "initialDelaySeconds": { "type": "integer" },
71
- "periodSeconds": { "type": "integer" }
194
+ "path": {
195
+ "type": "string",
196
+ "pattern": "^/[A-Za-z0-9._~/-]*$"
197
+ },
198
+ "command": {
199
+ "type": "array",
200
+ "minItems": 1,
201
+ "items": {
202
+ "type": "string"
203
+ },
204
+ "description": "Exec probe: healthy when it exits 0. Use instead of path."
205
+ },
206
+ "initialDelaySeconds": {
207
+ "type": "integer",
208
+ "minimum": 0,
209
+ "maximum": 3600
210
+ },
211
+ "periodSeconds": {
212
+ "type": "integer",
213
+ "minimum": 1,
214
+ "maximum": 3600
215
+ },
216
+ "timeoutSeconds": {
217
+ "type": "integer",
218
+ "minimum": 1,
219
+ "maximum": 600
220
+ },
221
+ "failureThreshold": {
222
+ "type": "integer",
223
+ "minimum": 1,
224
+ "maximum": 1000
225
+ }
72
226
  }
73
227
  },
74
228
  "liveness": {
75
229
  "type": "object",
230
+ "additionalProperties": false,
76
231
  "properties": {
77
- "path": { "type": "string" },
78
- "initialDelaySeconds": { "type": "integer" },
79
- "periodSeconds": { "type": "integer" }
232
+ "path": {
233
+ "type": "string",
234
+ "pattern": "^/[A-Za-z0-9._~/-]*$"
235
+ },
236
+ "command": {
237
+ "type": "array",
238
+ "minItems": 1,
239
+ "items": {
240
+ "type": "string"
241
+ },
242
+ "description": "Exec probe: healthy when it exits 0. Use instead of path."
243
+ },
244
+ "initialDelaySeconds": {
245
+ "type": "integer",
246
+ "minimum": 0,
247
+ "maximum": 3600
248
+ },
249
+ "periodSeconds": {
250
+ "type": "integer",
251
+ "minimum": 1,
252
+ "maximum": 3600
253
+ },
254
+ "timeoutSeconds": {
255
+ "type": "integer",
256
+ "minimum": 1,
257
+ "maximum": 600
258
+ },
259
+ "failureThreshold": {
260
+ "type": "integer",
261
+ "minimum": 1,
262
+ "maximum": 1000
263
+ }
264
+ }
265
+ },
266
+ "startup": {
267
+ "type": "object",
268
+ "additionalProperties": false,
269
+ "properties": {
270
+ "path": {
271
+ "type": "string",
272
+ "pattern": "^/[A-Za-z0-9._~/-]*$"
273
+ },
274
+ "command": {
275
+ "type": "array",
276
+ "minItems": 1,
277
+ "items": {
278
+ "type": "string"
279
+ },
280
+ "description": "Exec probe: healthy when it exits 0. Use instead of path."
281
+ },
282
+ "initialDelaySeconds": {
283
+ "type": "integer",
284
+ "minimum": 0,
285
+ "maximum": 3600
286
+ },
287
+ "periodSeconds": {
288
+ "type": "integer",
289
+ "minimum": 1,
290
+ "maximum": 3600
291
+ },
292
+ "timeoutSeconds": {
293
+ "type": "integer",
294
+ "minimum": 1,
295
+ "maximum": 600
296
+ },
297
+ "failureThreshold": {
298
+ "type": "integer",
299
+ "minimum": 1,
300
+ "maximum": 1000
301
+ }
80
302
  }
81
303
  }
82
- }
304
+ },
305
+ "additionalProperties": false
83
306
  },
84
307
  "dependencies": {
85
308
  "type": "array",
86
309
  "items": {
87
310
  "type": "object",
88
- "required": ["type"],
311
+ "required": [
312
+ "type"
313
+ ],
89
314
  "properties": {
90
- "type": { "type": "string", "enum": ["postgres", "redis", "mongo"] },
91
- "version": { "type": "string" },
92
- "database": { "type": "string" },
93
- "storage": { "type": "string", "description": "e.g. 1Gi" }
315
+ "type": {
316
+ "type": "string",
317
+ "enum": [
318
+ "postgres",
319
+ "redis",
320
+ "mongo"
321
+ ]
322
+ },
323
+ "version": {
324
+ "type": [
325
+ "string",
326
+ "number"
327
+ ],
328
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]*$"
329
+ },
330
+ "database": {
331
+ "type": "string",
332
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
333
+ },
334
+ "storage": {
335
+ "type": "string",
336
+ "pattern": "^[0-9]+(\\.[0-9]+)?(Mi|Gi|Ti)$",
337
+ "description": "e.g. 1Gi"
338
+ },
339
+ "resources": {
340
+ "type": "object",
341
+ "properties": {
342
+ "requests": {
343
+ "type": "object",
344
+ "properties": {
345
+ "cpu": {
346
+ "type": [
347
+ "string",
348
+ "number"
349
+ ],
350
+ "pattern": "^[0-9]{1,7}(\\.[0-9]{1,3})?m?$",
351
+ "description": "500m, 1 or 0.5 (max 1024 cores)."
352
+ },
353
+ "memory": {
354
+ "type": [
355
+ "string",
356
+ "number"
357
+ ],
358
+ "pattern": "^[0-9]{1,13}(\\.[0-9]{1,3})?(k|Ki|Mi|Gi|Ti|M|G|T)?$",
359
+ "description": "256Mi, 2Gi or 1G (max 1 TiB). No milli suffix."
360
+ }
361
+ }
362
+ },
363
+ "limits": {
364
+ "type": "object",
365
+ "properties": {
366
+ "cpu": {
367
+ "type": [
368
+ "string",
369
+ "number"
370
+ ],
371
+ "pattern": "^[0-9]{1,7}(\\.[0-9]{1,3})?m?$",
372
+ "description": "500m, 1 or 0.5 (max 1024 cores)."
373
+ },
374
+ "memory": {
375
+ "type": [
376
+ "string",
377
+ "number"
378
+ ],
379
+ "pattern": "^[0-9]{1,13}(\\.[0-9]{1,3})?(k|Ki|Mi|Gi|Ti|M|G|T)?$",
380
+ "description": "256Mi, 2Gi or 1G (max 1 TiB). No milli suffix."
381
+ }
382
+ }
383
+ }
384
+ },
385
+ "description": "CPU/memory for the dependency pod. The addon must declare resourcesPath."
386
+ }
94
387
  }
95
- }
388
+ },
389
+ "maxItems": 10,
390
+ "description": "Each type at most once per app; name + \"-\" + type must be at most 53 characters (Helm limit)."
96
391
  },
97
392
  "sidecars": {
98
393
  "type": "array",
99
394
  "description": "Extra containers in the same pod as your app (the k8s sidecar pattern) - reachable from your app via localhost:<port>, no Service needed.",
100
395
  "items": {
101
396
  "type": "object",
102
- "required": ["name", "image"],
397
+ "required": [
398
+ "name",
399
+ "image"
400
+ ],
103
401
  "properties": {
104
- "name": { "type": "string", "pattern": "^[a-z0-9-]+$" },
105
- "image": { "type": "string" },
106
- "port": { "type": "integer" },
107
- "command": { "type": "array", "items": { "type": "string" } },
108
- "args": { "type": "array", "items": { "type": "string" } },
109
- "env": { "type": "object", "additionalProperties": { "type": "string" } }
402
+ "name": {
403
+ "type": "string",
404
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
405
+ },
406
+ "image": {
407
+ "type": "string",
408
+ "pattern": "^[A-Za-z0-9][^\\s\\u0000-\\u001f\\u007f]*$"
409
+ },
410
+ "port": {
411
+ "type": "integer",
412
+ "minimum": 1,
413
+ "maximum": 65535
414
+ },
415
+ "command": {
416
+ "type": "array",
417
+ "items": {
418
+ "type": "string"
419
+ }
420
+ },
421
+ "args": {
422
+ "type": "array",
423
+ "items": {
424
+ "type": "string"
425
+ }
426
+ },
427
+ "env": {
428
+ "type": "object",
429
+ "additionalProperties": {
430
+ "type": "string"
431
+ },
432
+ "propertyNames": {
433
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
434
+ "maxLength": 253
435
+ },
436
+ "maxProperties": 200
437
+ }
110
438
  }
111
- }
439
+ },
440
+ "maxItems": 10
112
441
  },
113
442
  "sync": {
114
443
  "type": "array",
115
444
  "description": "Paths live-synced into the running container by Tilt without a full rebuild.",
116
445
  "items": {
117
446
  "type": "object",
118
- "required": ["localPath", "remotePath"],
447
+ "required": [
448
+ "localPath",
449
+ "remotePath"
450
+ ],
119
451
  "properties": {
120
- "localPath": { "type": "string" },
121
- "remotePath": { "type": "string" }
452
+ "localPath": {
453
+ "type": "string",
454
+ "minLength": 1,
455
+ "maxLength": 1000,
456
+ "pattern": "^(?![\\\\/~]|[A-Za-z]:)[^\\u0000-\\u001f\\u007f]*$",
457
+ "description": "Relative path inside the git repository (no absolute path, no ~, no control characters)."
458
+ },
459
+ "remotePath": {
460
+ "type": "string"
461
+ }
462
+ }
463
+ },
464
+ "maxItems": 50
465
+ },
466
+ "tls": {
467
+ "type": "boolean",
468
+ "default": true
469
+ },
470
+ "command": {
471
+ "type": "array",
472
+ "items": {
473
+ "type": "string"
474
+ },
475
+ "description": "Overrides the image ENTRYPOINT for the main container.",
476
+ "maxItems": 100
477
+ },
478
+ "args": {
479
+ "type": "array",
480
+ "items": {
481
+ "type": "string"
482
+ },
483
+ "description": "Overrides the image CMD for the main container.",
484
+ "maxItems": 100
485
+ },
486
+ "run": {
487
+ "type": "array",
488
+ "description": "Commands run in the container after files are synced. Needs at least one sync entry.",
489
+ "items": {
490
+ "type": "object",
491
+ "required": [
492
+ "command"
493
+ ],
494
+ "additionalProperties": false,
495
+ "properties": {
496
+ "command": {
497
+ "type": "string"
498
+ },
499
+ "trigger": {
500
+ "type": "array",
501
+ "items": {
502
+ "type": "string"
503
+ },
504
+ "description": "Only run when one of these paths changed."
505
+ }
506
+ }
507
+ },
508
+ "maxItems": 50
509
+ },
510
+ "envFile": {
511
+ "type": "string",
512
+ "description": "Dotenv-style file (KEY=value) inside the app folder, merged into env. Keys in env win."
513
+ },
514
+ "ports": {
515
+ "type": "array",
516
+ "description": "Extra container ports (also on the in-cluster Service).",
517
+ "items": {
518
+ "type": "object",
519
+ "required": [
520
+ "port"
521
+ ],
522
+ "additionalProperties": false,
523
+ "properties": {
524
+ "port": {
525
+ "type": "integer",
526
+ "minimum": 1,
527
+ "maximum": 65535
528
+ },
529
+ "name": {
530
+ "type": "string",
531
+ "pattern": "^[a-z]([a-z0-9-]{0,13}[a-z0-9])?$"
532
+ },
533
+ "protocol": {
534
+ "enum": [
535
+ "TCP",
536
+ "UDP"
537
+ ],
538
+ "default": "TCP"
539
+ },
540
+ "forward": {
541
+ "type": "boolean",
542
+ "default": false,
543
+ "description": "Also forward this port to localhost."
544
+ }
545
+ }
546
+ },
547
+ "maxItems": 20
548
+ },
549
+ "ingress": {
550
+ "type": "object",
551
+ "additionalProperties": false,
552
+ "description": "Extra ingress settings.",
553
+ "properties": {
554
+ "hosts": {
555
+ "type": "array",
556
+ "items": {
557
+ "type": "string",
558
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
559
+ },
560
+ "description": "More subdomains of local.test routed to this app.",
561
+ "maxItems": 20
562
+ },
563
+ "paths": {
564
+ "type": "array",
565
+ "description": "Replaces the default / route.",
566
+ "items": {
567
+ "type": "object",
568
+ "required": [
569
+ "path"
570
+ ],
571
+ "additionalProperties": false,
572
+ "properties": {
573
+ "path": {
574
+ "type": "string",
575
+ "maxLength": 200,
576
+ "pattern": "^/[A-Za-z0-9._~/-]*$",
577
+ "description": "No //, .. or percent escapes."
578
+ },
579
+ "port": {
580
+ "type": "integer",
581
+ "minimum": 1,
582
+ "maximum": 65535,
583
+ "description": "Must be \"port\" or a TCP entry of \"ports\"."
584
+ }
585
+ }
586
+ },
587
+ "maxItems": 50
588
+ },
589
+ "annotations": {
590
+ "type": "object",
591
+ "description": "Allowed keys: traefik.ingress.kubernetes.io/service.passhostheader, traefik.ingress.kubernetes.io/service.sticky.cookie, traefik.ingress.kubernetes.io/service.sticky.cookie.name, traefik.ingress.kubernetes.io/service.sticky.cookie.secure, traefik.ingress.kubernetes.io/service.serversscheme.",
592
+ "propertyNames": {
593
+ "enum": [
594
+ "traefik.ingress.kubernetes.io/service.passhostheader",
595
+ "traefik.ingress.kubernetes.io/service.sticky.cookie",
596
+ "traefik.ingress.kubernetes.io/service.sticky.cookie.name",
597
+ "traefik.ingress.kubernetes.io/service.sticky.cookie.secure",
598
+ "traefik.ingress.kubernetes.io/service.serversscheme"
599
+ ]
600
+ },
601
+ "additionalProperties": {
602
+ "type": [
603
+ "string",
604
+ "number",
605
+ "boolean"
606
+ ]
607
+ }
122
608
  }
123
609
  }
124
610
  },
125
- "tls": { "type": "boolean", "default": true }
611
+ "volumes": {
612
+ "type": "array",
613
+ "description": "Scratch space (emptyDir) mounted into the app. Gone when the pod is.",
614
+ "items": {
615
+ "type": "object",
616
+ "required": [
617
+ "name",
618
+ "mountPath"
619
+ ],
620
+ "additionalProperties": false,
621
+ "properties": {
622
+ "name": {
623
+ "type": "string",
624
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
625
+ },
626
+ "mountPath": {
627
+ "type": "string",
628
+ "pattern": "^/[A-Za-z0-9._/-]*$",
629
+ "description": "Absolute path in plain form (no //, /./, .., trailing /). Not under /proc, /sys, /dev or /var/run/secrets."
630
+ },
631
+ "sizeLimit": {
632
+ "type": "string",
633
+ "pattern": "^[0-9]+(\\.[0-9]+)?(Mi|Gi|Ti)$"
634
+ }
635
+ }
636
+ },
637
+ "maxItems": 20
638
+ },
639
+ "initContainers": {
640
+ "type": "array",
641
+ "description": "Run to completion before the app starts (migrations, seeding). Uses the app image unless image is set.",
642
+ "items": {
643
+ "type": "object",
644
+ "required": [
645
+ "name"
646
+ ],
647
+ "additionalProperties": false,
648
+ "properties": {
649
+ "name": {
650
+ "type": "string",
651
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
652
+ },
653
+ "image": {
654
+ "type": "string",
655
+ "pattern": "^[A-Za-z0-9][^\\s\\u0000-\\u001f\\u007f]*$"
656
+ },
657
+ "command": {
658
+ "type": "array",
659
+ "items": {
660
+ "type": "string"
661
+ }
662
+ },
663
+ "args": {
664
+ "type": "array",
665
+ "items": {
666
+ "type": "string"
667
+ }
668
+ },
669
+ "env": {
670
+ "type": "object",
671
+ "additionalProperties": {
672
+ "type": "string"
673
+ },
674
+ "propertyNames": {
675
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
676
+ "maxLength": 253
677
+ },
678
+ "maxProperties": 200
679
+ }
680
+ }
681
+ },
682
+ "maxItems": 10
683
+ },
684
+ "labels": {
685
+ "type": "object",
686
+ "description": "Extra labels. app and localctl.dev/* are reserved.",
687
+ "additionalProperties": {
688
+ "type": [
689
+ "string",
690
+ "number",
691
+ "boolean"
692
+ ]
693
+ },
694
+ "maxProperties": 50
695
+ },
696
+ "annotations": {
697
+ "type": "object",
698
+ "description": "Extra pod annotations: a plain key (note, team) or under prometheus.io/, reloader.stakater.com/, fluentbit.io/, ad.datadoghq.com/, co.elastic.logs/, logging.banzaicloud.io/, backup.velero.io/ or checksum/.",
699
+ "additionalProperties": {
700
+ "type": [
701
+ "string",
702
+ "number",
703
+ "boolean"
704
+ ]
705
+ },
706
+ "maxProperties": 50,
707
+ "propertyNames": {
708
+ "pattern": "^(?:[A-Za-z0-9_-]+|(?:prometheus\\.io|reloader\\.stakater\\.com|fluentbit\\.io|ad\\.datadoghq\\.com|co\\.elastic\\.logs|logging\\.banzaicloud\\.io|backup\\.velero\\.io|checksum)/[A-Za-z0-9][A-Za-z0-9._-]*)$"
709
+ }
710
+ },
711
+ "imagePullPolicy": {
712
+ "enum": [
713
+ "Always",
714
+ "IfNotPresent",
715
+ "Never"
716
+ ]
717
+ },
718
+ "terminationGracePeriodSeconds": {
719
+ "type": "integer",
720
+ "minimum": 0,
721
+ "maximum": 3600
722
+ },
723
+ "securityContext": {
724
+ "type": "object",
725
+ "additionalProperties": false,
726
+ "description": "Settings that tighten the container only.",
727
+ "properties": {
728
+ "runAsUser": {
729
+ "type": "integer",
730
+ "minimum": 0,
731
+ "maximum": 65535
732
+ },
733
+ "runAsGroup": {
734
+ "type": "integer",
735
+ "minimum": 0,
736
+ "maximum": 65535
737
+ },
738
+ "runAsNonRoot": {
739
+ "type": "boolean"
740
+ },
741
+ "readOnlyRootFilesystem": {
742
+ "type": "boolean"
743
+ },
744
+ "allowPrivilegeEscalation": {
745
+ "const": false,
746
+ "description": "Can only be turned off."
747
+ }
748
+ }
749
+ },
750
+ "environments": {
751
+ "type": "object",
752
+ "description": "Named overrides merged over this file with: localctl app up --env <name>. Objects merge, lists replace. name, namespace, envFile and environments cannot be overridden.",
753
+ "propertyNames": {
754
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
755
+ },
756
+ "additionalProperties": {
757
+ "type": "object"
758
+ }
759
+ }
126
760
  }
127
761
  }