@emilia-protocol/gate 0.9.5 → 0.10.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 (74) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +190 -0
  3. package/README.md +75 -16
  4. package/action-control-manifest.js +26 -0
  5. package/adapters/_kit.js +47 -5
  6. package/adapters/aws.js +15 -4
  7. package/adapters/github-demo.mjs +25 -22
  8. package/adapters/github.js +1 -1
  9. package/adapters/jira.js +1 -0
  10. package/adapters/linear.js +1 -0
  11. package/adapters/salesforce.js +1 -0
  12. package/adapters/stripe.js +1 -1
  13. package/adapters/supabase.js +26 -4
  14. package/adapters/vercel.js +33 -4
  15. package/aec-execution.js +1 -3
  16. package/breakglass.js +285 -51
  17. package/control-plane.js +341 -0
  18. package/coverage.js +722 -0
  19. package/custody-demo.mjs +13 -3
  20. package/demo.mjs +9 -3
  21. package/deploy/helm/README.md +16 -8
  22. package/deploy/helm/emilia-gate/Chart.yaml +3 -1
  23. package/deploy/helm/emilia-gate/templates/NOTES.txt +3 -2
  24. package/deploy/helm/emilia-gate/templates/deployment.yaml +55 -1
  25. package/deploy/helm/emilia-gate/values.yaml +18 -2
  26. package/deploy/helm/emilia-gate-service/Chart.yaml +11 -0
  27. package/deploy/helm/emilia-gate-service/README.md +81 -0
  28. package/deploy/helm/emilia-gate-service/templates/NOTES.txt +12 -0
  29. package/deploy/helm/emilia-gate-service/templates/_helpers.tpl +83 -0
  30. package/deploy/helm/emilia-gate-service/templates/deployment.yaml +153 -0
  31. package/deploy/helm/emilia-gate-service/templates/migration-job.yaml +73 -0
  32. package/deploy/helm/emilia-gate-service/templates/networkpolicy.yaml +113 -0
  33. package/deploy/helm/emilia-gate-service/templates/pdb.yaml +14 -0
  34. package/deploy/helm/emilia-gate-service/templates/service.yaml +20 -0
  35. package/deploy/helm/emilia-gate-service/templates/serviceaccount.yaml +8 -0
  36. package/deploy/helm/emilia-gate-service/tests/fixtures/001_gate.sql +26 -0
  37. package/deploy/helm/emilia-gate-service/tests/fixtures/002_runtime_access.sql +32 -0
  38. package/deploy/helm/emilia-gate-service/tests/fixtures/gate.config.mjs +29 -0
  39. package/deploy/helm/emilia-gate-service/tests/render-check.sh +145 -0
  40. package/deploy/helm/emilia-gate-service/values.schema.json +145 -0
  41. package/deploy/helm/emilia-gate-service/values.yaml +166 -0
  42. package/deploy/sql/001-runtime.sql +707 -0
  43. package/deploy/terraform/README.md +24 -14
  44. package/deploy/terraform/main.tf +59 -0
  45. package/deploy/terraform/service/README.md +81 -0
  46. package/deploy/terraform/service/main.tf +718 -0
  47. package/deploy/terraform/service/outputs.tf +19 -0
  48. package/deploy/terraform/service/tests/validate.sh +24 -0
  49. package/deploy/terraform/service/tests/validation.tftest.hcl +168 -0
  50. package/deploy/terraform/service/variables.tf +459 -0
  51. package/deploy/terraform/service/versions.tf +10 -0
  52. package/deploy/terraform/variables.tf +95 -2
  53. package/deployment-attestation.js +248 -0
  54. package/eg1-conformance.js +46 -12
  55. package/enterprise.js +6 -2
  56. package/ep-assure.mjs +3 -2
  57. package/evidence-postgres.js +357 -0
  58. package/evidence.js +10 -3
  59. package/execution-binding.js +141 -26
  60. package/index.js +556 -115
  61. package/key-registry.js +51 -19
  62. package/mcp.js +4 -2
  63. package/network-witness.js +500 -0
  64. package/package.json +25 -5
  65. package/reliance-kernel.js +5 -6
  66. package/reliance-packet.js +43 -10
  67. package/reports/assurance-package.js +45 -19
  68. package/reports/reperform.js +78 -18
  69. package/reports/underwriter.js +1 -1
  70. package/settlement.js +300 -0
  71. package/store-postgres.js +14 -0
  72. package/store.js +26 -5
  73. package/strict-json.js +86 -0
  74. package/witness-postgres.js +97 -0
@@ -0,0 +1,718 @@
1
+ locals {
2
+ base_labels = merge({
3
+ "app.kubernetes.io/name" = var.name
4
+ "app.kubernetes.io/instance" = var.name
5
+ "app.kubernetes.io/part-of" = "emilia-gate"
6
+ "app.kubernetes.io/managed-by" = "terraform"
7
+ "emiliaprotocol.ai/deployment-model" = "byoc"
8
+ }, var.labels)
9
+
10
+ service_selector_labels = {
11
+ "app.kubernetes.io/name" = var.name
12
+ "app.kubernetes.io/instance" = var.name
13
+ "app.kubernetes.io/component" = "service"
14
+ }
15
+
16
+ base_selector_labels = {
17
+ "app.kubernetes.io/name" = var.name
18
+ "app.kubernetes.io/instance" = var.name
19
+ }
20
+
21
+ migration_id = substr(sha256(jsonencode({
22
+ image = var.image
23
+ command = var.migration_command
24
+ revision = var.migration_revision
25
+ })), 0, 10)
26
+
27
+ https_egress_cidrs = distinct(concat(
28
+ var.kms_egress_cidrs,
29
+ var.github_egress_cidrs,
30
+ var.siem_egress_cidrs,
31
+ ))
32
+
33
+ migration_postgres_secret_name = var.migration_postgres_secret_name == null ? "" : var.migration_postgres_secret_name
34
+ migration_postgres_secret_key = var.migration_postgres_secret_key == null ? "" : var.migration_postgres_secret_key
35
+ migration_credentials_valid = try(
36
+ trimspace(var.migration_postgres_secret_name) != ""
37
+ && trimspace(var.migration_postgres_secret_key) != ""
38
+ && var.migration_postgres_secret_name != var.postgres_secret_name,
39
+ false,
40
+ )
41
+
42
+ runtime_secret_env = concat(
43
+ var.github_token_secret_name == null ? [] : [{
44
+ env_name = var.github_token_env_name
45
+ secret_name = var.github_token_secret_name
46
+ secret_key = var.github_token_secret_key
47
+ }],
48
+ [for env_name, reference in var.secret_env : {
49
+ env_name = env_name
50
+ secret_name = reference.secret_name
51
+ secret_key = reference.secret_key
52
+ }],
53
+ )
54
+ configurable_env_names = concat(
55
+ [for reference in local.runtime_secret_env : reference.env_name],
56
+ keys(var.extra_env),
57
+ )
58
+ reserved_env_names = toset([
59
+ "NODE_ENV",
60
+ "HOST",
61
+ "PORT",
62
+ "EMILIA_GATE_CONFIG",
63
+ "EP_GATE_PORT",
64
+ "EP_GATE_LOG_LEVEL",
65
+ var.postgres_env_name,
66
+ var.api_token_env_name,
67
+ var.kms_env_name,
68
+ var.issuer_roots_env_name,
69
+ ])
70
+ }
71
+
72
+ resource "kubernetes_service_account_v1" "gate" {
73
+ metadata {
74
+ name = var.name
75
+ namespace = var.namespace
76
+ labels = local.base_labels
77
+ }
78
+
79
+ automount_service_account_token = false
80
+ }
81
+
82
+ resource "kubernetes_network_policy_v1" "default_deny" {
83
+ count = var.network_policy_enabled ? 1 : 0
84
+
85
+ metadata {
86
+ name = "${var.name}-default-deny"
87
+ namespace = var.namespace
88
+ labels = local.base_labels
89
+ }
90
+
91
+ spec {
92
+ pod_selector {
93
+ match_labels = local.base_selector_labels
94
+ }
95
+ policy_types = ["Ingress", "Egress"]
96
+ }
97
+ }
98
+
99
+ resource "kubernetes_network_policy_v1" "ingress" {
100
+ count = var.network_policy_enabled && var.allow_same_namespace_ingress ? 1 : 0
101
+
102
+ metadata {
103
+ name = "${var.name}-ingress"
104
+ namespace = var.namespace
105
+ labels = local.base_labels
106
+ }
107
+
108
+ spec {
109
+ pod_selector {
110
+ match_labels = local.service_selector_labels
111
+ }
112
+
113
+ policy_types = ["Ingress"]
114
+
115
+ ingress {
116
+ from {
117
+ pod_selector {}
118
+ }
119
+ ports {
120
+ port = var.port
121
+ protocol = "TCP"
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ resource "kubernetes_network_policy_v1" "dns" {
128
+ count = var.network_policy_enabled ? 1 : 0
129
+
130
+ metadata {
131
+ name = "${var.name}-dns"
132
+ namespace = var.namespace
133
+ labels = local.base_labels
134
+ }
135
+
136
+ spec {
137
+ pod_selector {
138
+ match_labels = local.base_selector_labels
139
+ }
140
+
141
+ policy_types = ["Egress"]
142
+
143
+ egress {
144
+ to {
145
+ namespace_selector {
146
+ match_labels = var.dns_namespace_labels
147
+ }
148
+ pod_selector {
149
+ match_labels = var.dns_pod_labels
150
+ }
151
+ }
152
+ ports {
153
+ port = 53
154
+ protocol = "UDP"
155
+ }
156
+ ports {
157
+ port = 53
158
+ protocol = "TCP"
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ resource "kubernetes_network_policy_v1" "postgres_pods" {
165
+ count = var.network_policy_enabled && length(var.postgres_pod_labels) > 0 ? 1 : 0
166
+
167
+ metadata {
168
+ name = "${var.name}-postgres-pods"
169
+ namespace = var.namespace
170
+ labels = local.base_labels
171
+ }
172
+
173
+ spec {
174
+ pod_selector {
175
+ match_labels = local.base_selector_labels
176
+ }
177
+
178
+ policy_types = ["Egress"]
179
+
180
+ egress {
181
+ to {
182
+ pod_selector {
183
+ match_labels = var.postgres_pod_labels
184
+ }
185
+ }
186
+ ports {
187
+ port = var.postgres_port
188
+ protocol = "TCP"
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ resource "kubernetes_network_policy_v1" "postgres_cidrs" {
195
+ count = var.network_policy_enabled && length(var.postgres_egress_cidrs) > 0 ? 1 : 0
196
+
197
+ metadata {
198
+ name = "${var.name}-postgres-cidrs"
199
+ namespace = var.namespace
200
+ labels = local.base_labels
201
+ }
202
+
203
+ spec {
204
+ pod_selector {
205
+ match_labels = local.base_selector_labels
206
+ }
207
+ policy_types = ["Egress"]
208
+
209
+ dynamic "egress" {
210
+ for_each = toset(var.postgres_egress_cidrs)
211
+ content {
212
+ to {
213
+ ip_block {
214
+ cidr = egress.value
215
+ }
216
+ }
217
+ ports {
218
+ port = var.postgres_port
219
+ protocol = "TCP"
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+
226
+ resource "kubernetes_network_policy_v1" "https_egress" {
227
+ count = var.network_policy_enabled && length(local.https_egress_cidrs) > 0 ? 1 : 0
228
+
229
+ metadata {
230
+ name = "${var.name}-https-egress"
231
+ namespace = var.namespace
232
+ labels = local.base_labels
233
+ }
234
+
235
+ spec {
236
+ pod_selector {
237
+ match_labels = local.service_selector_labels
238
+ }
239
+ policy_types = ["Egress"]
240
+
241
+ dynamic "egress" {
242
+ for_each = toset(local.https_egress_cidrs)
243
+ content {
244
+ to {
245
+ ip_block {
246
+ cidr = egress.value
247
+ }
248
+ }
249
+ ports {
250
+ port = 443
251
+ protocol = "TCP"
252
+ }
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ resource "kubernetes_job_v1" "migration" {
259
+ count = var.migration_enabled ? 1 : 0
260
+
261
+ metadata {
262
+ name = "${var.name}-migrate-${local.migration_id}"
263
+ namespace = var.namespace
264
+ labels = merge(local.base_labels, {
265
+ "app.kubernetes.io/component" = "migration"
266
+ })
267
+ }
268
+
269
+ wait_for_completion = true
270
+
271
+ spec {
272
+ active_deadline_seconds = var.migration_active_deadline_seconds
273
+ backoff_limit = var.migration_backoff_limit
274
+
275
+ template {
276
+ metadata {
277
+ labels = merge(local.base_selector_labels, {
278
+ "app.kubernetes.io/component" = "migration"
279
+ })
280
+ }
281
+
282
+ spec {
283
+ automount_service_account_token = false
284
+ restart_policy = "Never"
285
+
286
+ security_context {
287
+ run_as_non_root = true
288
+ run_as_user = 10001
289
+ run_as_group = 10001
290
+ fs_group = 10001
291
+ fs_group_change_policy = "OnRootMismatch"
292
+
293
+ seccomp_profile {
294
+ type = "RuntimeDefault"
295
+ }
296
+ }
297
+
298
+ dynamic "image_pull_secrets" {
299
+ for_each = toset(var.image_pull_secrets)
300
+ content {
301
+ name = image_pull_secrets.value
302
+ }
303
+ }
304
+
305
+ container {
306
+ name = "migrate"
307
+ image = var.image
308
+ image_pull_policy = var.image_pull_policy
309
+ command = var.migration_command
310
+
311
+ env {
312
+ name = "NODE_ENV"
313
+ value = "production"
314
+ }
315
+
316
+ env {
317
+ name = "EMILIA_GATE_CONFIG"
318
+ value = "${var.configuration_mount_path}/${var.configuration_secret_key}"
319
+ }
320
+
321
+ env {
322
+ name = var.postgres_env_name
323
+ value_from {
324
+ secret_key_ref {
325
+ name = local.migration_postgres_secret_name
326
+ key = local.migration_postgres_secret_key
327
+ optional = false
328
+ }
329
+ }
330
+ }
331
+
332
+ security_context {
333
+ allow_privilege_escalation = false
334
+ read_only_root_filesystem = true
335
+
336
+ capabilities {
337
+ drop = ["ALL"]
338
+ }
339
+ }
340
+
341
+ resources {
342
+ requests = var.migration_resources.requests
343
+ limits = var.migration_resources.limits
344
+ }
345
+
346
+ volume_mount {
347
+ name = "tmp"
348
+ mount_path = "/tmp"
349
+ }
350
+
351
+ volume_mount {
352
+ name = "configuration"
353
+ mount_path = var.configuration_mount_path
354
+ read_only = true
355
+ }
356
+ }
357
+
358
+ volume {
359
+ name = "tmp"
360
+ empty_dir {
361
+ size_limit = "32Mi"
362
+ }
363
+ }
364
+
365
+
366
+ volume {
367
+ name = "configuration"
368
+ secret {
369
+ secret_name = var.configuration_secret_name
370
+ optional = false
371
+ default_mode = "0440"
372
+
373
+ items {
374
+ key = var.configuration_secret_key
375
+ path = var.configuration_secret_key
376
+ mode = "0440"
377
+ }
378
+ items {
379
+ key = var.migration_script_secret_key
380
+ path = var.migration_script_secret_key
381
+ mode = "0440"
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
387
+ }
388
+
389
+ depends_on = [
390
+ kubernetes_network_policy_v1.default_deny,
391
+ kubernetes_network_policy_v1.dns,
392
+ kubernetes_network_policy_v1.postgres_pods,
393
+ kubernetes_network_policy_v1.postgres_cidrs,
394
+ ]
395
+
396
+ timeouts {
397
+ create = "15m"
398
+ update = "15m"
399
+ }
400
+
401
+ lifecycle {
402
+ precondition {
403
+ condition = local.migration_credentials_valid
404
+ error_message = "migration_enabled requires a non-empty migration_postgres_secret_name/key, and the migration Secret must differ from postgres_secret_name. Runtime credential fallback is refused."
405
+ }
406
+ }
407
+ }
408
+
409
+ resource "kubernetes_deployment_v1" "gate" {
410
+ metadata {
411
+ name = var.name
412
+ namespace = var.namespace
413
+ labels = local.base_labels
414
+ }
415
+
416
+ spec {
417
+ replicas = var.replicas
418
+ revision_history_limit = 3
419
+
420
+ strategy {
421
+ type = "RollingUpdate"
422
+ rolling_update {
423
+ max_unavailable = "0"
424
+ max_surge = "1"
425
+ }
426
+ }
427
+
428
+ selector {
429
+ match_labels = local.service_selector_labels
430
+ }
431
+
432
+ template {
433
+ metadata {
434
+ labels = merge(local.base_labels, local.service_selector_labels)
435
+ annotations = var.pod_annotations
436
+ }
437
+
438
+ spec {
439
+ service_account_name = kubernetes_service_account_v1.gate.metadata[0].name
440
+ automount_service_account_token = false
441
+ termination_grace_period_seconds = 30
442
+
443
+ security_context {
444
+ run_as_non_root = true
445
+ run_as_user = 10001
446
+ run_as_group = 10001
447
+ fs_group = 10001
448
+ fs_group_change_policy = "OnRootMismatch"
449
+
450
+ seccomp_profile {
451
+ type = "RuntimeDefault"
452
+ }
453
+ }
454
+
455
+ dynamic "image_pull_secrets" {
456
+ for_each = toset(var.image_pull_secrets)
457
+ content {
458
+ name = image_pull_secrets.value
459
+ }
460
+ }
461
+
462
+ container {
463
+ name = "gate-service"
464
+ image = var.image
465
+ image_pull_policy = var.image_pull_policy
466
+
467
+ port {
468
+ name = "http"
469
+ container_port = var.port
470
+ protocol = "TCP"
471
+ }
472
+
473
+ env {
474
+ name = "NODE_ENV"
475
+ value = "production"
476
+ }
477
+ env {
478
+ name = "HOST"
479
+ value = "0.0.0.0"
480
+ }
481
+ env {
482
+ name = "PORT"
483
+ value = tostring(var.port)
484
+ }
485
+ env {
486
+ name = "EMILIA_GATE_CONFIG"
487
+ value = "${var.configuration_mount_path}/${var.configuration_secret_key}"
488
+ }
489
+ env {
490
+ name = "EP_GATE_PORT"
491
+ value = tostring(var.port)
492
+ }
493
+ env {
494
+ name = "EP_GATE_LOG_LEVEL"
495
+ value = var.log_level
496
+ }
497
+
498
+ env {
499
+ name = var.postgres_env_name
500
+ value_from {
501
+ secret_key_ref {
502
+ name = var.postgres_secret_name
503
+ key = var.postgres_secret_key
504
+ optional = false
505
+ }
506
+ }
507
+ }
508
+ env {
509
+ name = var.api_token_env_name
510
+ value_from {
511
+ secret_key_ref {
512
+ name = var.api_token_secret_name
513
+ key = var.api_token_secret_key
514
+ optional = false
515
+ }
516
+ }
517
+ }
518
+ dynamic "env" {
519
+ for_each = var.kms_secret_name == null ? [] : [var.kms_secret_name]
520
+ content {
521
+ name = var.kms_env_name
522
+ value_from {
523
+ secret_key_ref {
524
+ name = env.value
525
+ key = var.kms_secret_key
526
+ optional = false
527
+ }
528
+ }
529
+ }
530
+ }
531
+ env {
532
+ name = var.issuer_roots_env_name
533
+ value_from {
534
+ secret_key_ref {
535
+ name = var.issuer_roots_secret_name
536
+ key = var.issuer_roots_secret_key
537
+ optional = false
538
+ }
539
+ }
540
+ }
541
+
542
+ dynamic "env" {
543
+ for_each = { for index, reference in local.runtime_secret_env : tostring(index) => reference }
544
+ content {
545
+ name = env.value.env_name
546
+ value_from {
547
+ secret_key_ref {
548
+ name = env.value.secret_name
549
+ key = env.value.secret_key
550
+ optional = false
551
+ }
552
+ }
553
+ }
554
+ }
555
+
556
+ dynamic "env" {
557
+ for_each = var.extra_env
558
+ content {
559
+ name = env.key
560
+ value = env.value
561
+ }
562
+ }
563
+
564
+ startup_probe {
565
+ http_get {
566
+ path = "/v1/live"
567
+ port = "http"
568
+ }
569
+ period_seconds = 2
570
+ timeout_seconds = 2
571
+ failure_threshold = 30
572
+ }
573
+
574
+ liveness_probe {
575
+ http_get {
576
+ path = "/v1/live"
577
+ port = "http"
578
+ }
579
+ period_seconds = 10
580
+ timeout_seconds = 2
581
+ failure_threshold = 3
582
+ }
583
+
584
+ readiness_probe {
585
+ http_get {
586
+ path = "/v1/ready"
587
+ port = "http"
588
+ }
589
+ period_seconds = 5
590
+ timeout_seconds = 2
591
+ failure_threshold = 3
592
+ }
593
+
594
+ resources {
595
+ requests = var.resources.requests
596
+ limits = var.resources.limits
597
+ }
598
+
599
+ security_context {
600
+ allow_privilege_escalation = false
601
+ read_only_root_filesystem = true
602
+
603
+ capabilities {
604
+ drop = ["ALL"]
605
+ }
606
+ }
607
+
608
+ volume_mount {
609
+ name = "tmp"
610
+ mount_path = "/tmp"
611
+ }
612
+
613
+ volume_mount {
614
+ name = "configuration"
615
+ mount_path = var.configuration_mount_path
616
+ read_only = true
617
+ }
618
+ }
619
+
620
+ volume {
621
+ name = "tmp"
622
+ empty_dir {
623
+ size_limit = "64Mi"
624
+ }
625
+ }
626
+
627
+ volume {
628
+ name = "configuration"
629
+ secret {
630
+ secret_name = var.configuration_secret_name
631
+ optional = false
632
+ default_mode = "0440"
633
+
634
+ items {
635
+ key = var.configuration_secret_key
636
+ path = var.configuration_secret_key
637
+ mode = "0440"
638
+ }
639
+ }
640
+ }
641
+
642
+ topology_spread_constraint {
643
+ max_skew = 1
644
+ topology_key = "kubernetes.io/hostname"
645
+ when_unsatisfiable = "ScheduleAnyway"
646
+
647
+ label_selector {
648
+ match_labels = local.service_selector_labels
649
+ }
650
+ }
651
+
652
+ node_selector = var.node_selector
653
+
654
+ dynamic "toleration" {
655
+ for_each = var.tolerations
656
+ content {
657
+ key = toleration.value.key
658
+ operator = toleration.value.operator
659
+ value = toleration.value.value
660
+ effect = toleration.value.effect
661
+ toleration_seconds = toleration.value.toleration_seconds
662
+ }
663
+ }
664
+ }
665
+ }
666
+ }
667
+
668
+ depends_on = [kubernetes_job_v1.migration]
669
+
670
+ lifecycle {
671
+ precondition {
672
+ condition = length(distinct(local.configurable_env_names)) == length(local.configurable_env_names)
673
+ error_message = "github_token_secret_name, secret_env, and extra_env must not define duplicate environment variable names."
674
+ }
675
+ precondition {
676
+ condition = length(setintersection(toset(local.configurable_env_names), local.reserved_env_names)) == 0
677
+ error_message = "secret_env and extra_env must not override module-managed environment variables."
678
+ }
679
+ }
680
+ }
681
+
682
+ resource "kubernetes_service_v1" "gate" {
683
+ metadata {
684
+ name = var.name
685
+ namespace = var.namespace
686
+ labels = local.base_labels
687
+ }
688
+
689
+ spec {
690
+ type = "ClusterIP"
691
+ selector = local.service_selector_labels
692
+
693
+ port {
694
+ name = "http"
695
+ port = var.port
696
+ target_port = "http"
697
+ protocol = "TCP"
698
+ }
699
+ }
700
+ }
701
+
702
+ resource "kubernetes_pod_disruption_budget_v1" "gate" {
703
+ count = var.pdb_enabled ? 1 : 0
704
+
705
+ metadata {
706
+ name = var.name
707
+ namespace = var.namespace
708
+ labels = local.base_labels
709
+ }
710
+
711
+ spec {
712
+ min_available = var.pdb_min_available
713
+
714
+ selector {
715
+ match_labels = local.service_selector_labels
716
+ }
717
+ }
718
+ }