kube-dsl 0.4.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +293 -0
- data/lib/kube-dsl/dsl/pkg/version/info.rb +1 -19
- data/lib/kube-dsl/validations.rb +23 -17
- data/lib/kube-dsl/value_fields.rb +1 -1
- data/lib/kube-dsl/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b49cbbb2071a035cf03ba3b5a945ac776fb3df15573875f8d5790f25335aeae
|
4
|
+
data.tar.gz: bc2c169581f445a84dd1104935b6a4b89273bd52d87e4091dd0d6fca884b2d0a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: faae392718b9cc76a9d699c77fed71764ef503918e75b3a83e9255259c4db7bb279c0d0db377e6913c78c623dc40d6f14c73921e7d0aa951c4a1a550dc9f5dd6
|
7
|
+
data.tar.gz: 61be1d7e1693604c30d6189517e23d260f6c27bfb4926ba8070dd75cb0d8a487beb877a70158801595f2c86bcbeacf7806dc5829aabedf3396692a6168ab76c1
|
data/CHANGELOG.md
CHANGED
data/README.md
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
## kube-dsl
|
2
|
+
A Ruby DSL for defining Kubernetes resources.
|
3
|
+
|
4
|
+
## What is this thing?
|
5
|
+
|
6
|
+
KubeDSL provides a [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) for defining Kubernetes resource objects in Ruby. Why would you want to do this? Well,
|
7
|
+
|
8
|
+
1. I think Ruby code is easier to read than YAML.
|
9
|
+
1. every property is a Ruby method, meaning Ruby will blow up if you try to configure the object the wrong way.
|
10
|
+
1. doing so follows the principle of [infrastructure as code](https://en.wikipedia.org/wiki/Infrastructure_as_code).
|
11
|
+
1. validations are built-in.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Either run `gem install kube-dsl` or add it to your Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'kube-dsl', '< 1'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
KubeDSL directly mirrors the fields and nesting structure of each Kubernetes YAML (or JSON) object. Ruby fields are snake_cased while Kubernetes fields are camelCased. Let's take a look at a short example where we define a Namespace:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
ns = KubeDSL.namespace do
|
27
|
+
metadata do
|
28
|
+
name 'my-namespace'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
In the example above, we've defined a `KubeDSL::DSL::V1::Namespace` object and assigned it to a local variable called `ns`. Now let's convert it into a YAML string and print it out:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
# ---
|
37
|
+
# apiVersion: v1
|
38
|
+
# kind: Namespace
|
39
|
+
# metadata:
|
40
|
+
# name: foo
|
41
|
+
puts ns.to_resource.to_yaml
|
42
|
+
```
|
43
|
+
|
44
|
+
The `#to_resource` method returns an instance of the `KubeDSL::Resource` class while `#to_yaml` converts the resource into a YAML string. Pretty cool, eh? See the next few sections for examples creating other types of common Kubernetes objects.
|
45
|
+
|
46
|
+
### ServiceAccount Example
|
47
|
+
|
48
|
+
Note how key/value pairs are added to the `labels` field.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
KubeDSL.service_account do
|
52
|
+
metadata do
|
53
|
+
name 'my-service-account'
|
54
|
+
namespace 'my-namespace'
|
55
|
+
|
56
|
+
labels do
|
57
|
+
add :app, 'my-app'
|
58
|
+
add :role, 'web'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
### Service Example
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
KubeDSL.service do
|
68
|
+
metadata do
|
69
|
+
name 'my-service'
|
70
|
+
namespace 'my-namespace'
|
71
|
+
|
72
|
+
labels do
|
73
|
+
add :app, 'my-app'
|
74
|
+
add :role, 'web'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
spec do
|
79
|
+
type 'NodePort'
|
80
|
+
|
81
|
+
selector do
|
82
|
+
add :app, 'my-app'
|
83
|
+
add :role, 'web'
|
84
|
+
end
|
85
|
+
|
86
|
+
port do
|
87
|
+
name 'http'
|
88
|
+
port 3000
|
89
|
+
protocol 'TCP'
|
90
|
+
target_port 'http'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
### Deployment Example
|
97
|
+
|
98
|
+
Note:
|
99
|
+
|
100
|
+
1. Elements of arrays can be given names (see the use of the `#container` method below) so they can be easily retrieved and/or modified later.
|
101
|
+
1. The example below shows how to add config maps and secrets to a deployment via references.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
KubeDSL.deployment do
|
105
|
+
metadata do
|
106
|
+
name 'my-deployment'
|
107
|
+
namespace 'my-namespace'
|
108
|
+
|
109
|
+
labels do
|
110
|
+
add :app, 'my-app'
|
111
|
+
add :role, 'web'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
spec do
|
116
|
+
replicas 2
|
117
|
+
|
118
|
+
selector do
|
119
|
+
match_labels do
|
120
|
+
add :app, 'my-app'
|
121
|
+
add :role, 'web'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
strategy do
|
126
|
+
type 'RollingUpdate'
|
127
|
+
|
128
|
+
rolling_update do
|
129
|
+
max_surge '25%'
|
130
|
+
max_unavailable 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
template do
|
135
|
+
metadata do
|
136
|
+
labels do
|
137
|
+
add :app, 'my-app'
|
138
|
+
add :role, 'web'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
spec do
|
143
|
+
# elements of arrays can be given names (:web in this case) so they can be
|
144
|
+
# easily retrieved and/or modified later
|
145
|
+
container(:web) do
|
146
|
+
name 'my-web-container'
|
147
|
+
image_pull_policy 'IfNotPresent'
|
148
|
+
|
149
|
+
port do
|
150
|
+
container_port 3000
|
151
|
+
name 'http'
|
152
|
+
protocol 'TCP'
|
153
|
+
end
|
154
|
+
|
155
|
+
env_from do
|
156
|
+
config_map_ref do
|
157
|
+
name 'my-config-map'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
env_from do
|
162
|
+
secret_ref do
|
163
|
+
name 'my-secrets'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
readiness_probe do
|
168
|
+
success_threshold 1
|
169
|
+
failure_threshold 2
|
170
|
+
initial_delay_seconds 15
|
171
|
+
period_seconds 3
|
172
|
+
timeout_seconds 1
|
173
|
+
|
174
|
+
http_get do
|
175
|
+
path '/healthz'
|
176
|
+
port 3000
|
177
|
+
scheme 'HTTP'
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
image_pull_secret do
|
183
|
+
name 'my-registry-secret'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
restart_policy 'Always'
|
188
|
+
service_account_name 'my-service-account'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
### Ingress Example
|
195
|
+
|
196
|
+
NOTE: the example below includes an annotation that is specific to the Nginx ingress controller.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
KubeDSL::DSL::Extensions::V1beta1::Ingress.new do
|
200
|
+
metadata do
|
201
|
+
name 'my-ingress'
|
202
|
+
namespace 'my-namespace'
|
203
|
+
|
204
|
+
annotations do
|
205
|
+
add :'kubernetes.io/ingress.class', 'nginx'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
spec do
|
210
|
+
rule do
|
211
|
+
host 'my-website.com'
|
212
|
+
|
213
|
+
http do
|
214
|
+
path do
|
215
|
+
path '/'
|
216
|
+
|
217
|
+
backend do
|
218
|
+
service_name 'my-service'
|
219
|
+
service_port 80
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
### ConfigMap Example
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
KubeDSL.config_map do
|
232
|
+
metadata do
|
233
|
+
name 'my-config-map'
|
234
|
+
namespace 'my-namespace'
|
235
|
+
end
|
236
|
+
|
237
|
+
data do
|
238
|
+
add :MY_VAR, 'value'
|
239
|
+
add :MY_OTHER_VAR, 'value'
|
240
|
+
end
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
### Secret Example
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
KubeDSL.secret do
|
248
|
+
metadata do
|
249
|
+
name 'my-secrets'
|
250
|
+
namespace 'my-namespace'
|
251
|
+
end
|
252
|
+
|
253
|
+
type 'Opaque'
|
254
|
+
|
255
|
+
data do
|
256
|
+
add :MY_SECRET, 'value'
|
257
|
+
end
|
258
|
+
end
|
259
|
+
```
|
260
|
+
|
261
|
+
## Validations
|
262
|
+
|
263
|
+
All `KubeDSL::DSLObject`s respond to `#valid?` and `#validate` methods. Use `#valid?` to determine whether or not an object is valid. Use `#validate` to retrieve a list of validation errors:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
ns = KubeDSL.namespace do
|
267
|
+
metadata do
|
268
|
+
name 123
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
ns.valid? # => false
|
273
|
+
ns.validate # => #<KubeDSL::Validations::ValidationErrors:0x00007fc8ce276e80 ... >
|
274
|
+
ns.validate.messages # => {"metadata.name"=>["is not a String"]}
|
275
|
+
```
|
276
|
+
|
277
|
+
The handy `#validate!` method will raise a `KubeDSL::ValidationError` if the object is invalid.
|
278
|
+
|
279
|
+
## Code Generation
|
280
|
+
|
281
|
+
All the Ruby code present in KubeDSL is generated from the Kubernetes JSON schema available [here](https://github.com/instrumenta/kubernetes-json-schema). Run the following rake task to regenerate:
|
282
|
+
|
283
|
+
```bash
|
284
|
+
bundle exec rake generate
|
285
|
+
```
|
286
|
+
|
287
|
+
## Authors
|
288
|
+
|
289
|
+
* Cameron C. Dutro: http://github.com/camertron
|
290
|
+
|
291
|
+
## License
|
292
|
+
|
293
|
+
Licensed under the MIT license.
|
@@ -1,24 +1,6 @@
|
|
1
1
|
module KubeDSL::DSL::Pkg::Version
|
2
2
|
class Info < ::KubeDSL::DSLObject
|
3
|
-
|
4
|
-
value_field :compiler
|
5
|
-
value_field :git_commit
|
6
|
-
value_field :git_tree_state
|
7
|
-
value_field :git_version
|
8
|
-
value_field :go_version
|
9
|
-
value_field :major
|
10
|
-
value_field :minor
|
11
|
-
value_field :platform
|
12
|
-
|
13
|
-
validates :build_date, field: { format: :string }, presence: false
|
14
|
-
validates :compiler, field: { format: :string }, presence: false
|
15
|
-
validates :git_commit, field: { format: :string }, presence: false
|
16
|
-
validates :git_tree_state, field: { format: :string }, presence: false
|
17
|
-
validates :git_version, field: { format: :string }, presence: false
|
18
|
-
validates :go_version, field: { format: :string }, presence: false
|
19
|
-
validates :major, field: { format: :string }, presence: false
|
20
|
-
validates :minor, field: { format: :string }, presence: false
|
21
|
-
validates :platform, field: { format: :string }, presence: false
|
3
|
+
value_fields :build_date, :compiler, :git_commit, :git_tree_state, :git_version, :go_version, :major, :minor, :platform
|
22
4
|
|
23
5
|
def serialize
|
24
6
|
{}.tap do |result|
|
data/lib/kube-dsl/validations.rb
CHANGED
@@ -27,9 +27,9 @@ module KubeDSL
|
|
27
27
|
@presence = presence
|
28
28
|
end
|
29
29
|
|
30
|
-
def validate(obj, errors)
|
30
|
+
def validate(obj, errors, nesting)
|
31
31
|
if presence && obj_empty?(obj)
|
32
|
-
errors.add(field_name, 'is required')
|
32
|
+
errors.add([*nesting, field_name].join('.'), 'is required')
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -52,17 +52,17 @@ module KubeDSL
|
|
52
52
|
@kind_of = opts.fetch(:kind_of)
|
53
53
|
end
|
54
54
|
|
55
|
-
def validate(obj, errors)
|
55
|
+
def validate(obj, errors, nesting)
|
56
56
|
unless obj.is_a?(Array)
|
57
|
-
errors.add(field_name, 'is not an array')
|
57
|
+
errors.add([*nesting, field_name].join('.'), 'is not an array')
|
58
58
|
return
|
59
59
|
end
|
60
60
|
|
61
61
|
obj.each_with_index do |elem, idx|
|
62
62
|
unless elem.nil? || elem.is_a?(kind_of)
|
63
63
|
errors.add(
|
64
|
-
field_name, "contains an object at index #{idx}
|
65
|
-
"expected '#{kind_of.name}'"
|
64
|
+
[*nesting, field_name].join('.'), "contains an object at index #{idx} "\
|
65
|
+
"of type '#{elem.class.name}', expected '#{kind_of.name}'"
|
66
66
|
)
|
67
67
|
end
|
68
68
|
end
|
@@ -102,9 +102,9 @@ module KubeDSL
|
|
102
102
|
@format_validator ||= FormatValidator.new(options[:format])
|
103
103
|
end
|
104
104
|
|
105
|
-
def validate(obj, errors)
|
105
|
+
def validate(obj, errors, nesting)
|
106
106
|
unless format_validator.valid?(obj)
|
107
|
-
errors.add(field_name, "is not a #{format_validator.klasses.map(&:to_s).join(', ')}")
|
107
|
+
errors.add([*nesting, field_name].join('.'), "is not a #{format_validator.klasses.map(&:to_s).join(', ')}")
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
@@ -117,9 +117,9 @@ module KubeDSL
|
|
117
117
|
@kind_of = opts.fetch(:kind_of)
|
118
118
|
end
|
119
119
|
|
120
|
-
def validate(obj, errors)
|
120
|
+
def validate(obj, errors, nesting)
|
121
121
|
unless obj.nil? || obj.is_a?(kind_of)
|
122
|
-
errors.add(field_name, "'#{obj.class.name}', expected '#{kind_of.name}'")
|
122
|
+
errors.add([*nesting, field_name].join('.'), "'#{obj.class.name}', expected '#{kind_of.name}'")
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
@@ -132,10 +132,12 @@ module KubeDSL
|
|
132
132
|
@format_validator ||= FormatValidator.new(options[:value_format])
|
133
133
|
end
|
134
134
|
|
135
|
-
def validate(obj, errors)
|
135
|
+
def validate(obj, errors, nesting)
|
136
136
|
obj.kv_pairs.each_pair do |k, v|
|
137
137
|
unless format_validator.valid?(v)
|
138
|
-
errors.add(
|
138
|
+
errors.add(
|
139
|
+
[*nesting, field_name].join('.'),
|
140
|
+
"expected element '#{k}' to be a #{format_validator.klasses.map(&:to_s).join(', ')}, got #{v.class.name}")
|
139
141
|
end
|
140
142
|
end
|
141
143
|
end
|
@@ -149,9 +151,9 @@ module KubeDSL
|
|
149
151
|
@list = opts[:in]
|
150
152
|
end
|
151
153
|
|
152
|
-
def validate(obj, errors)
|
154
|
+
def validate(obj, errors, nesting)
|
153
155
|
unless list.include?(obj)
|
154
|
-
errors.add(field_name, "is not in #{list.join(', ')}")
|
156
|
+
errors.add([*nesting, field_name].join('.'), "is not in #{list.join(', ')}")
|
155
157
|
end
|
156
158
|
end
|
157
159
|
end
|
@@ -186,12 +188,16 @@ module KubeDSL
|
|
186
188
|
end
|
187
189
|
|
188
190
|
module InstanceMethods
|
189
|
-
def validate
|
190
|
-
errors
|
191
|
+
def validate(errors = nil, nesting = [])
|
192
|
+
errors ||= ValidationErrors.new
|
191
193
|
|
192
194
|
self.class.validators.each do |field_name, validators|
|
193
195
|
field = send(field_name)
|
194
|
-
validators.each { |val| val.validate(field, errors) }
|
196
|
+
validators.each { |val| val.validate(field, errors, nesting) }
|
197
|
+
|
198
|
+
if field.respond_to?(:validate)
|
199
|
+
field.validate(errors, nesting + [field_name.to_s])
|
200
|
+
end
|
195
201
|
end
|
196
202
|
|
197
203
|
errors
|
@@ -15,7 +15,7 @@ module KubeDSL
|
|
15
15
|
define_method(field) do |*args|
|
16
16
|
if args.empty?
|
17
17
|
instance_variable_get(:"@#{field}") || (
|
18
|
-
default.respond_to?(:call) ? default.call : default
|
18
|
+
default.respond_to?(:call) ? default.call(self) : default
|
19
19
|
)
|
20
20
|
else
|
21
21
|
instance_variable_set(:"@#{field}", args.first)
|
data/lib/kube-dsl/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kube-dsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Dutro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-inflector
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- CHANGELOG.md
|
35
35
|
- Gemfile
|
36
36
|
- LICENSE
|
37
|
+
- README.md
|
37
38
|
- Rakefile
|
38
39
|
- kube-dsl.gemspec
|
39
40
|
- lib/kube-dsl.rb
|