kube_cluster 0.2.1 → 0.3.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -4
  3. data/examples/01-basic-redis-pod/manifest.rb +1 -1
  4. data/examples/02-manifest-with-middleware/manifest.rb +37 -0
  5. data/examples/02-manifest-with-middleware/middleware/labels.rb +4 -0
  6. data/examples/02-manifest-with-middleware/middleware/namespace.rb +4 -0
  7. data/examples/02-manifest-with-middleware/templates/config_map.rb +13 -0
  8. data/examples/02-manifest-with-middleware/templates/deployment.rb +59 -0
  9. data/examples/02-manifest-with-middleware/templates/horizontal_pod_autoscaler.rb +30 -0
  10. data/examples/02-manifest-with-middleware/templates/ingress.rb +38 -0
  11. data/examples/02-manifest-with-middleware/templates/service.rb +12 -0
  12. data/examples/{version2 → 03-app-with-database}/demo.rb +2 -2
  13. data/examples/{version2 → 03-app-with-database}/postgresql.rb +4 -4
  14. data/examples/{version2 → 03-app-with-database}/ruby_on_rails.rb +1 -1
  15. data/lib/kube/cluster/manifest.rb +13 -64
  16. data/lib/kube/cluster/middleware/annotations.rb +32 -0
  17. data/lib/kube/cluster/middleware/hpa_for_deployment.rb +111 -0
  18. data/lib/kube/cluster/{manifest/middleware → middleware}/ingress_for_service.rb +36 -34
  19. data/lib/kube/cluster/middleware/labels.rb +59 -0
  20. data/lib/kube/cluster/middleware/namespace.rb +31 -0
  21. data/lib/kube/cluster/middleware/pod_anti_affinity.rb +61 -0
  22. data/lib/kube/cluster/middleware/resource_preset.rb +64 -0
  23. data/lib/kube/cluster/middleware/security_context.rb +84 -0
  24. data/lib/kube/cluster/middleware/service_for_deployment.rb +71 -0
  25. data/lib/kube/cluster/middleware/stack.rb +43 -0
  26. data/lib/kube/cluster/middleware.rb +69 -0
  27. data/lib/kube/cluster/resource.rb +78 -0
  28. data/lib/kube/cluster/version.rb +1 -1
  29. data/lib/kube/cluster.rb +21 -0
  30. metadata +25 -19
  31. data/examples/database/manifest.rb +0 -238
  32. data/examples/web-app/manifest.rb +0 -215
  33. data/lib/kube/cluster/manifest/middleware/annotations.rb +0 -32
  34. data/lib/kube/cluster/manifest/middleware/hpa_for_deployment.rb +0 -109
  35. data/lib/kube/cluster/manifest/middleware/labels.rb +0 -59
  36. data/lib/kube/cluster/manifest/middleware/namespace.rb +0 -31
  37. data/lib/kube/cluster/manifest/middleware/pod_anti_affinity.rb +0 -61
  38. data/lib/kube/cluster/manifest/middleware/resource_preset.rb +0 -64
  39. data/lib/kube/cluster/manifest/middleware/security_context.rb +0 -84
  40. data/lib/kube/cluster/manifest/middleware/service_for_deployment.rb +0 -69
  41. data/lib/kube/cluster/manifest/middleware.rb +0 -178
  42. data/lib/kube/cluster/manifest/stack.rb +0 -56
  43. /data/examples/{version2 → 03-app-with-database}/helpers.rb +0 -0
  44. /data/examples/{version2 → 03-app-with-database}/my_app.rb +0 -0
@@ -1,238 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Database (PostgreSQL) Example
5
- #
6
- # Demonstrates the Ruby equivalents of Bitnami common chart patterns:
7
- # - Secret lifecycle management (_secrets.tpl)
8
- # - StorageClass resolution (_storage.tpl)
9
- # - StatefulSet with persistent volumes
10
- # - Headless service for stable DNS
11
- # - NetworkPolicy for database isolation
12
- # - Standard labels and naming (_labels.tpl, _names.tpl)
13
- # - Resource presets (_resources.tpl)
14
- #
15
- # Usage:
16
- # ruby examples/database/manifest.rb
17
- # ruby examples/database/manifest.rb > database.yaml
18
-
19
- require "kube/schema"
20
- require "securerandom"
21
-
22
- # ── Naming ────────────────────────────────────────────────────────────────────
23
-
24
- APP_NAME = "postgresql"
25
- RELEASE_NAME = "my-release"
26
- NAMESPACE = "database"
27
- FULLNAME = "#{RELEASE_NAME}-#{APP_NAME}"[0, 63].chomp("-")
28
-
29
- # ── Labels ────────────────────────────────────────────────────────────────────
30
-
31
- STANDARD_LABELS = {
32
- "app.kubernetes.io/name": APP_NAME,
33
- "app.kubernetes.io/instance": RELEASE_NAME,
34
- "app.kubernetes.io/version": "16.4.0",
35
- "app.kubernetes.io/component": "primary",
36
- "app.kubernetes.io/managed-by": "kube_cluster",
37
- }
38
-
39
- MATCH_LABELS = STANDARD_LABELS.slice(
40
- :"app.kubernetes.io/name",
41
- :"app.kubernetes.io/instance",
42
- :"app.kubernetes.io/component",
43
- )
44
-
45
- # ── Secrets (from _secrets.tpl) ───────────────────────────────────────────────
46
- # Bitnami's secrets.passwords.manage generates random passwords, reuses existing
47
- # ones on upgrade, and base64 encodes them. In Ruby we do this directly.
48
-
49
- POSTGRES_PASSWORD = SecureRandom.alphanumeric(24)
50
- REPLICATION_PASSWORD = SecureRandom.alphanumeric(24)
51
-
52
- def base64(str)
53
- [str].pack("m0")
54
- end
55
-
56
- # ── Storage (from _storage.tpl) ───────────────────────────────────────────────
57
- # Bitnami resolves storage class from global > persistence > default.
58
- # The "-" convention means explicitly use the default storage class (empty string).
59
-
60
- STORAGE_CLASS = "standard" # set to "-" for default, or nil to omit
61
- STORAGE_SIZE = "10Gi"
62
-
63
- # ── Resource presets ──────────────────────────────────────────────────────────
64
-
65
- RESOURCES = {
66
- requests: { cpu: "500m", memory: "512Mi" },
67
- limits: { cpu: "750m", memory: "768Mi" },
68
- }
69
-
70
- # ── Build manifests ───────────────────────────────────────────────────────────
71
-
72
- manifest = Kube::Schema::Manifest.new
73
-
74
- # -- Namespace --
75
-
76
- manifest << Kube::Schema["Namespace"].new {
77
- metadata.name = NAMESPACE
78
- metadata.labels = STANDARD_LABELS.reject { |k, _| k == :"app.kubernetes.io/component" }
79
- }
80
-
81
- # -- Secret --
82
- # Pattern from _secrets.tpl: base64-encoded credentials, separate keys for
83
- # each password, supports existing secret reuse on upgrade.
84
-
85
- manifest << Kube::Schema["Secret"].new {
86
- metadata.name = FULLNAME
87
- metadata.namespace = NAMESPACE
88
- metadata.labels = STANDARD_LABELS
89
- self.type = "Opaque"
90
- self.data = {
91
- "postgres-password": base64(POSTGRES_PASSWORD),
92
- "replication-password": base64(REPLICATION_PASSWORD),
93
- }
94
- }
95
-
96
- # -- Headless Service (for StatefulSet stable DNS) --
97
-
98
- manifest << Kube::Schema["Service"].new {
99
- metadata.name = "#{FULLNAME}-headless"
100
- metadata.namespace = NAMESPACE
101
- metadata.labels = STANDARD_LABELS
102
-
103
- spec.clusterIP = "None"
104
- spec.selector = MATCH_LABELS
105
- spec.ports = [
106
- { name: "tcp-postgresql", port: 5432, targetPort: "tcp-postgresql" },
107
- ]
108
- }
109
-
110
- # -- Primary Service (for client connections) --
111
-
112
- manifest << Kube::Schema["Service"].new {
113
- metadata.name = FULLNAME
114
- metadata.namespace = NAMESPACE
115
- metadata.labels = STANDARD_LABELS
116
-
117
- spec.selector = MATCH_LABELS
118
- spec.ports = [
119
- { name: "tcp-postgresql", port: 5432, targetPort: "tcp-postgresql" },
120
- ]
121
- }
122
-
123
- # -- StatefulSet --
124
- # Uses storage class resolution pattern, secret references, resource presets,
125
- # pod anti-affinity for spreading replicas.
126
-
127
- manifest << Kube::Schema["StatefulSet"].new {
128
- metadata.name = FULLNAME
129
- metadata.namespace = NAMESPACE
130
- metadata.labels = STANDARD_LABELS
131
-
132
- spec.serviceName = "#{FULLNAME}-headless"
133
- spec.replicas = 1
134
- spec.selector.matchLabels = MATCH_LABELS
135
-
136
- spec.template.metadata.labels = STANDARD_LABELS
137
- spec.template.spec.containers = [
138
- {
139
- name: APP_NAME,
140
- image: "docker.io/postgres:16.4-alpine",
141
- ports: [
142
- { name: "tcp-postgresql", containerPort: 5432 },
143
- ],
144
- resources: RESOURCES,
145
- env: [
146
- { name: "POSTGRES_PASSWORD", valueFrom: { secretKeyRef: { name: FULLNAME, key: "postgres-password" } } },
147
- { name: "PGDATA", value: "/var/lib/postgresql/data/pgdata" },
148
- ],
149
- volumeMounts: [
150
- { name: "data", mountPath: "/var/lib/postgresql/data" },
151
- ],
152
- livenessProbe: {
153
- exec: { command: ["pg_isready", "-U", "postgres"] },
154
- initialDelaySeconds: 30,
155
- periodSeconds: 10,
156
- timeoutSeconds: 5,
157
- failureThreshold: 6,
158
- },
159
- readinessProbe: {
160
- exec: { command: ["pg_isready", "-U", "postgres"] },
161
- initialDelaySeconds: 5,
162
- periodSeconds: 10,
163
- timeoutSeconds: 5,
164
- failureThreshold: 6,
165
- },
166
- },
167
- ]
168
-
169
- # Pod anti-affinity: hard anti-affinity to guarantee one pod per node
170
- # (from _affinities.tpl: common.affinities.pods.hard)
171
- spec.template.spec.affinity = {
172
- podAntiAffinity: {
173
- requiredDuringSchedulingIgnoredDuringExecution: [
174
- {
175
- labelSelector: { matchLabels: MATCH_LABELS },
176
- topologyKey: "kubernetes.io/hostname",
177
- },
178
- ],
179
- },
180
- }
181
-
182
- # Storage class resolution (from _storage.tpl)
183
- storage_class = STORAGE_CLASS == "-" ? "" : STORAGE_CLASS
184
-
185
- spec.volumeClaimTemplates = [
186
- {
187
- metadata: { name: "data" },
188
- spec: {
189
- accessModes: ["ReadWriteOnce"],
190
- storageClassName: storage_class,
191
- resources: { requests: { storage: STORAGE_SIZE } },
192
- },
193
- },
194
- ]
195
- }
196
-
197
- # -- NetworkPolicy --
198
- # Isolate the database: only allow ingress from pods with the app label,
199
- # deny everything else. This is a common production hardening pattern.
200
-
201
- manifest << Kube::Schema["NetworkPolicy"].new {
202
- metadata.name = FULLNAME
203
- metadata.namespace = NAMESPACE
204
- metadata.labels = STANDARD_LABELS
205
-
206
- spec.podSelector = { matchLabels: MATCH_LABELS }
207
- spec.policyTypes = ["Ingress", "Egress"]
208
- spec.ingress = [
209
- {
210
- from: [
211
- {
212
- podSelector: {
213
- matchLabels: { "app.kubernetes.io/name": "web-app" },
214
- },
215
- },
216
- ],
217
- ports: [
218
- { protocol: "TCP", port: "5432" },
219
- ],
220
- },
221
- ]
222
- # Allow DNS egress + nothing else
223
- spec.egress = [
224
- {
225
- to: [
226
- { namespaceSelector: {} },
227
- ],
228
- ports: [
229
- { protocol: "UDP", port: "53" },
230
- { protocol: "TCP", port: "53" },
231
- ],
232
- },
233
- ]
234
- }
235
-
236
- # ── Render ────────────────────────────────────────────────────────────────────
237
-
238
- puts manifest.to_yaml
@@ -1,215 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "kube/schema"
5
-
6
- MATCH_LABELS = STANDARD_LABELS.slice(
7
- :"app.kubernetes.io/name",
8
- :"app.kubernetes.io/instance",
9
- )
10
-
11
- REGISTRY = "docker.io"
12
- REPOSITORY = "nginx"
13
- TAG = "1.27.3-alpine"
14
- IMAGE = "#{REGISTRY}/#{REPOSITORY}:#{TAG}"
15
-
16
- RESOURCE_PRESETS = {
17
- "nano": { requests: { cpu: "100m", memory: "128Mi" }, limits: { cpu: "150m", memory: "192Mi" } },
18
- "micro": { requests: { cpu: "250m", memory: "256Mi" }, limits: { cpu: "375m", memory: "384Mi" } },
19
- "small": { requests: { cpu: "500m", memory: "512Mi" }, limits: { cpu: "750m", memory: "768Mi" } },
20
- "medium": { requests: { cpu: "500m", memory: "1024Mi" }, limits: { cpu: "750m", memory: "1536Mi" } },
21
- "large": { requests: { cpu: "1.0", memory: "2048Mi" }, limits: { cpu: "1.5", memory: "3072Mi" } },
22
- "xlarge": { requests: { cpu: "1.0", memory: "3072Mi" }, limits: { cpu: "3.0", memory: "6144Mi" } },
23
- "2xlarge": { requests: { cpu: "1.0", memory: "3072Mi" }, limits: { cpu: "6.0", memory: "12288Mi" } },
24
- }
25
-
26
- module AutoConfig
27
- end
28
-
29
- class MyApp < Kube::Schema::Manifest
30
- include Helpers
31
- include AutoConfig
32
-
33
- def initialize(namespace: DEFAULT_NAMESPACE, labels: STANDARD_LABELS)
34
- @app_name = "web-app"
35
- @fullname = "#{RELEASE_NAME}-#{APP_NAME}"[0, 63].chomp("-")
36
- @namespace = namespace
37
-
38
- @labels = {
39
- "app.kubernetes.io/name": @app_name
40
- "app.kubernetes.io/managed-by": "kube_cluster",
41
- }
42
- end
43
-
44
- stack do
45
- use Middleware::Namespace
46
- use Middleware::Labels
47
- end
48
-
49
- class Namespace < Kube::Schema["Namespace"]
50
- def initialize(namespace:)
51
- build {
52
- metadata.name = namespace
53
- }
54
- end
55
- end
56
-
57
- class ConfigMap < Kube::Schema["ConfigMap"]
58
- def initialize(namespace:)
59
- build {
60
- metadata.name = "#{namespace}-config"
61
- spec.data = {
62
- RAILS_ENV: "production",
63
- LOG_LEVEL: "info",
64
- WORKERS: "4",
65
- PORT: "3000",
66
- }
67
- }
68
- end
69
- end
70
-
71
- class Deployment < Kube::Schema["Deployment"]
72
- def initialize(namespace:)
73
- build {
74
- metadata.name = namespace
75
-
76
- spec.replicas = 3
77
- spec.selector.matchLabels = MATCH_LABELS
78
-
79
- spec.template.metadata.labels = STANDARD_LABELS
80
- spec.template.metadata.annotations = {
81
- # Checksum pattern from _utils.tpl -- triggers rolling restart on config change
82
- "checksum/config": "{{ sha256sum of configmap data }}",
83
- }
84
-
85
- spec.template.spec.containers = [
86
- {
87
- name: APP_NAME,
88
- image: IMAGE,
89
- ports: [{ name: "http", containerPort: 3000, protocol: "TCP" }],
90
- resources: RESOURCES,
91
- env: [
92
- { name: "PORT", value: "3000" },
93
- ],
94
- envFrom: [
95
- { configMapRef: { name: "#{FULLNAME}-config" } },
96
- ],
97
- livenessProbe: {
98
- httpGet: { path: "/healthz", port: "http" },
99
- initialDelaySeconds: 15,
100
- periodSeconds: 10,
101
- },
102
- readinessProbe: {
103
- httpGet: { path: "/readyz", port: "http" },
104
- initialDelaySeconds: 5,
105
- periodSeconds: 5,
106
- },
107
- },
108
- ]
109
-
110
- # Pod anti-affinity (from _affinities.tpl)
111
- # Soft anti-affinity: prefer spreading pods across nodes but don't enforce it
112
- spec.template.spec.affinity = {
113
- podAntiAffinity: {
114
- preferredDuringSchedulingIgnoredDuringExecution: [
115
- {
116
- weight: 1,
117
- podAffinityTerm: {
118
- labelSelector: {
119
- matchLabels: MATCH_LABELS,
120
- },
121
- topologyKey: "kubernetes.io/hostname",
122
- },
123
- },
124
- ],
125
- },
126
- }
127
- }
128
- end
129
- end
130
-
131
- class Service < Kube::Schema["Service"]
132
- def initialize(namespace:)
133
- build {
134
- metadata.name = namespace
135
-
136
- spec.selector = MATCH_LABELS
137
- spec.ports = [
138
- { name: "http", port: 80, targetPort: "http", protocol: "TCP" },
139
- ]
140
- }
141
- end
142
- end
143
-
144
- class Ingress < Kube::Schema["Ingress"]
145
- def initialize(namespace:)
146
- build {
147
- metadata.name = namespace
148
- metadata.annotations = {
149
- "cert-manager.io/cluster-issuer": "letsencrypt-prod",
150
- "nginx.ingress.kubernetes.io/ssl-redirect": "true",
151
- }
152
-
153
- spec.ingressClassName = "nginx"
154
- spec.tls = [
155
- {
156
- hosts: ["app.example.com"],
157
- secretName: "#{namespace}-tls",
158
- },
159
- ]
160
- spec.rules = [
161
- {
162
- host: "app.example.com",
163
- http: {
164
- paths: [
165
- {
166
- path: "/",
167
- pathType: "Prefix",
168
- backend: {
169
- service: {
170
- name: FULLNAME,
171
- port: { name: "http" },
172
- },
173
- },
174
- },
175
- ],
176
- },
177
- },
178
- ]
179
- }
180
- end
181
- end
182
-
183
- class HorizontalPodAutoscaler < Kube::Schema["HorizontalPodAutoscaler"]
184
- def initialize(namespace:)
185
- build {
186
- metadata.name = namespace
187
-
188
- spec.scaleTargetRef.apiVersion = "apps/v1"
189
- spec.scaleTargetRef.kind = "Deployment"
190
- spec.scaleTargetRef.name = namespace
191
-
192
- spec.minReplicas = 3
193
- spec.maxReplicas = 10
194
- spec.metrics = [
195
- {
196
- type: "Resource",
197
- resource: {
198
- name: "cpu",
199
- target: { type: "Utilization", averageUtilization: 75 },
200
- },
201
- },
202
- {
203
- type: "Resource",
204
- resource: {
205
- name: "memory",
206
- target: { type: "Utilization", averageUtilization: 80 },
207
- },
208
- },
209
- ]
210
- }
211
- end
212
- end
213
- end
214
-
215
- puts MyApp.new.to_yaml
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kube
4
- module Cluster
5
- class Manifest < Kube::Schema::Manifest
6
- class Middleware
7
- # Merges annotations into +metadata.annotations+ on every resource.
8
- # Existing annotations are preserved; the supplied annotations act
9
- # as defaults that can be overridden per-resource.
10
- #
11
- # stack do
12
- # use Middleware::Annotations,
13
- # "prometheus.io/scrape": "true",
14
- # "prometheus.io/port": "9090"
15
- # end
16
- #
17
- class Annotations < Middleware
18
- def initialize(**annotations)
19
- @annotations = annotations.transform_keys(&:to_sym).transform_values(&:to_s)
20
- end
21
-
22
- def call(resource)
23
- h = resource.to_h
24
- h[:metadata] ||= {}
25
- h[:metadata][:annotations] = @annotations.merge(h[:metadata][:annotations] || {})
26
- rebuild(resource, h)
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kube
4
- module Cluster
5
- class Manifest < Kube::Schema::Manifest
6
- class Middleware
7
- # Generates a HorizontalPodAutoscaler for every pod-bearing
8
- # resource that carries the +app.kubernetes.io/autoscale+ label.
9
- #
10
- # The label value encodes the min and max replicas as "min-max":
11
- #
12
- # metadata.labels = { "app.kubernetes.io/autoscale": "1-5" }
13
- #
14
- # Options:
15
- # cpu: — target CPU utilization percentage (default: 75)
16
- # memory: — target memory utilization percentage (default: 80)
17
- #
18
- # stack do
19
- # use Middleware::HPAForDeployment
20
- # use Middleware::HPAForDeployment, cpu: 60, memory: 70
21
- # end
22
- #
23
- class HPAForDeployment < Middleware
24
- LABEL = :"app.kubernetes.io/autoscale"
25
-
26
- def initialize(cpu: 75, memory: 80)
27
- @cpu = cpu
28
- @memory = memory
29
- end
30
-
31
- def call(resource)
32
- return resource unless pod_bearing?(resource)
33
-
34
- value = label(resource, LABEL)
35
- return resource unless value
36
-
37
- min, max = parse_range(value)
38
-
39
- h = resource.to_h
40
- name = h.dig(:metadata, :name)
41
- namespace = h.dig(:metadata, :namespace)
42
- labels = h.dig(:metadata, :labels) || {}
43
- api_version = h[:apiVersion] || "apps/v1"
44
- resource_kind = kind(resource)
45
-
46
- # Capture ivars as locals — the block runs via instance_exec
47
- # on a BlackHoleStruct, so @ivars would resolve on the BHS.
48
- cpu_target = @cpu
49
- memory_target = @memory
50
-
51
- hpa = Kube::Schema["HorizontalPodAutoscaler"].new {
52
- metadata.name = name
53
- metadata.namespace = namespace if namespace
54
- metadata.labels = labels.reject { |k, _| k == LABEL }
55
-
56
- spec.scaleTargetRef = {
57
- apiVersion: api_version,
58
- kind: resource_kind,
59
- name: name,
60
- }
61
- spec.minReplicas = min
62
- spec.maxReplicas = max
63
- spec.metrics = [
64
- {
65
- type: "Resource",
66
- resource: {
67
- name: "cpu",
68
- target: { type: "Utilization", averageUtilization: cpu_target },
69
- },
70
- },
71
- {
72
- type: "Resource",
73
- resource: {
74
- name: "memory",
75
- target: { type: "Utilization", averageUtilization: memory_target },
76
- },
77
- },
78
- ]
79
- }
80
-
81
- [resource, hpa]
82
- end
83
-
84
- private
85
-
86
- def parse_range(value)
87
- parts = value.to_s.split("-", 2)
88
-
89
- unless parts.length == 2
90
- raise ArgumentError,
91
- "Invalid autoscale label: #{value.inspect}. Expected format: \"min-max\" (e.g. \"1-5\")"
92
- end
93
-
94
- min = Integer(parts[0])
95
- max = Integer(parts[1])
96
-
97
- unless min > 0 && max >= min
98
- raise ArgumentError,
99
- "Invalid autoscale range: min=#{min}, max=#{max}. " \
100
- "min must be > 0 and max must be >= min."
101
- end
102
-
103
- [min, max]
104
- end
105
- end
106
- end
107
- end
108
- end
109
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kube
4
- module Cluster
5
- class Manifest < Kube::Schema::Manifest
6
- class Middleware
7
- # Merges labels into +metadata.labels+ on every resource.
8
- # Existing labels are preserved; the supplied labels act as defaults
9
- # that can be overridden per-resource.
10
- #
11
- # stack do
12
- # use Middleware::Labels, app: "web-app", managed_by: "kube_cluster"
13
- # end
14
- #
15
- # The keyword arguments are converted to standard label keys:
16
- #
17
- # app: -> "app.kubernetes.io/name"
18
- # instance: -> "app.kubernetes.io/instance"
19
- # version: -> "app.kubernetes.io/version"
20
- # component: -> "app.kubernetes.io/component"
21
- # part_of: -> "app.kubernetes.io/part-of"
22
- # managed_by: -> "app.kubernetes.io/managed-by"
23
- #
24
- # Any unrecognized keys are passed through as-is (string or symbol).
25
- #
26
- class Labels < Middleware
27
- STANDARD_KEYS = {
28
- app: :"app.kubernetes.io/name",
29
- instance: :"app.kubernetes.io/instance",
30
- version: :"app.kubernetes.io/version",
31
- component: :"app.kubernetes.io/component",
32
- part_of: :"app.kubernetes.io/part-of",
33
- managed_by: :"app.kubernetes.io/managed-by",
34
- }.freeze
35
-
36
- def initialize(**labels)
37
- @labels = normalize(labels)
38
- end
39
-
40
- def call(resource)
41
- h = resource.to_h
42
- h[:metadata] ||= {}
43
- h[:metadata][:labels] = @labels.merge(h[:metadata][:labels] || {})
44
- rebuild(resource, h)
45
- end
46
-
47
- private
48
-
49
- def normalize(labels)
50
- labels.each_with_object({}) do |(key, value), result|
51
- normalized_key = STANDARD_KEYS.fetch(key, key)
52
- result[normalized_key] = value.to_s
53
- end
54
- end
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Kube
4
- module Cluster
5
- class Manifest < Kube::Schema::Manifest
6
- class Middleware
7
- # Sets +metadata.namespace+ on all namespace-scoped resources.
8
- # Cluster-scoped kinds (Namespace, ClusterRole, etc.) are skipped.
9
- #
10
- # stack do
11
- # use Middleware::Namespace, "production"
12
- # end
13
- #
14
- class Namespace < Middleware
15
- def initialize(namespace)
16
- @namespace = namespace
17
- end
18
-
19
- def call(resource)
20
- return resource if cluster_scoped?(resource)
21
-
22
- h = resource.to_h
23
- h[:metadata] ||= {}
24
- h[:metadata][:namespace] = @namespace
25
- rebuild(resource, h)
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end