minfra-cli 0.1.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 +7 -0
- data/.dockerignore +12 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +2 -0
- data/Dockerfile +12 -0
- data/bin/build +20 -0
- data/bin/console +16 -0
- data/bin/container_exec +9 -0
- data/bin/run_tests +74 -0
- data/bin/setup.sh +22 -0
- data/exe/minfra +6 -0
- data/lib/deep_merge.rb +149 -0
- data/lib/hash.rb +28 -0
- data/lib/minfra/cli/ask.rb +43 -0
- data/lib/minfra/cli/command.rb +35 -0
- data/lib/minfra/cli/commands/dev.rb +54 -0
- data/lib/minfra/cli/commands/kube.rb +279 -0
- data/lib/minfra/cli/commands/project/branch.rb +17 -0
- data/lib/minfra/cli/commands/project/tag.rb +40 -0
- data/lib/minfra/cli/commands/project.rb +113 -0
- data/lib/minfra/cli/commands/setup.rb +49 -0
- data/lib/minfra/cli/commands/stack/app_template.rb +65 -0
- data/lib/minfra/cli/commands/stack/client_template.rb +36 -0
- data/lib/minfra/cli/commands/stack/kube_stack_template.rb +94 -0
- data/lib/minfra/cli/commands/stack.rb +120 -0
- data/lib/minfra/cli/commands/tag.rb +86 -0
- data/lib/minfra/cli/common.rb +41 -0
- data/lib/minfra/cli/config.rb +111 -0
- data/lib/minfra/cli/document.rb +19 -0
- data/lib/minfra/cli/hook.rb +65 -0
- data/lib/minfra/cli/logging.rb +26 -0
- data/lib/minfra/cli/main_command.rb +32 -0
- data/lib/minfra/cli/plugins.rb +34 -0
- data/lib/minfra/cli/runner.rb +59 -0
- data/lib/minfra/cli/templater.rb +63 -0
- data/lib/minfra/cli/version.rb +5 -0
- data/lib/minfra/cli.rb +80 -0
- data/lib/orchparty/ast.rb +53 -0
- data/lib/orchparty/cli.rb +69 -0
- data/lib/orchparty/context.rb +22 -0
- data/lib/orchparty/dsl_parser.rb +229 -0
- data/lib/orchparty/dsl_parser_kubernetes.rb +361 -0
- data/lib/orchparty/kubernetes_application.rb +305 -0
- data/lib/orchparty/plugin.rb +24 -0
- data/lib/orchparty/plugins/env.rb +41 -0
- data/lib/orchparty/transformations/all.rb +18 -0
- data/lib/orchparty/transformations/mixin.rb +73 -0
- data/lib/orchparty/transformations/remove_internal.rb +16 -0
- data/lib/orchparty/transformations/sort.rb +10 -0
- data/lib/orchparty/transformations/variable.rb +56 -0
- data/lib/orchparty/transformations.rb +24 -0
- data/lib/orchparty/version.rb +3 -0
- data/lib/orchparty.rb +59 -0
- data/minfra-cli.gemspec +40 -0
- data/project.json +7 -0
- data/templates/kind.yaml.erb +33 -0
- data/templates/kube_config.yaml.erb +7 -0
- data/templates/minfra_config.json.erb +26 -0
- metadata +196 -0
@@ -0,0 +1,361 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'securerandom'
|
3
|
+
module Orchparty
|
4
|
+
module Kubernetes
|
5
|
+
class DSLParser
|
6
|
+
attr_reader :filename
|
7
|
+
|
8
|
+
def initialize(filename)
|
9
|
+
@filename = filename
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
file_content = File.read(filename)
|
14
|
+
builder = RootBuilder.new
|
15
|
+
builder.instance_eval(file_content, filename)
|
16
|
+
builder._build
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Builder
|
21
|
+
def self.build(*args, block)
|
22
|
+
builder = self.new(*args)
|
23
|
+
builder.instance_eval(&block) if block
|
24
|
+
builder._build
|
25
|
+
end
|
26
|
+
|
27
|
+
def assign_or_merge(node, key, value)
|
28
|
+
if node[key]
|
29
|
+
node[key] = node[key].deep_merge_concat(value)
|
30
|
+
else
|
31
|
+
node[key] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class RootBuilder < Builder
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
@root = AST.root
|
40
|
+
end
|
41
|
+
|
42
|
+
def import(rel_file)
|
43
|
+
old_file_path = Pathname.new(caller[0][/[^:]+/]).parent
|
44
|
+
rel_file_path = Pathname.new rel_file
|
45
|
+
new_file_path = old_file_path + rel_file_path
|
46
|
+
file_content = File.read(new_file_path)
|
47
|
+
instance_eval(file_content, new_file_path.expand_path.to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
def application(name, &block)
|
51
|
+
@root.applications[name] = ApplicationBuilder.build(name, block)
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def mixin(name, &block)
|
56
|
+
@root._mixins[name] = MixinBuilder.build(name, block)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def _build
|
61
|
+
@root
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class MixinBuilder < Builder
|
66
|
+
|
67
|
+
def initialize(name)
|
68
|
+
@mixin = AST.mixin(name: name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def template(path)
|
72
|
+
chart_name = "_mixin_temp_name"
|
73
|
+
unless @mixin.services[chart_name]
|
74
|
+
@mixin.services[chart_name] = AST.chart(name: chart_name, _type: "chart" )
|
75
|
+
@mixin._service_order << chart_name
|
76
|
+
end
|
77
|
+
chart = @mixin.services[chart_name]
|
78
|
+
chart.template = path
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def service(name, &block)
|
83
|
+
|
84
|
+
chart_name = "_mixin_temp_name"
|
85
|
+
unless @mixin.services[chart_name]
|
86
|
+
@mixin.services[chart_name] = AST.chart(name: chart_name, _type: "chart" )
|
87
|
+
@mixin._service_order << chart_name
|
88
|
+
end
|
89
|
+
chart = @mixin.services[chart_name]
|
90
|
+
|
91
|
+
result = ServiceBuilder.build(name, "chart-service", block)
|
92
|
+
|
93
|
+
name = "chart-#{chart.name}-#{name}"
|
94
|
+
@mixin.services[name] = result
|
95
|
+
@mixin._service_order << name
|
96
|
+
chart._services << name
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def helm(name, &block)
|
101
|
+
result = ServiceBuilder.build(name, "helm", block)
|
102
|
+
@mixin.services[name] = result
|
103
|
+
@mixin._mixins[name] = result
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def apply(name, &block)
|
109
|
+
result = ServiceBuilder.build(name, "apply", block)
|
110
|
+
@mixin.services[name] = result
|
111
|
+
@mixin._mixins[name] = result
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def mixin(name, &block)
|
116
|
+
@mixin._mixins[name] = ServiceMixinBuilder.build(name, block)
|
117
|
+
end
|
118
|
+
|
119
|
+
def volumes(&block)
|
120
|
+
@mixin.volumes = HashBuilder.build(block)
|
121
|
+
end
|
122
|
+
|
123
|
+
def networks(&block)
|
124
|
+
@mixin.networks = HashBuilder.build(block)
|
125
|
+
end
|
126
|
+
|
127
|
+
def _build
|
128
|
+
@mixin
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class ApplicationBuilder < Builder
|
133
|
+
|
134
|
+
def initialize(name)
|
135
|
+
@application = AST.application(name: name)
|
136
|
+
end
|
137
|
+
|
138
|
+
def mix(name)
|
139
|
+
@application._mix << name
|
140
|
+
end
|
141
|
+
|
142
|
+
def mixin(name, &block)
|
143
|
+
@application._mixins[name] = ApplicationMixinBuilder.build(block)
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
def all(&block)
|
148
|
+
@application.all = AllBuilder.build(block)
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
def variables(&block)
|
153
|
+
@application._variables = VariableBuilder.build(block)
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
157
|
+
def volumes(&block)
|
158
|
+
@application.volumes = HashBuilder.build(block)
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
def helm(name, &block)
|
163
|
+
result = ServiceBuilder.build(name, "helm", block)
|
164
|
+
@application.services[name] = result
|
165
|
+
@application._service_order << name
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
def label(&block)
|
170
|
+
name = SecureRandom.hex
|
171
|
+
result = ServiceWithoutNameBuilder.build("label", block)
|
172
|
+
@application.services[name] = result
|
173
|
+
@application._service_order << name
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
177
|
+
def apply(name, &block)
|
178
|
+
result = ServiceBuilder.build(name, "apply", block)
|
179
|
+
@application.services[name] = result
|
180
|
+
@application._service_order << name
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
184
|
+
def secret_generic(name, &block)
|
185
|
+
result = ServiceBuilder.build(name, "secret_generic", block)
|
186
|
+
@application.services[name] = result
|
187
|
+
@application._service_order << name
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
def wait(&block)
|
192
|
+
name = SecureRandom.hex
|
193
|
+
result = ServiceBuilder.build(name, "wait", block)
|
194
|
+
@application.services[name] = result
|
195
|
+
@application._service_order << name
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
199
|
+
def chart(name, &block)
|
200
|
+
@application.services[name] = ChartBuilder.build(name, @application, "chart", block)
|
201
|
+
@application._service_order << name
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
def template(path)
|
206
|
+
chart_name = @application.name
|
207
|
+
unless @application.services[chart_name]
|
208
|
+
@application.services[chart_name] = AST.chart(name: chart_name, _type: "chart" )
|
209
|
+
@application._service_order << chart_name
|
210
|
+
end
|
211
|
+
chart = @application.services[chart_name]
|
212
|
+
chart.template = path
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
216
|
+
def service(name, &block)
|
217
|
+
|
218
|
+
chart_name = @application.name
|
219
|
+
unless @application.services[chart_name]
|
220
|
+
@application.services[chart_name] = AST.chart(name: chart_name, _type: "chart" )
|
221
|
+
@application._service_order << chart_name
|
222
|
+
end
|
223
|
+
chart = @application.services[chart_name]
|
224
|
+
|
225
|
+
result = ServiceBuilder.build(name, "chart-service", block)
|
226
|
+
|
227
|
+
name = "chart-#{chart.name}-#{name}"
|
228
|
+
@application.services[name] = result
|
229
|
+
@application._service_order << name
|
230
|
+
chart._services << name
|
231
|
+
self
|
232
|
+
end
|
233
|
+
|
234
|
+
def _build
|
235
|
+
@application
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class HashBuilder < Builder
|
240
|
+
|
241
|
+
def method_missing(_, *values, &block)
|
242
|
+
if block_given?
|
243
|
+
value = HashBuilder.build(block)
|
244
|
+
if values.count == 1
|
245
|
+
@hash ||= AST.hash
|
246
|
+
@hash[values.first.to_sym] = value
|
247
|
+
else
|
248
|
+
@hash ||= AST.array
|
249
|
+
@hash << value
|
250
|
+
end
|
251
|
+
else
|
252
|
+
value = values.first
|
253
|
+
if value.is_a? Hash
|
254
|
+
@hash ||= AST.hash
|
255
|
+
key, value = value.first
|
256
|
+
begin
|
257
|
+
@hash[key.to_sym] = value
|
258
|
+
rescue
|
259
|
+
warn "Problem with key: #{key} #{value}"
|
260
|
+
raise
|
261
|
+
end
|
262
|
+
|
263
|
+
else
|
264
|
+
@hash ||= AST.array
|
265
|
+
@hash << value
|
266
|
+
end
|
267
|
+
end
|
268
|
+
self
|
269
|
+
end
|
270
|
+
|
271
|
+
def _build
|
272
|
+
@hash
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class VariableBuilder < HashBuilder
|
277
|
+
def _build
|
278
|
+
super || {}
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
class CommonBuilder < Builder
|
283
|
+
|
284
|
+
def initialize(node)
|
285
|
+
@node = node
|
286
|
+
end
|
287
|
+
|
288
|
+
def mix(name)
|
289
|
+
@node._mix << name
|
290
|
+
end
|
291
|
+
|
292
|
+
def method_missing(name, *values, &block)
|
293
|
+
if block_given?
|
294
|
+
assign_or_merge(@node, name, HashBuilder.build(block))
|
295
|
+
else
|
296
|
+
assign_or_merge(@node, name, values.first)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def _build
|
301
|
+
@node
|
302
|
+
end
|
303
|
+
|
304
|
+
def variables(&block)
|
305
|
+
@node._variables ||= {}
|
306
|
+
@node._variables = @node._variables.merge(VariableBuilder.build(block))
|
307
|
+
self
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class AllBuilder < CommonBuilder
|
312
|
+
def initialize
|
313
|
+
super AST.all
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
class ApplicationMixinBuilder < CommonBuilder
|
318
|
+
def initialize
|
319
|
+
super AST.application_mixin
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
class ServiceWithoutNameBuilder < CommonBuilder
|
324
|
+
|
325
|
+
def initialize( type)
|
326
|
+
super AST.service(_type: type)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class ServiceBuilder < CommonBuilder
|
331
|
+
|
332
|
+
def initialize(name, type)
|
333
|
+
super AST.service(name: name, _type: type)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
class ServiceMixinBuilder < CommonBuilder
|
338
|
+
|
339
|
+
def initialize(name)
|
340
|
+
super AST.service(name: name)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
class ChartBuilder < CommonBuilder
|
345
|
+
def initialize(name, application, type)
|
346
|
+
super AST.chart(name: name, _type: type )
|
347
|
+
@application = application
|
348
|
+
end
|
349
|
+
|
350
|
+
def service(name, &block)
|
351
|
+
result = ServiceBuilder.build(name, "chart-service", block)
|
352
|
+
|
353
|
+
name = "chart-#{@node.name}-#{name}"
|
354
|
+
@application.services[name] = result
|
355
|
+
@application._service_order << name
|
356
|
+
@node._services << name
|
357
|
+
self
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'erubis'
|
3
|
+
require 'open3'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'yaml'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/core_ext'
|
9
|
+
|
10
|
+
module Orchparty
|
11
|
+
module Services
|
12
|
+
class Context
|
13
|
+
attr_accessor :cluster_name
|
14
|
+
attr_accessor :namespace
|
15
|
+
attr_accessor :dir_path
|
16
|
+
attr_accessor :app_config
|
17
|
+
attr_accessor :options
|
18
|
+
|
19
|
+
def initialize(cluster_name: , namespace:, file_path: , app_config:, out_io: STDOUT)
|
20
|
+
self.cluster_name = cluster_name
|
21
|
+
self.namespace = namespace
|
22
|
+
self.dir_path = file_path
|
23
|
+
self.app_config = app_config
|
24
|
+
@out_io = out_io
|
25
|
+
self.options=options
|
26
|
+
end
|
27
|
+
|
28
|
+
def template(file_path, helm, flag: "-f ", fix_file_path: nil)
|
29
|
+
return "" unless file_path
|
30
|
+
puts "Rendering: #{file_path}"
|
31
|
+
file_path = File.join(self.dir_path, file_path)
|
32
|
+
if(file_path.end_with?(".erb"))
|
33
|
+
helm.application = OpenStruct.new(cluster_name: cluster_name, namespace: namespace)
|
34
|
+
template = Erubis::Eruby.new(File.read(file_path))
|
35
|
+
yaml = template.result(helm.get_binding)
|
36
|
+
file = Tempfile.new("kube-deploy.yaml")
|
37
|
+
file.write(yaml)
|
38
|
+
file.close
|
39
|
+
file_path = file.path
|
40
|
+
end
|
41
|
+
"#{flag}#{fix_file_path || file_path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_install(helm)
|
45
|
+
@out_io.puts "---"
|
46
|
+
@out_io.puts install_cmd(helm, value_path(helm))
|
47
|
+
@out_io.puts upgrade_cmd(helm, value_path(helm))
|
48
|
+
@out_io.puts "---"
|
49
|
+
@out_io.puts File.read(template(value_path(helm), helm, flag: "")) if value_path(helm)
|
50
|
+
end
|
51
|
+
|
52
|
+
# On 05.02.2021 we have decided that it would be best to print both commands.
|
53
|
+
# This way it would be possible to debug both upgrade and install and also people would not see git diffs all the time.
|
54
|
+
def print_upgrade(helm)
|
55
|
+
print_install(helm)
|
56
|
+
end
|
57
|
+
|
58
|
+
def upgrade(helm)
|
59
|
+
@out_io.puts system(upgrade_cmd(helm))
|
60
|
+
end
|
61
|
+
|
62
|
+
def install(helm)
|
63
|
+
@out_io.puts system(install_cmd(helm))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Helm < Context
|
68
|
+
def value_path(helm)
|
69
|
+
helm[:values]
|
70
|
+
end
|
71
|
+
|
72
|
+
def upgrade_cmd(helm, fix_file_path = nil)
|
73
|
+
"helm upgrade --namespace #{namespace} --kube-context #{cluster_name} --version #{helm.version} #{helm.name} #{helm.chart} #{template(value_path(helm), helm, fix_file_path: fix_file_path)}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def install_cmd(helm, fix_file_path = nil)
|
77
|
+
"helm install --create-namespace --namespace #{namespace} --kube-context #{cluster_name} --version #{helm.version} #{helm.name} #{helm.chart} #{template(value_path(helm), helm, fix_file_path: fix_file_path)}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Apply < Context
|
82
|
+
def value_path(apply)
|
83
|
+
apply[:name]
|
84
|
+
end
|
85
|
+
|
86
|
+
def upgrade_cmd(apply, fix_file_path = nil)
|
87
|
+
"kubectl apply --namespace #{namespace} --context #{cluster_name} #{template(value_path(apply), apply, fix_file_path: fix_file_path)}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def install_cmd(apply, fix_file_path = nil)
|
91
|
+
"kubectl apply --namespace #{namespace} --context #{cluster_name} #{template(value_path(apply), apply, fix_file_path: fix_file_path)}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class SecretGeneric < Context
|
96
|
+
def value_path(secret)
|
97
|
+
secret[:from_file]
|
98
|
+
end
|
99
|
+
|
100
|
+
def upgrade_cmd(secret, fix_file_path=nil)
|
101
|
+
"kubectl --namespace #{namespace} --context #{cluster_name} create secret generic --dry-run -o yaml #{secret[:name]} #{template(value_path(secret), secret, flag: "--from-file=", fix_file_path: fix_file_path)} | kubectl --context #{cluster_name} apply -f -"
|
102
|
+
end
|
103
|
+
|
104
|
+
def install_cmd(secret, fix_file_path=nil)
|
105
|
+
"kubectl --namespace #{namespace} --context #{cluster_name} create secret generic --dry-run -o yaml #{secret[:name]} #{template(value_path(secret), secret, flag: "--from-file=", fix_file_path: fix_file_path)} | kubectl --context #{cluster_name} apply -f -"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class Label < Context
|
110
|
+
def print_install(label)
|
111
|
+
@out_io.puts "---"
|
112
|
+
@out_io.puts install_cmd(label)
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_upgrade(label)
|
116
|
+
@out_io.puts "---"
|
117
|
+
@out_io.puts upgrade_cmd(label)
|
118
|
+
end
|
119
|
+
|
120
|
+
def upgrade(label)
|
121
|
+
@out_io.puts system(upgrade_cmd(label))
|
122
|
+
end
|
123
|
+
|
124
|
+
def install(label)
|
125
|
+
@out_io.puts system(install_cmd(label))
|
126
|
+
end
|
127
|
+
|
128
|
+
def upgrade_cmd(label)
|
129
|
+
"kubectl --namespace #{namespace} --context #{cluster_name} label --overwrite #{label[:resource]} #{label[:name]} #{label["value"]}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def install_cmd(label)
|
133
|
+
"kubectl --namespace #{namespace} --context #{cluster_name} label --overwrite #{label[:resource]} #{label[:name]} #{label["value"]}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Wait < Context
|
138
|
+
def print_install(wait)
|
139
|
+
@out_io.puts "---"
|
140
|
+
@out_io.puts wait.cmd
|
141
|
+
end
|
142
|
+
|
143
|
+
def print_upgrade(wait)
|
144
|
+
@out_io.puts "---"
|
145
|
+
@out_io.puts wait.cmd
|
146
|
+
end
|
147
|
+
|
148
|
+
def upgrade(wait)
|
149
|
+
eval(wait.cmd)
|
150
|
+
end
|
151
|
+
|
152
|
+
def install(wait)
|
153
|
+
eval(wait.cmd)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class Chart < Context
|
158
|
+
class CleanBinding
|
159
|
+
def get_binding(params)
|
160
|
+
params.instance_eval do
|
161
|
+
binding
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def build_chart(chart)
|
167
|
+
params = chart._services.map {|s| app_config.services[s.to_sym] }.map{|s| [s.name, s]}.to_h
|
168
|
+
Dir.mktmpdir do |dir|
|
169
|
+
run(templates_path: File.expand_path(chart.template, self.dir_path), params: params, output_chart_path: dir, chart: chart)
|
170
|
+
yield dir
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def run(templates_path:, params:, output_chart_path:, chart: )
|
175
|
+
system("mkdir -p #{output_chart_path}")
|
176
|
+
system("mkdir -p #{File.join(output_chart_path, 'templates')}")
|
177
|
+
|
178
|
+
system("cp #{File.join(templates_path, 'values.yaml')} #{File.join(output_chart_path, 'values.yaml')}")
|
179
|
+
system("cp #{File.join(templates_path, '.helmignore')} #{File.join(output_chart_path, '.helmignore')}")
|
180
|
+
system("cp #{File.join(templates_path, 'templates/_helpers.tpl')} #{File.join(output_chart_path, 'templates/_helpers.tpl')}")
|
181
|
+
|
182
|
+
generate_chart_yaml(
|
183
|
+
templates_path: templates_path,
|
184
|
+
output_chart_path: output_chart_path,
|
185
|
+
chart_name: chart.name,
|
186
|
+
)
|
187
|
+
|
188
|
+
params.each do |app_name, subparams|
|
189
|
+
subparams[:chart] = chart
|
190
|
+
generate_documents_from_erbs(
|
191
|
+
templates_path: templates_path,
|
192
|
+
app_name: app_name,
|
193
|
+
params: subparams,
|
194
|
+
output_chart_path: output_chart_path
|
195
|
+
)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def generate_documents_from_erbs(templates_path:, app_name:, params:, output_chart_path:)
|
200
|
+
if params[:kind].nil?
|
201
|
+
warn "ERROR: Could not generate service '#{app_name}'. Missing key: 'kind'."
|
202
|
+
exit 1
|
203
|
+
end
|
204
|
+
|
205
|
+
kind = params.fetch(:kind)
|
206
|
+
|
207
|
+
Dir[File.join(templates_path, kind, '*.erb')].each do |template_path|
|
208
|
+
template_name = File.basename(template_path, '.erb')
|
209
|
+
output_path = File.join(output_chart_path, 'templates', "#{app_name}-#{template_name}")
|
210
|
+
|
211
|
+
template = Erubis::Eruby.new(File.read(template_path))
|
212
|
+
params.app_name = app_name
|
213
|
+
params.templates_path = templates_path
|
214
|
+
begin
|
215
|
+
document = template.result(CleanBinding.new.get_binding(params))
|
216
|
+
rescue Exception
|
217
|
+
puts "#{template_path} has a problem: #{$!.inspect}"
|
218
|
+
raise
|
219
|
+
end
|
220
|
+
File.write(output_path, document)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def generate_chart_yaml(templates_path:, output_chart_path:, chart_name: )
|
225
|
+
template_path = File.join(templates_path, 'Chart.yaml.erb')
|
226
|
+
output_path = File.join(output_chart_path, 'Chart.yaml')
|
227
|
+
|
228
|
+
template = Erubis::Eruby.new(File.read(template_path))
|
229
|
+
params = Hashie::Mash.new(chart_name: chart_name)
|
230
|
+
document = template.result(CleanBinding.new.get_binding(params))
|
231
|
+
File.write(output_path, document)
|
232
|
+
end
|
233
|
+
|
234
|
+
def print_install(chart)
|
235
|
+
build_chart(chart) do |chart_path|
|
236
|
+
@out_io.puts `helm template --namespace #{namespace} --kube-context #{cluster_name} #{chart.name} #{chart_path}`
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def print_upgrade(chart)
|
241
|
+
print_install(chart)
|
242
|
+
end
|
243
|
+
|
244
|
+
def install(chart)
|
245
|
+
build_chart(chart) do |chart_path|
|
246
|
+
@out_io.puts system("helm install --create-namespace --namespace #{namespace} --kube-context #{cluster_name} #{chart.name} #{chart_path}")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def upgrade(chart)
|
251
|
+
build_chart(chart) do |chart_path|
|
252
|
+
@out_io.puts system("helm upgrade --namespace #{namespace} --kube-context #{cluster_name} #{chart.name} #{chart_path}")
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class KubernetesApplication
|
260
|
+
attr_accessor :cluster_name
|
261
|
+
attr_accessor :file_path
|
262
|
+
attr_accessor :namespace
|
263
|
+
attr_accessor :app_config
|
264
|
+
|
265
|
+
def initialize(app_config: [], namespace:, cluster_name:, file_name:, out_io: STDOUT)
|
266
|
+
self.file_path = Pathname.new(file_name).parent.expand_path
|
267
|
+
self.cluster_name = cluster_name
|
268
|
+
self.namespace = namespace
|
269
|
+
self.app_config = app_config
|
270
|
+
@out_io= out_io
|
271
|
+
end
|
272
|
+
|
273
|
+
def install
|
274
|
+
each_service(:install)
|
275
|
+
end
|
276
|
+
|
277
|
+
def upgrade
|
278
|
+
each_service(:upgrade)
|
279
|
+
end
|
280
|
+
|
281
|
+
def print(method)
|
282
|
+
each_service("print_#{method}".to_sym)
|
283
|
+
end
|
284
|
+
|
285
|
+
def combine_charts(app_config)
|
286
|
+
services = app_config._service_order.map(&:to_s)
|
287
|
+
app_config._service_order.each do |name|
|
288
|
+
current_service = app_config[:services][name]
|
289
|
+
if current_service._type == "chart"
|
290
|
+
current_service._services.each do |n|
|
291
|
+
services.delete n.to_s
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
services
|
296
|
+
end
|
297
|
+
|
298
|
+
def each_service(method)
|
299
|
+
services = combine_charts(app_config)
|
300
|
+
services.each do |name|
|
301
|
+
service = app_config[:services][name]
|
302
|
+
"::Orchparty::Services::#{service._type.classify}".constantize.new(cluster_name: cluster_name, namespace: namespace, file_path: file_path, app_config: app_config, out_io: @out_io).send(method, service)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Orchparty
|
2
|
+
module Plugin
|
3
|
+
@plugins = {}
|
4
|
+
|
5
|
+
def self.load_plugin(name)
|
6
|
+
begin
|
7
|
+
require "orchparty/plugins/#{name}"
|
8
|
+
raise "Plugin didn't correctly register itself" unless @plugins[name]
|
9
|
+
@plugins[name]
|
10
|
+
rescue LoadError
|
11
|
+
puts "could not load the plugin #{name}, you might install it as a gem or you need to write it by your self ;)"
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.plugins
|
17
|
+
@plugins
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.register_plugin(name, mod)
|
21
|
+
@plugins[name] = mod
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|