kube_cluster 0.3.7 → 0.3.9

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.
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
3
6
  module Kube
4
7
  module Cluster
5
8
  class Middleware
@@ -62,3 +65,164 @@ module Kube
62
65
  end
63
66
  end
64
67
  end
68
+
69
+ test do
70
+ Middleware = Kube::Cluster::Middleware
71
+
72
+ it "injects_small_preset_into_deployment" do
73
+ m = manifest(Kube::Cluster["Deployment"].new {
74
+ metadata.name = "web"
75
+ metadata.labels = { "app.kubernetes.io/size": "small" }
76
+ spec.selector.matchLabels = { app: "web" }
77
+ spec.template.metadata.labels = { app: "web" }
78
+ spec.template.spec.containers = [
79
+ { name: "web", image: "nginx:latest" },
80
+ ]
81
+ })
82
+
83
+ Middleware::ResourcePreset.new.call(m)
84
+ container = m.resources.first.to_h.dig(:spec, :template, :spec, :containers, 0)
85
+
86
+ container.dig(:resources, :requests, :cpu).should == "500m"
87
+ end
88
+
89
+ it "injects_nano_preset" do
90
+ m = manifest(Kube::Cluster["Deployment"].new {
91
+ metadata.name = "tiny"
92
+ metadata.labels = { "app.kubernetes.io/size": "nano" }
93
+ spec.selector.matchLabels = { app: "tiny" }
94
+ spec.template.metadata.labels = { app: "tiny" }
95
+ spec.template.spec.containers = [
96
+ { name: "app", image: "app:latest" },
97
+ ]
98
+ })
99
+
100
+ Middleware::ResourcePreset.new.call(m)
101
+ container = m.resources.first.to_h.dig(:spec, :template, :spec, :containers, 0)
102
+
103
+ container.dig(:resources, :requests, :cpu).should == "100m"
104
+ end
105
+
106
+ it "injects_xlarge_preset" do
107
+ m = manifest(Kube::Cluster["Deployment"].new {
108
+ metadata.name = "big"
109
+ metadata.labels = { "app.kubernetes.io/size": "xlarge" }
110
+ spec.selector.matchLabels = { app: "big" }
111
+ spec.template.metadata.labels = { app: "big" }
112
+ spec.template.spec.containers = [
113
+ { name: "app", image: "app:latest" },
114
+ ]
115
+ })
116
+
117
+ Middleware::ResourcePreset.new.call(m)
118
+ container = m.resources.first.to_h.dig(:spec, :template, :spec, :containers, 0)
119
+
120
+ container.dig(:resources, :limits, :cpu).should == "3"
121
+ end
122
+
123
+ it "applies_to_all_containers" do
124
+ m = manifest(Kube::Cluster["Deployment"].new {
125
+ metadata.name = "multi"
126
+ metadata.labels = { "app.kubernetes.io/size": "micro" }
127
+ spec.selector.matchLabels = { app: "multi" }
128
+ spec.template.metadata.labels = { app: "multi" }
129
+ spec.template.spec.containers = [
130
+ { name: "app", image: "app:latest" },
131
+ { name: "sidecar", image: "sidecar:latest" },
132
+ ]
133
+ })
134
+
135
+ Middleware::ResourcePreset.new.call(m)
136
+ containers = m.resources.first.to_h.dig(:spec, :template, :spec, :containers)
137
+
138
+ containers.last.dig(:resources, :requests, :cpu).should == "250m"
139
+ end
140
+
141
+ it "skips_non_pod_bearing_resources" do
142
+ resource = Kube::Cluster["ConfigMap"].new {
143
+ metadata.name = "config"
144
+ metadata.labels = { "app.kubernetes.io/size": "small" }
145
+ }
146
+ m = manifest(resource)
147
+
148
+ Middleware::ResourcePreset.new.call(m)
149
+
150
+ m.resources.first.to_h.should == resource.to_h
151
+ end
152
+
153
+ it "skips_resources_without_size_label" do
154
+ m = manifest(Kube::Cluster["Deployment"].new {
155
+ metadata.name = "web"
156
+ spec.selector.matchLabels = { app: "web" }
157
+ spec.template.metadata.labels = { app: "web" }
158
+ spec.template.spec.containers = [
159
+ { name: "web", image: "nginx:latest" },
160
+ ]
161
+ })
162
+
163
+ Middleware::ResourcePreset.new.call(m)
164
+ container = m.resources.first.to_h.dig(:spec, :template, :spec, :containers, 0)
165
+
166
+ container[:resources].should.be.nil
167
+ end
168
+
169
+ it "raises_on_unknown_size" do
170
+ m = manifest(Kube::Cluster["Deployment"].new {
171
+ metadata.name = "web"
172
+ metadata.labels = { "app.kubernetes.io/size": "potato" }
173
+ spec.selector.matchLabels = { app: "web" }
174
+ spec.template.metadata.labels = { app: "web" }
175
+ spec.template.spec.containers = [
176
+ { name: "web", image: "nginx:latest" },
177
+ ]
178
+ })
179
+
180
+ lambda { Middleware::ResourcePreset.new.call(m) }.should.raise ArgumentError
181
+ end
182
+
183
+ it "applies_to_statefulset" do
184
+ m = manifest(Kube::Cluster["StatefulSet"].new {
185
+ metadata.name = "db"
186
+ metadata.labels = { "app.kubernetes.io/size": "medium" }
187
+ spec.selector.matchLabels = { app: "db" }
188
+ spec.template.metadata.labels = { app: "db" }
189
+ spec.template.spec.containers = [
190
+ { name: "postgres", image: "postgres:16" },
191
+ ]
192
+ })
193
+
194
+ Middleware::ResourcePreset.new.call(m)
195
+ container = m.resources.first.to_h.dig(:spec, :template, :spec, :containers, 0)
196
+
197
+ container.dig(:resources, :requests, :cpu).should == "500m"
198
+ end
199
+
200
+ it "preserves_existing_container_resources_via_deep_merge" do
201
+ m = manifest(Kube::Cluster["Deployment"].new {
202
+ metadata.name = "web"
203
+ metadata.labels = { "app.kubernetes.io/size": "small" }
204
+ spec.selector.matchLabels = { app: "web" }
205
+ spec.template.metadata.labels = { app: "web" }
206
+ spec.template.spec.containers = [
207
+ {
208
+ name: "web", image: "nginx:latest",
209
+ resources: { requests: { cpu: "999m" } },
210
+ },
211
+ ]
212
+ })
213
+
214
+ Middleware::ResourcePreset.new.call(m)
215
+ container = m.resources.first.to_h.dig(:spec, :template, :spec, :containers, 0)
216
+
217
+ # The container's explicit value wins over the preset
218
+ container.dig(:resources, :requests, :cpu).should == "999m"
219
+ end
220
+
221
+ private
222
+
223
+ def manifest(*resources)
224
+ m = Kube::Cluster::Manifest.new
225
+ resources.each { |r| m << r }
226
+ m
227
+ end
228
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
3
6
  module Kube
4
7
  module Cluster
5
8
  class Middleware
@@ -82,3 +85,143 @@ module Kube
82
85
  end
83
86
  end
84
87
  end
88
+
89
+ test do
90
+ Middleware = Kube::Cluster::Middleware
91
+
92
+ it "applies_restricted_profile_by_default" do
93
+ m = manifest(Kube::Cluster["Deployment"].new {
94
+ metadata.name = "web"
95
+ spec.selector.matchLabels = { app: "web" }
96
+ spec.template.metadata.labels = { app: "web" }
97
+ spec.template.spec.containers = [
98
+ { name: "web", image: "nginx:latest" },
99
+ ]
100
+ })
101
+
102
+ Middleware::SecurityContext.new.call(m)
103
+ h = m.resources.first.to_h
104
+ pod_sc = h.dig(:spec, :template, :spec, :securityContext)
105
+
106
+ pod_sc[:runAsNonRoot].should == true
107
+ end
108
+
109
+ it "applies_baseline_profile_via_label" do
110
+ m = manifest(Kube::Cluster["Deployment"].new {
111
+ metadata.name = "web"
112
+ metadata.labels = { "app.kubernetes.io/security": "baseline" }
113
+ spec.selector.matchLabels = { app: "web" }
114
+ spec.template.metadata.labels = { app: "web" }
115
+ spec.template.spec.containers = [
116
+ { name: "web", image: "nginx:latest" },
117
+ ]
118
+ })
119
+
120
+ Middleware::SecurityContext.new.call(m)
121
+ h = m.resources.first.to_h
122
+ pod_sc = h.dig(:spec, :template, :spec, :securityContext)
123
+
124
+ pod_sc[:runAsNonRoot].should == true
125
+ end
126
+
127
+ it "applies_baseline_profile_via_constructor_default" do
128
+ m = manifest(Kube::Cluster["Deployment"].new {
129
+ metadata.name = "web"
130
+ spec.selector.matchLabels = { app: "web" }
131
+ spec.template.metadata.labels = { app: "web" }
132
+ spec.template.spec.containers = [
133
+ { name: "web", image: "nginx:latest" },
134
+ ]
135
+ })
136
+
137
+ Middleware::SecurityContext.new(default: :baseline).call(m)
138
+ h = m.resources.first.to_h
139
+ pod_sc = h.dig(:spec, :template, :spec, :securityContext)
140
+
141
+ pod_sc[:seccompProfile].should.be.nil
142
+ end
143
+
144
+ it "label_overrides_constructor_default" do
145
+ m = manifest(Kube::Cluster["Deployment"].new {
146
+ metadata.name = "web"
147
+ metadata.labels = { "app.kubernetes.io/security": "restricted" }
148
+ spec.selector.matchLabels = { app: "web" }
149
+ spec.template.metadata.labels = { app: "web" }
150
+ spec.template.spec.containers = [
151
+ { name: "web", image: "nginx:latest" },
152
+ ]
153
+ })
154
+
155
+ Middleware::SecurityContext.new(default: :baseline).call(m)
156
+ h = m.resources.first.to_h
157
+ pod_sc = h.dig(:spec, :template, :spec, :securityContext)
158
+
159
+ pod_sc[:seccompProfile].should == { type: "RuntimeDefault" }
160
+ end
161
+
162
+ it "applies_to_all_containers" do
163
+ m = manifest(Kube::Cluster["Deployment"].new {
164
+ metadata.name = "web"
165
+ spec.selector.matchLabels = { app: "web" }
166
+ spec.template.metadata.labels = { app: "web" }
167
+ spec.template.spec.containers = [
168
+ { name: "app", image: "app:latest" },
169
+ { name: "sidecar", image: "sidecar:latest" },
170
+ ]
171
+ })
172
+
173
+ Middleware::SecurityContext.new.call(m)
174
+ containers = m.resources.first.to_h.dig(:spec, :template, :spec, :containers)
175
+
176
+ containers.last.dig(:securityContext, :allowPrivilegeEscalation).should == false
177
+ end
178
+
179
+ it "skips_non_pod_bearing_resources" do
180
+ resource = Kube::Cluster["ConfigMap"].new { metadata.name = "config" }
181
+ m = manifest(resource)
182
+
183
+ Middleware::SecurityContext.new.call(m)
184
+
185
+ m.resources.first.to_h.should == resource.to_h
186
+ end
187
+
188
+ it "raises_on_unknown_profile" do
189
+ m = manifest(Kube::Cluster["Deployment"].new {
190
+ metadata.name = "web"
191
+ metadata.labels = { "app.kubernetes.io/security": "yolo" }
192
+ spec.selector.matchLabels = { app: "web" }
193
+ spec.template.metadata.labels = { app: "web" }
194
+ spec.template.spec.containers = [
195
+ { name: "web", image: "nginx:latest" },
196
+ ]
197
+ })
198
+
199
+ lambda { Middleware::SecurityContext.new.call(m) }.should.raise ArgumentError
200
+ end
201
+
202
+ it "preserves_existing_pod_security_context" do
203
+ m = manifest(Kube::Cluster["Deployment"].new {
204
+ metadata.name = "web"
205
+ spec.selector.matchLabels = { app: "web" }
206
+ spec.template.metadata.labels = { app: "web" }
207
+ spec.template.spec.securityContext = { runAsUser: 9999 }
208
+ spec.template.spec.containers = [
209
+ { name: "web", image: "nginx:latest" },
210
+ ]
211
+ })
212
+
213
+ Middleware::SecurityContext.new.call(m)
214
+ pod_sc = m.resources.first.to_h.dig(:spec, :template, :spec, :securityContext)
215
+
216
+ # Existing value wins
217
+ pod_sc[:runAsUser].should == 9999
218
+ end
219
+
220
+ private
221
+
222
+ def manifest(*resources)
223
+ m = Kube::Cluster::Manifest.new
224
+ resources.each { |r| m << r }
225
+ m
226
+ end
227
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bundler/setup"
4
+ require "kube/cluster"
5
+
3
6
  module Kube
4
7
  module Cluster
5
8
  class Middleware
@@ -69,3 +72,144 @@ module Kube
69
72
  end
70
73
  end
71
74
  end
75
+
76
+ test do
77
+ Middleware = Kube::Cluster::Middleware
78
+
79
+ it "generates_service_from_deployment" do
80
+ m = manifest(Kube::Cluster["Deployment"].new {
81
+ metadata.name = "web"
82
+ metadata.namespace = "production"
83
+ metadata.labels = { app: "web" }
84
+ spec.selector.matchLabels = { app: "web" }
85
+ spec.template.metadata.labels = { app: "web" }
86
+ spec.template.spec.containers = [
87
+ { name: "web", image: "nginx", ports: [{ name: "http", containerPort: 8080, protocol: "TCP" }] },
88
+ ]
89
+ })
90
+
91
+ Middleware::ServiceForDeployment.new.call(m)
92
+
93
+ deploy, service = m.resources
94
+ sh = service.to_h
95
+ port = sh.dig(:spec, :ports, 0)
96
+
97
+ port[:protocol].should == "TCP"
98
+ end
99
+
100
+ it "maps_multiple_ports" do
101
+ m = manifest(Kube::Cluster["Deployment"].new {
102
+ metadata.name = "web"
103
+ spec.selector.matchLabels = { app: "web" }
104
+ spec.template.metadata.labels = { app: "web" }
105
+ spec.template.spec.containers = [
106
+ {
107
+ name: "web", image: "nginx",
108
+ ports: [
109
+ { name: "http", containerPort: 8080 },
110
+ { name: "metrics", containerPort: 9090 },
111
+ ],
112
+ },
113
+ ]
114
+ })
115
+
116
+ Middleware::ServiceForDeployment.new.call(m)
117
+ service = m.resources.last
118
+ ports = service.to_h.dig(:spec, :ports)
119
+
120
+ ports.size.should == 2
121
+ end
122
+
123
+ it "copies_labels_from_source" do
124
+ m = manifest(Kube::Cluster["Deployment"].new {
125
+ metadata.name = "web"
126
+ metadata.labels = { "app.kubernetes.io/name": "web", "app.kubernetes.io/size": "small" }
127
+ spec.selector.matchLabels = { app: "web" }
128
+ spec.template.metadata.labels = { app: "web" }
129
+ spec.template.spec.containers = [
130
+ { name: "web", image: "nginx", ports: [{ name: "http", containerPort: 8080 }] },
131
+ ]
132
+ })
133
+
134
+ Middleware::ServiceForDeployment.new.call(m)
135
+ service_labels = m.resources.last.to_h.dig(:metadata, :labels)
136
+
137
+ service_labels[:"app.kubernetes.io/size"].should == "small"
138
+ end
139
+
140
+ it "skips_deployment_without_named_ports" do
141
+ m = manifest(Kube::Cluster["Deployment"].new {
142
+ metadata.name = "web"
143
+ spec.selector.matchLabels = { app: "web" }
144
+ spec.template.metadata.labels = { app: "web" }
145
+ spec.template.spec.containers = [
146
+ { name: "web", image: "nginx", ports: [{ containerPort: 8080 }] },
147
+ ]
148
+ })
149
+
150
+ Middleware::ServiceForDeployment.new.call(m)
151
+
152
+ m.resources.size.should == 1
153
+ end
154
+
155
+ it "skips_deployment_without_ports" do
156
+ m = manifest(Kube::Cluster["Deployment"].new {
157
+ metadata.name = "worker"
158
+ spec.selector.matchLabels = { app: "worker" }
159
+ spec.template.metadata.labels = { app: "worker" }
160
+ spec.template.spec.containers = [
161
+ { name: "worker", image: "worker:latest" },
162
+ ]
163
+ })
164
+
165
+ Middleware::ServiceForDeployment.new.call(m)
166
+
167
+ m.resources.size.should == 1
168
+ end
169
+
170
+ it "skips_non_pod_bearing_resources" do
171
+ m = manifest(Kube::Cluster["ConfigMap"].new { metadata.name = "config" })
172
+
173
+ Middleware::ServiceForDeployment.new.call(m)
174
+
175
+ m.resources.size.should == 1
176
+ end
177
+
178
+ it "skips_deployment_without_match_labels" do
179
+ m = manifest(Kube::Cluster["Deployment"].new {
180
+ metadata.name = "web"
181
+ spec.template.metadata.labels = { app: "web" }
182
+ spec.template.spec.containers = [
183
+ { name: "web", image: "nginx", ports: [{ name: "http", containerPort: 8080 }] },
184
+ ]
185
+ })
186
+
187
+ Middleware::ServiceForDeployment.new.call(m)
188
+
189
+ m.resources.size.should == 1
190
+ end
191
+
192
+ it "works_with_statefulset" do
193
+ m = manifest(Kube::Cluster["StatefulSet"].new {
194
+ metadata.name = "db"
195
+ metadata.namespace = "database"
196
+ spec.selector.matchLabels = { app: "db" }
197
+ spec.template.metadata.labels = { app: "db" }
198
+ spec.template.spec.containers = [
199
+ { name: "postgres", image: "postgres:16", ports: [{ name: "tcp-pg", containerPort: 5432 }] },
200
+ ]
201
+ })
202
+
203
+ Middleware::ServiceForDeployment.new.call(m)
204
+
205
+ m.resources.last.to_h.dig(:metadata, :namespace).should == "database"
206
+ end
207
+
208
+ private
209
+
210
+ def manifest(*resources)
211
+ m = Kube::Cluster::Manifest.new
212
+ resources.each { |r| m << r }
213
+ m
214
+ end
215
+ end