kube_schema 1.12.0 → 2.0.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.
- checksums.yaml +4 -4
- data/.envrc +1 -0
- data/.github/FUNDING.yml +3 -0
- data/.rubocop.yml +226 -5984
- data/CHANGELOG.md +110 -3
- data/DEPRECATIONS.md +172 -0
- data/Gemfile +13 -3
- data/Gemfile.lock +41 -31
- data/RELEASE.md +161 -0
- data/Rakefile +1 -1
- data/bin/rubocop +3 -0
- data/bin/test +2 -9
- data/cops/begin_block_assignment.rb +34 -0
- data/cops/comment_structure.rb +307 -0
- data/cops/constant_matches_path.rb +142 -0
- data/cops/multiline_method_arguments.rb +89 -0
- data/cops/no_conditional_assignment.rb +186 -0
- data/cops/no_console_logging.rb +62 -0
- data/cops/no_modifier_if.rb +74 -0
- data/cops/no_return.rb +152 -0
- data/cops/support/prism_source.rb +234 -0
- data/cops/use_tap.rb +100 -0
- data/flake.lock +3 -3
- data/flake.nix +22 -15
- data/gemset.nix +142 -73
- data/kube_schema.gemspec +4 -2
- data/lib/kube/errors.rb +652 -645
- data/lib/kube/monkey_patches.rb +5 -6
- data/lib/kube/schema/instance.rb +344 -363
- data/lib/kube/schema/lazy.rb +370 -0
- data/lib/kube/schema/manifest.rb +286 -325
- data/lib/kube/schema/resource.rb +348 -393
- data/lib/kube/schema/sub_spec.rb +261 -293
- data/lib/kube/schema.rb +173 -172
- data/lib/{kube/schema → kube_schema}/version.rb +1 -1
- data/lib/kube_schema.rb +6 -0
- data/readme.md +5 -0
- metadata +49 -9
- data/bin/increment-version +0 -45
- data/bin/release-gem +0 -32
- data/bin/tag-version +0 -19
- data/lib/kube/schema/version.rb.erb +0 -7
data/lib/kube/schema/sub_spec.rb
CHANGED
|
@@ -2,28 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module Kube
|
|
4
4
|
module Schema
|
|
5
|
-
# A
|
|
6
|
-
# definitions — things like Container, ContainerPort, Volume, Probe,
|
|
7
|
-
# etc. that live inside resource specs but have no apiVersion/kind.
|
|
8
|
-
#
|
|
9
|
-
# SubSpec validates against the OpenAPI JSON Schema definition and
|
|
10
|
-
# produces a plain Hash via #to_h, suitable for embedding directly
|
|
11
|
-
# inside Resource specs.
|
|
5
|
+
# A schema-validated wrapper for non-resource Kubernetes definitions.
|
|
12
6
|
#
|
|
7
|
+
# @example
|
|
13
8
|
# container = Kube::Schema::SubSpec["Container"].new {
|
|
14
9
|
# name = "app"
|
|
15
10
|
# image = "nginx:1.27"
|
|
16
11
|
# ports = [{ containerPort: 80 }]
|
|
17
12
|
# }
|
|
18
|
-
#
|
|
19
13
|
# container.valid? # => true
|
|
20
14
|
# container.to_h # => { name: "app", image: "nginx:1.27", ... }
|
|
21
15
|
#
|
|
22
|
-
#
|
|
23
|
-
# no explicit .to_h is needed:
|
|
24
|
-
#
|
|
16
|
+
# @example Auto-coerces inside a Resource, no #to_h needed
|
|
25
17
|
# spec.template.spec.containers = [container]
|
|
26
|
-
#
|
|
27
18
|
class SubSpec
|
|
28
19
|
|
|
29
20
|
def initialize(hash = {}, &block)
|
|
@@ -42,7 +33,8 @@ module Kube
|
|
|
42
33
|
end
|
|
43
34
|
end
|
|
44
35
|
|
|
45
|
-
#
|
|
36
|
+
# Overridden by the factory in Kube::Schema::Instance.
|
|
37
|
+
# @returns [JSONSchemer::Schema, nil] the schema, once overridden.
|
|
46
38
|
def self.schema
|
|
47
39
|
raise "Kube::Schema::SubSpec should NOT be instantiated directly"
|
|
48
40
|
end
|
|
@@ -63,7 +55,9 @@ module Kube
|
|
|
63
55
|
end
|
|
64
56
|
end
|
|
65
57
|
|
|
66
|
-
# Like #valid? but raises
|
|
58
|
+
# Like #valid? but raises on failure.
|
|
59
|
+
# @returns [Boolean] true if the sub-spec is valid.
|
|
60
|
+
# @raises [Kube::ValidationError] with details on failure.
|
|
67
61
|
def valid!
|
|
68
62
|
if self.class.schema.nil?
|
|
69
63
|
true
|
|
@@ -72,9 +66,10 @@ module Kube
|
|
|
72
66
|
errors = self.class.schema.validate(data).to_a
|
|
73
67
|
|
|
74
68
|
unless errors.empty?
|
|
75
|
-
raise Kube::ValidationError.new(
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
raise Kube::ValidationError.new(
|
|
70
|
+
errors,
|
|
71
|
+
kind: self.class.definition_name,
|
|
72
|
+
manifest: data,
|
|
78
73
|
)
|
|
79
74
|
end
|
|
80
75
|
|
|
@@ -83,6 +78,7 @@ module Kube
|
|
|
83
78
|
end
|
|
84
79
|
|
|
85
80
|
# Returns the sub-spec data as a plain Hash.
|
|
81
|
+
# @returns [Hash] the sub-spec data, symbol-keyed.
|
|
86
82
|
def to_h
|
|
87
83
|
data = deep_compact(@data)
|
|
88
84
|
data.reject { |_, v| v.is_a?(Hash) && v.empty? }
|
|
@@ -94,10 +90,10 @@ module Kube
|
|
|
94
90
|
|
|
95
91
|
# Look up a sub-spec definition by short name.
|
|
96
92
|
#
|
|
93
|
+
# @example
|
|
97
94
|
# Kube::Schema::SubSpec["Container"]
|
|
98
95
|
# Kube::Schema::SubSpec["ContainerPort"]
|
|
99
96
|
# Kube::Schema::SubSpec["Volume"]
|
|
100
|
-
#
|
|
101
97
|
class << self
|
|
102
98
|
def [](name)
|
|
103
99
|
version = Schema.schema_version || Schema::DEFAULT_VERSION
|
|
@@ -113,7 +109,9 @@ module Kube
|
|
|
113
109
|
when Hash
|
|
114
110
|
obj.each_with_object({}) do |(k, v), result|
|
|
115
111
|
compacted = deep_compact(v)
|
|
116
|
-
|
|
112
|
+
unless compacted.nil?
|
|
113
|
+
result[k] = compacted
|
|
114
|
+
end
|
|
117
115
|
end
|
|
118
116
|
when Array
|
|
119
117
|
obj.map { |v| deep_compact(v) }
|
|
@@ -151,335 +149,305 @@ module Kube
|
|
|
151
149
|
end
|
|
152
150
|
end
|
|
153
151
|
|
|
154
|
-
if __FILE__ == $0
|
|
155
|
-
require "bundler/setup"
|
|
156
|
-
require "rspec/autorun"
|
|
157
|
-
require "kube/schema"
|
|
158
|
-
|
|
159
|
-
RSpec.describe Kube::Schema::SubSpec do
|
|
160
|
-
describe ".[]" do
|
|
161
|
-
it "returns a Class that subclasses SubSpec" do
|
|
162
|
-
klass = described_class["Container"]
|
|
163
|
-
expect(klass).to be_a(Class)
|
|
164
|
-
expect(klass).to be < described_class
|
|
165
|
-
end
|
|
166
152
|
|
|
167
|
-
|
|
168
|
-
a = described_class["Container"]
|
|
169
|
-
b = described_class["Container"]
|
|
170
|
-
expect(a).to be(b)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
it "resolves ContainerPort" do
|
|
174
|
-
klass = described_class["ContainerPort"]
|
|
175
|
-
expect(klass).to be < described_class
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
it "resolves Probe" do
|
|
179
|
-
klass = described_class["Probe"]
|
|
180
|
-
expect(klass).to be < described_class
|
|
181
|
-
end
|
|
153
|
+
__END__
|
|
182
154
|
|
|
183
|
-
|
|
184
|
-
klass = described_class["Volume"]
|
|
185
|
-
expect(klass).to be < described_class
|
|
186
|
-
end
|
|
155
|
+
require "kube/schema"
|
|
187
156
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
expect(klass).to be < described_class
|
|
191
|
-
end
|
|
157
|
+
sub_spec = Kube::Schema::SubSpec
|
|
158
|
+
container = lambda { sub_spec["Container"] }
|
|
192
159
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
160
|
+
describe "Kube::Schema::SubSpec" do
|
|
161
|
+
describe ".[]" do
|
|
162
|
+
it "returns a Class that subclasses SubSpec" do
|
|
163
|
+
klass = sub_spec["Container"]
|
|
164
|
+
klass.should.be.kind_of Class
|
|
165
|
+
klass.should.be < sub_spec
|
|
166
|
+
end
|
|
197
167
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
end
|
|
168
|
+
it "caches sub-spec classes by name" do
|
|
169
|
+
sub_spec["Container"].should.be.identical_to sub_spec["Container"]
|
|
170
|
+
end
|
|
202
171
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
end
|
|
172
|
+
it "resolves ContainerPort" do
|
|
173
|
+
sub_spec["ContainerPort"].should.be < sub_spec
|
|
206
174
|
end
|
|
207
175
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
klass = described_class["Container"]
|
|
211
|
-
expect(klass.definition_name).to eq("Container")
|
|
212
|
-
end
|
|
176
|
+
it "resolves Probe" do
|
|
177
|
+
sub_spec["Probe"].should.be < sub_spec
|
|
213
178
|
end
|
|
214
179
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
klass = described_class["Container"]
|
|
218
|
-
expect(klass.schema).not_to be_nil
|
|
219
|
-
end
|
|
180
|
+
it "resolves Volume" do
|
|
181
|
+
sub_spec["Volume"].should.be < sub_spec
|
|
220
182
|
end
|
|
221
183
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
klass = described_class["Container"]
|
|
225
|
-
props = klass.schema_properties
|
|
226
|
-
expect(props).to include(:name, :image, :ports, :env, :resources)
|
|
227
|
-
end
|
|
184
|
+
it "resolves EnvVar" do
|
|
185
|
+
sub_spec["EnvVar"].should.be < sub_spec
|
|
228
186
|
end
|
|
229
187
|
|
|
230
|
-
|
|
231
|
-
|
|
188
|
+
it "resolves ResourceRequirements" do
|
|
189
|
+
sub_spec["ResourceRequirements"].should.be < sub_spec
|
|
190
|
+
end
|
|
232
191
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
end
|
|
192
|
+
it "resolves a full definition key" do
|
|
193
|
+
sub_spec["io.k8s.api.core.v1.Container"].should.be < sub_spec
|
|
194
|
+
end
|
|
237
195
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
196
|
+
it "raises for an unknown definition" do
|
|
197
|
+
error = lambda { sub_spec["ThisDoesNotExist999"] }.should.raise(RuntimeError)
|
|
198
|
+
error.message.should.match(/No definition found/)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
242
201
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
202
|
+
describe ".definition_name" do
|
|
203
|
+
it "returns the short name used to look up the sub-spec" do
|
|
204
|
+
container.().definition_name.should == "Container"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
247
207
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
expect(sub.to_h).to include(name: "app", image: "nginx:1.27")
|
|
254
|
-
end
|
|
208
|
+
describe ".schema" do
|
|
209
|
+
it "has a schema attached" do
|
|
210
|
+
container.().schema.should.not.be.nil
|
|
211
|
+
end
|
|
212
|
+
end
|
|
255
213
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
expect(sub.to_h[:resources][:requests]).to eq({ cpu: "100m", memory: "128Mi" })
|
|
264
|
-
end
|
|
214
|
+
describe ".schema_properties" do
|
|
215
|
+
it "lists known properties as symbols" do
|
|
216
|
+
props = container.().schema_properties
|
|
217
|
+
[:name, :image, :ports, :env, :resources].each { |prop| props.should.include prop }
|
|
218
|
+
end
|
|
219
|
+
end
|
|
265
220
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
221
|
+
describe "#initialize" do
|
|
222
|
+
it "accepts a hash" do
|
|
223
|
+
sub = container.().new(name: "app", image: "nginx")
|
|
224
|
+
sub.to_h[:name].should == "app"
|
|
225
|
+
sub.to_h[:image].should == "nginx"
|
|
270
226
|
end
|
|
271
227
|
|
|
272
|
-
|
|
273
|
-
|
|
228
|
+
it "accepts string keys" do
|
|
229
|
+
sub = container.().new("name" => "app", "image" => "nginx")
|
|
230
|
+
sub.to_h[:name].should == "app"
|
|
231
|
+
sub.to_h[:image].should == "nginx"
|
|
232
|
+
end
|
|
274
233
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
expect(sub.image).to eq("nginx")
|
|
279
|
-
end
|
|
234
|
+
it "creates an empty sub-spec when no arguments are given" do
|
|
235
|
+
container.().new.to_h.should == {}
|
|
236
|
+
end
|
|
280
237
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
238
|
+
it "accepts a block for DSL-style initialization" do
|
|
239
|
+
sub = container.().new {
|
|
240
|
+
self.name = "app"
|
|
241
|
+
self.image = "nginx:1.27"
|
|
242
|
+
}
|
|
243
|
+
sub.to_h[:name].should == "app"
|
|
244
|
+
sub.to_h[:image].should == "nginx:1.27"
|
|
285
245
|
end
|
|
286
246
|
|
|
287
|
-
|
|
288
|
-
|
|
247
|
+
it "supports nested DSL" do
|
|
248
|
+
sub = container.().new {
|
|
249
|
+
self.name = "app"
|
|
250
|
+
self.image = "nginx"
|
|
251
|
+
self.resources.requests = { cpu: "100m", memory: "128Mi" }
|
|
252
|
+
self.resources.limits = { cpu: "500m", memory: "256Mi" }
|
|
253
|
+
}
|
|
254
|
+
sub.to_h[:resources][:requests].should == { cpu: "100m", memory: "128Mi" }
|
|
255
|
+
end
|
|
289
256
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
257
|
+
it "ignores unknown properties" do
|
|
258
|
+
container.().new(name: "app", bogus_field: "ignored").to_h.should == { name: "app" }
|
|
259
|
+
end
|
|
260
|
+
end
|
|
294
261
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
262
|
+
describe "accessor methods" do
|
|
263
|
+
it "defines reader methods for schema properties" do
|
|
264
|
+
sub = container.().new(name: "app", image: "nginx")
|
|
265
|
+
sub.name.should == "app"
|
|
266
|
+
sub.image.should == "nginx"
|
|
267
|
+
end
|
|
299
268
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
sub = klass.new(image: "nginx")
|
|
303
|
-
expect(sub.valid?).to be false
|
|
304
|
-
end
|
|
269
|
+
it "returns nil for unset properties" do
|
|
270
|
+
container.().new(name: "app").image.should.be.nil
|
|
305
271
|
end
|
|
272
|
+
end
|
|
306
273
|
|
|
307
|
-
|
|
308
|
-
|
|
274
|
+
describe "#valid?" do
|
|
275
|
+
it "returns true for valid data" do
|
|
276
|
+
container.().new(name: "app", image: "nginx").valid?.should.be.true
|
|
277
|
+
end
|
|
309
278
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
end
|
|
279
|
+
it "returns false for data violating the schema" do
|
|
280
|
+
container.().new(name: "app", ports: "not_an_array").valid?.should.be.false
|
|
281
|
+
end
|
|
314
282
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
283
|
+
it "returns false when required fields are missing" do
|
|
284
|
+
# Container requires 'name'
|
|
285
|
+
container.().new(image: "nginx").valid?.should.be.false
|
|
286
|
+
end
|
|
287
|
+
end
|
|
319
288
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
289
|
+
describe "#valid!" do
|
|
290
|
+
it "returns true for valid data" do
|
|
291
|
+
container.().new(name: "app").valid!.should.be.true
|
|
292
|
+
end
|
|
324
293
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
expect(error.message).to include("name is required but missing")
|
|
329
|
-
end
|
|
330
|
-
end
|
|
294
|
+
it "raises ValidationError for invalid data" do
|
|
295
|
+
sub = container.().new(image: "nginx")
|
|
296
|
+
lambda { sub.valid! }.should.raise(Kube::ValidationError)
|
|
331
297
|
end
|
|
332
298
|
|
|
333
|
-
|
|
334
|
-
|
|
299
|
+
it "includes the definition name in the error" do
|
|
300
|
+
sub = container.().new(image: "nginx")
|
|
301
|
+
error = lambda { sub.valid! }.should.raise(Kube::ValidationError)
|
|
302
|
+
error.message.should.match(/Container/)
|
|
303
|
+
end
|
|
335
304
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
305
|
+
it "shows which required keys are missing" do
|
|
306
|
+
sub = container.().new(image: "nginx")
|
|
307
|
+
error = lambda { sub.valid! }.should.raise(Kube::ValidationError)
|
|
308
|
+
error.message.should.include "name is required but missing"
|
|
309
|
+
end
|
|
310
|
+
end
|
|
342
311
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
312
|
+
describe "#to_h" do
|
|
313
|
+
it "returns a plain Hash" do
|
|
314
|
+
h = container.().new(name: "app", image: "nginx").to_h
|
|
315
|
+
h.should.be.kind_of Hash
|
|
316
|
+
h.should == { name: "app", image: "nginx" }
|
|
317
|
+
end
|
|
347
318
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
expect(sub.to_h).not_to have_key(:kind)
|
|
352
|
-
end
|
|
319
|
+
it "strips empty sub-hashes" do
|
|
320
|
+
container.().new(name: "app").to_h.should == { name: "app" }
|
|
321
|
+
end
|
|
353
322
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
self.ports = [{ containerPort: 80 }]
|
|
359
|
-
}
|
|
360
|
-
expect(sub.to_h[:ports]).to eq([{ containerPort: 80 }])
|
|
361
|
-
end
|
|
323
|
+
it "does NOT include apiVersion or kind" do
|
|
324
|
+
h = container.().new(name: "app").to_h
|
|
325
|
+
h.should.not.include :apiVersion
|
|
326
|
+
h.should.not.include :kind
|
|
362
327
|
end
|
|
363
328
|
|
|
364
|
-
|
|
365
|
-
|
|
329
|
+
it "preserves nested structure" do
|
|
330
|
+
sub = container.().new {
|
|
331
|
+
self.name = "app"
|
|
332
|
+
self.image = "nginx"
|
|
333
|
+
self.ports = [{ containerPort: 80 }]
|
|
334
|
+
}
|
|
335
|
+
sub.to_h[:ports].should == [{ containerPort: 80 }]
|
|
336
|
+
end
|
|
337
|
+
end
|
|
366
338
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
339
|
+
describe "#==" do
|
|
340
|
+
it "considers two sub-specs equal when their data matches" do
|
|
341
|
+
a = container.().new(name: "app", image: "nginx")
|
|
342
|
+
b = container.().new(name: "app", image: "nginx")
|
|
343
|
+
a.should == b
|
|
344
|
+
end
|
|
372
345
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
346
|
+
it "considers two sub-specs unequal when their data differs" do
|
|
347
|
+
a = container.().new(name: "app", image: "nginx")
|
|
348
|
+
b = container.().new(name: "other", image: "nginx")
|
|
349
|
+
a.should.not == b
|
|
350
|
+
end
|
|
378
351
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
expect(sub).not_to eq({ name: "app" })
|
|
382
|
-
end
|
|
352
|
+
it "is not equal to a plain Hash" do
|
|
353
|
+
container.().new(name: "app").should.not == { name: "app" }
|
|
383
354
|
end
|
|
355
|
+
end
|
|
384
356
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
end
|
|
357
|
+
describe "auto-coercion in Resource" do
|
|
358
|
+
it "auto-coerces SubSpec instances inside Resource arrays" do
|
|
359
|
+
app = container.().new {
|
|
360
|
+
self.name = "app"
|
|
361
|
+
self.image = "nginx:1.27"
|
|
362
|
+
self.ports = [{ containerPort: 80 }]
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
deploy = Kube::Schema["Deployment"].new {
|
|
366
|
+
metadata.name = "web"
|
|
367
|
+
spec.replicas = 1
|
|
368
|
+
spec.selector.matchLabels = { app: "web" }
|
|
369
|
+
spec.template.metadata.labels = { app: "web" }
|
|
370
|
+
spec.template.spec.containers = [app]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
containers = deploy.to_h[:spec][:template][:spec][:containers]
|
|
374
|
+
containers.should.be.kind_of Array
|
|
375
|
+
containers.first.should.be.kind_of Hash
|
|
376
|
+
containers.first[:name].should == "app"
|
|
377
|
+
containers.first[:image].should == "nginx:1.27"
|
|
378
|
+
end
|
|
408
379
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
end
|
|
380
|
+
it "produces valid YAML with auto-coerced SubSpec" do
|
|
381
|
+
nginx = container.().new {
|
|
382
|
+
self.name = "nginx"
|
|
383
|
+
self.image = "nginx:1.27"
|
|
384
|
+
self.ports = [{ containerPort: 80 }]
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
deploy = Kube::Schema["Deployment"].new {
|
|
388
|
+
metadata.name = "web"
|
|
389
|
+
spec.replicas = 1
|
|
390
|
+
spec.selector.matchLabels = { app: "web" }
|
|
391
|
+
spec.template.metadata.labels = { app: "web" }
|
|
392
|
+
spec.template.spec.containers = [nginx]
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
parsed = YAML.safe_load(deploy.to_yaml)
|
|
396
|
+
parsed["spec"]["template"]["spec"]["containers"].first["name"].should == "nginx"
|
|
397
|
+
end
|
|
428
398
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
399
|
+
it "handles multiple SubSpec instances in an array" do
|
|
400
|
+
app = container.().new(name: "app", image: "app:latest")
|
|
401
|
+
sidecar = container.().new(name: "sidecar", image: "sidecar:latest")
|
|
402
|
+
|
|
403
|
+
deploy = Kube::Schema["Deployment"].new {
|
|
404
|
+
metadata.name = "web"
|
|
405
|
+
spec.replicas = 1
|
|
406
|
+
spec.selector.matchLabels = { app: "web" }
|
|
407
|
+
spec.template.metadata.labels = { app: "web" }
|
|
408
|
+
spec.template.spec.containers = [app, sidecar]
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
containers = deploy.to_h[:spec][:template][:spec][:containers]
|
|
412
|
+
containers.size.should == 2
|
|
413
|
+
containers.map { |c| c[:name] }.should == ["app", "sidecar"]
|
|
414
|
+
end
|
|
445
415
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
end
|
|
416
|
+
it "works mixed with plain hashes" do
|
|
417
|
+
typed = container.().new(name: "typed", image: "typed:latest")
|
|
418
|
+
|
|
419
|
+
deploy = Kube::Schema["Deployment"].new {
|
|
420
|
+
metadata.name = "web"
|
|
421
|
+
spec.replicas = 1
|
|
422
|
+
spec.selector.matchLabels = { app: "web" }
|
|
423
|
+
spec.template.metadata.labels = { app: "web" }
|
|
424
|
+
spec.template.spec.containers = [
|
|
425
|
+
typed,
|
|
426
|
+
{ name: "plain", image: "plain:latest" }
|
|
427
|
+
]
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
containers = deploy.to_h[:spec][:template][:spec][:containers]
|
|
431
|
+
containers.map { |c| c[:name] }.should == ["typed", "plain"]
|
|
463
432
|
end
|
|
433
|
+
end
|
|
464
434
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
end
|
|
435
|
+
describe "disambiguation" do
|
|
436
|
+
it "prefers stable API versions over beta/alpha" do
|
|
437
|
+
# MatchCondition exists in v1, v1alpha1, and v1beta1
|
|
438
|
+
klass = sub_spec["MatchCondition"]
|
|
439
|
+
klass.should.be < sub_spec
|
|
440
|
+
klass.schema.should.not.be.nil
|
|
472
441
|
end
|
|
442
|
+
end
|
|
473
443
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
expect(defs).to eq(defs.sort)
|
|
482
|
-
end
|
|
444
|
+
describe "Instance#list_definitions" do
|
|
445
|
+
it "returns a sorted array of short definition names" do
|
|
446
|
+
defs = Kube::Schema::Instance.new("1.34").list_definitions
|
|
447
|
+
defs.should.be.kind_of Array
|
|
448
|
+
defs.should.not.be.empty
|
|
449
|
+
["Container", "ContainerPort", "Probe", "Volume"].each { |name| defs.should.include name }
|
|
450
|
+
defs.should == defs.sort
|
|
483
451
|
end
|
|
484
452
|
end
|
|
485
453
|
end
|