kontena-cli 0.13.4 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/kontena-cli.gemspec +2 -0
  4. data/lib/kontena/cli/app_command.rb +2 -0
  5. data/lib/kontena/cli/apps/common.rb +80 -74
  6. data/lib/kontena/cli/apps/config_command.rb +29 -0
  7. data/lib/kontena/cli/apps/deploy_command.rb +12 -81
  8. data/lib/kontena/cli/apps/docker_helper.rb +3 -3
  9. data/lib/kontena/cli/apps/init_command.rb +0 -3
  10. data/lib/kontena/cli/apps/list_command.rb +2 -3
  11. data/lib/kontena/cli/apps/logs_command.rb +2 -3
  12. data/lib/kontena/cli/apps/monitor_command.rb +3 -4
  13. data/lib/kontena/cli/apps/remove_command.rb +4 -4
  14. data/lib/kontena/cli/apps/restart_command.rb +2 -3
  15. data/lib/kontena/cli/apps/scale_command.rb +3 -5
  16. data/lib/kontena/cli/apps/service_generator.rb +123 -0
  17. data/lib/kontena/cli/apps/service_generator_v2.rb +26 -0
  18. data/lib/kontena/cli/apps/show_command.rb +1 -2
  19. data/lib/kontena/cli/apps/start_command.rb +2 -3
  20. data/lib/kontena/cli/apps/stop_command.rb +2 -3
  21. data/lib/kontena/cli/apps/yaml/reader.rb +150 -0
  22. data/lib/kontena/cli/apps/yaml/service_extender.rb +60 -0
  23. data/lib/kontena/cli/apps/yaml/validations.rb +79 -0
  24. data/lib/kontena/cli/apps/yaml/validator.rb +55 -0
  25. data/lib/kontena/cli/apps/yaml/validator_v2.rb +74 -0
  26. data/lib/kontena/cli/common.rb +23 -0
  27. data/lib/kontena/cli/etcd/remove_command.rb +2 -0
  28. data/lib/kontena/cli/grids/remove_command.rb +2 -0
  29. data/lib/kontena/cli/grids/users/remove_command.rb +3 -0
  30. data/lib/kontena/cli/master/azure/create_command.rb +0 -2
  31. data/lib/kontena/cli/master/packet/create_command.rb +42 -0
  32. data/lib/kontena/cli/master/packet_command.rb +14 -0
  33. data/lib/kontena/cli/master/upcloud/create_command.rb +39 -0
  34. data/lib/kontena/cli/master/upcloud_command.rb +13 -0
  35. data/lib/kontena/cli/master/users/remove_command.rb +3 -0
  36. data/lib/kontena/cli/master/users/roles/remove_command.rb +2 -0
  37. data/lib/kontena/cli/master_command.rb +4 -0
  38. data/lib/kontena/cli/node_command.rb +4 -0
  39. data/lib/kontena/cli/nodes/azure/create_command.rb +0 -2
  40. data/lib/kontena/cli/nodes/list_command.rb +4 -8
  41. data/lib/kontena/cli/nodes/packet/create_command.rb +35 -0
  42. data/lib/kontena/cli/nodes/packet/restart_command.rb +17 -0
  43. data/lib/kontena/cli/nodes/packet/terminate_command.rb +20 -0
  44. data/lib/kontena/cli/nodes/packet_command.rb +15 -0
  45. data/lib/kontena/cli/nodes/remove_command.rb +2 -0
  46. data/lib/kontena/cli/nodes/show_command.rb +3 -1
  47. data/lib/kontena/cli/nodes/upcloud/create_command.rb +33 -0
  48. data/lib/kontena/cli/nodes/upcloud/restart_command.rb +20 -0
  49. data/lib/kontena/cli/nodes/upcloud/terminate_command.rb +20 -0
  50. data/lib/kontena/cli/nodes/upcloud_command.rb +15 -0
  51. data/lib/kontena/cli/registry/remove_command.rb +3 -0
  52. data/lib/kontena/cli/services/remove_command.rb +2 -0
  53. data/lib/kontena/cli/services/services_helper.rb +1 -0
  54. data/lib/kontena/cli/vault/list_command.rb +2 -0
  55. data/lib/kontena/cli/vault/read_command.rb +2 -0
  56. data/lib/kontena/cli/vault/remove_command.rb +4 -0
  57. data/lib/kontena/cli/vault/update_command.rb +8 -1
  58. data/lib/kontena/cli/vault/write_command.rb +2 -0
  59. data/lib/kontena/cli/vpn/remove_command.rb +3 -0
  60. data/lib/kontena/machine/azure/master_provisioner.rb +2 -2
  61. data/lib/kontena/machine/azure/node_provisioner.rb +7 -4
  62. data/lib/kontena/machine/digital_ocean/node_provisioner.rb +1 -1
  63. data/lib/kontena/machine/packet.rb +17 -0
  64. data/lib/kontena/machine/packet/cloudinit.yml +66 -0
  65. data/lib/kontena/machine/packet/cloudinit_master.yml +118 -0
  66. data/lib/kontena/machine/packet/master_provisioner.rb +93 -0
  67. data/lib/kontena/machine/packet/node_destroyer.rb +42 -0
  68. data/lib/kontena/machine/packet/node_provisioner.rb +77 -0
  69. data/lib/kontena/machine/packet/node_restarter.rb +41 -0
  70. data/lib/kontena/machine/packet/packet_common.rb +89 -0
  71. data/lib/kontena/machine/upcloud.rb +9 -0
  72. data/lib/kontena/machine/upcloud/cloudinit.yml +64 -0
  73. data/lib/kontena/machine/upcloud/cloudinit_master.yml +118 -0
  74. data/lib/kontena/machine/upcloud/master_provisioner.rb +136 -0
  75. data/lib/kontena/machine/upcloud/node_destroyer.rb +82 -0
  76. data/lib/kontena/machine/upcloud/node_provisioner.rb +119 -0
  77. data/lib/kontena/machine/upcloud/node_restarter.rb +47 -0
  78. data/lib/kontena/machine/upcloud/upcloud_common.rb +70 -0
  79. data/lib/kontena/scripts/completer +8 -3
  80. data/spec/fixtures/docker-compose_v2.yml +10 -0
  81. data/spec/fixtures/kontena-invalid.yml +4 -0
  82. data/spec/fixtures/kontena-with-variables.yml +19 -0
  83. data/spec/fixtures/kontena.yml +2 -2
  84. data/spec/fixtures/kontena_v2.yml +35 -0
  85. data/spec/kontena/cli/app/common_spec.rb +39 -101
  86. data/spec/kontena/cli/app/deploy_command_spec.rb +37 -388
  87. data/spec/kontena/cli/app/docker_helper_spec.rb +4 -4
  88. data/spec/kontena/cli/app/service_generator_spec.rb +374 -0
  89. data/spec/kontena/cli/app/service_generator_v2_spec.rb +74 -0
  90. data/spec/kontena/cli/app/yaml/reader_spec.rb +249 -0
  91. data/spec/kontena/cli/app/yaml/service_extender_spec.rb +104 -0
  92. data/spec/kontena/cli/app/yaml/validator_spec.rb +263 -0
  93. data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +309 -0
  94. data/spec/kontena/cli/common_spec.rb +39 -1
  95. data/spec/kontena/cli/master/users/remove_command_spec.rb +9 -0
  96. data/spec/kontena/cli/master/users/roles/remove_command_spec.rb +2 -0
  97. metadata +86 -2
@@ -10,7 +10,7 @@ describe Kontena::Cli::Apps::DockerHelper do
10
10
  let(:services_with_valid_hooks) do
11
11
  {
12
12
  'test_service' => {
13
- 'build' => '.',
13
+ 'build' => { 'context' => '.' },
14
14
  'image' => 'test_service',
15
15
  'hooks' => {
16
16
  'pre_build' => [
@@ -25,7 +25,7 @@ describe Kontena::Cli::Apps::DockerHelper do
25
25
  let(:services_with_invalid_hook) do
26
26
  {
27
27
  'test_service' => {
28
- 'build' => '.',
28
+ 'build' => { 'context' => '.' },
29
29
  'image' => 'test_service',
30
30
  'hooks' => {
31
31
  'pre_build' => [
@@ -40,7 +40,7 @@ describe Kontena::Cli::Apps::DockerHelper do
40
40
  let(:services_with_no_hook) do
41
41
  {
42
42
  'test_service' => {
43
- 'build' => '.',
43
+ 'build' => { 'context' => '.' },
44
44
  'image' => 'test_service',
45
45
  }
46
46
  }
@@ -70,7 +70,7 @@ describe Kontena::Cli::Apps::DockerHelper do
70
70
  end
71
71
 
72
72
  describe '#run_pre_build_hook' do
73
-
73
+
74
74
  context 'when hook defined' do
75
75
  it 'runs the hook' do
76
76
  allow(subject).to receive(:build_docker_image)
@@ -0,0 +1,374 @@
1
+ require_relative "../../../spec_helper"
2
+ require "kontena/cli/apps/service_generator"
3
+
4
+ describe Kontena::Cli::Apps::ServiceGenerator do
5
+ let(:subject) do
6
+ described_class.new({})
7
+ end
8
+
9
+ describe '#parse_data' do
10
+ context 'volumes' do
11
+ it 'returns volumes if set' do
12
+ data = {
13
+ 'image' => 'foo/bar:latest',
14
+ 'volumes' => [
15
+ 'mongodb-1'
16
+ ]
17
+ }
18
+ result = subject.send(:parse_data, data)
19
+ expect(result['volumes']).to eq(data['volumes'])
20
+ end
21
+
22
+ it 'returns empty volumes if not set' do
23
+ data = {
24
+ 'image' => 'foo/bar:latest'
25
+ }
26
+ result = subject.send(:parse_data, data)
27
+ expect(result['volumes']).to eq([])
28
+ end
29
+ end
30
+
31
+ context 'volumes_from' do
32
+ it 'returns volumes_from if set' do
33
+ data = {
34
+ 'image' => 'foo/bar:latest',
35
+ 'volumes_from' => [
36
+ 'mongodb-1'
37
+ ]
38
+ }
39
+ result = subject.send(:parse_data, data)
40
+ expect(result['volumes_from']).to eq(data['volumes_from'])
41
+ end
42
+
43
+ it 'returns empty volumes_from if not set' do
44
+ data = {
45
+ 'image' => 'foo/bar:latest'
46
+ }
47
+ result = subject.send(:parse_data, data)
48
+ expect(result['volumes_from']).to eq([])
49
+ end
50
+ end
51
+
52
+ context 'command' do
53
+ it 'returns cmd array if set' do
54
+ data = {
55
+ 'image' => 'foo/bar:latest',
56
+ 'command' => 'ls -la'
57
+ }
58
+ result = subject.send(:parse_data, data)
59
+ expect(result['cmd']).to eq(data['command'].split(' '))
60
+ end
61
+
62
+ it 'does not return cmd if not set' do
63
+ data = {
64
+ 'image' => 'foo/bar:latest'
65
+ }
66
+ result = subject.send(:parse_data, data)
67
+ expect(result.has_key?('cmd')).to be_falsey
68
+ end
69
+ end
70
+
71
+ context 'affinity' do
72
+ it 'returns affinity if set' do
73
+ data = {
74
+ 'image' => 'foo/bar:latest',
75
+ 'affinity' => [
76
+ 'label==az=b'
77
+ ]
78
+ }
79
+ result = subject.send(:parse_data, data)
80
+ expect(result['affinity']).to eq(data['affinity'])
81
+ end
82
+
83
+ it 'returns affinity as empty array if not set' do
84
+ data = {
85
+ 'image' => 'foo/bar:latest'
86
+ }
87
+ result = subject.send(:parse_data, data)
88
+ expect(result.has_key?('affinity')).to be_truthy
89
+ expect(result['affinity']).to eq([])
90
+ end
91
+ end
92
+
93
+ context 'user' do
94
+ it 'returns user if set' do
95
+ data = {
96
+ 'image' => 'foo/bar:latest',
97
+ 'user' => 'user'
98
+ }
99
+ result = subject.send(:parse_data, data)
100
+ expect(result['user']).to eq('user')
101
+ end
102
+
103
+ it 'does not return user if not set' do
104
+ data = {
105
+ 'image' => 'foo/bar:latest'
106
+ }
107
+ result = subject.send(:parse_data, data)
108
+ expect(result.has_key?('user')).to be_falsey
109
+ end
110
+ end
111
+
112
+ context 'stateful' do
113
+ it 'returns stateful if set' do
114
+ data = {
115
+ 'image' => 'foo/bar:latest',
116
+ 'stateful' => true
117
+ }
118
+ result = subject.send(:parse_data, data)
119
+ expect(result['stateful']).to eq(true)
120
+ end
121
+
122
+ it 'returns stateful as false if not set' do
123
+ data = {
124
+ 'image' => 'foo/bar:latest'
125
+ }
126
+ result = subject.send(:parse_data, data)
127
+ expect(result['stateful']).to eq(false)
128
+ end
129
+ end
130
+
131
+ context 'links' do
132
+ it 'returns empty array if links not set' do
133
+ data = {
134
+ 'image' => 'wordpress:latest'
135
+ }
136
+ result = subject.send(:parse_data, data)
137
+ expect(result['links']).to eq([])
138
+ end
139
+
140
+ it 'returns parsed links array' do
141
+ data = {
142
+ 'image' => 'wordpress:latest',
143
+ 'links' => ['mysql:db']
144
+ }
145
+ result = subject.send(:parse_data, data)
146
+ expect(result['links']).to eq([{
147
+ 'name' => 'mysql',
148
+ 'alias' => 'db'
149
+ }])
150
+ end
151
+ end
152
+ context 'privileged' do
153
+ it 'returns privileged if set' do
154
+ data = {
155
+ 'image' => 'foo/bar:latest',
156
+ 'privileged' => false
157
+ }
158
+ result = subject.send(:parse_data, data)
159
+ expect(result['privileged']).to eq(false)
160
+ end
161
+
162
+ it 'does not return privileged if not set' do
163
+ data = {
164
+ 'image' => 'foo/bar:latest'
165
+ }
166
+ result = subject.send(:parse_data, data)
167
+ expect(result['privileged']).to be_nil
168
+ end
169
+ end
170
+
171
+ context 'cap_add' do
172
+ it 'returns cap_drop if set' do
173
+ data = {
174
+ 'image' => 'foo/bar:latest',
175
+ 'cap_add' => [
176
+ 'NET_ADMIN'
177
+ ]
178
+ }
179
+ result = subject.send(:parse_data, data)
180
+ expect(result['cap_add']).to eq(data['cap_add'])
181
+ end
182
+
183
+ it 'does not return cap_add if not set' do
184
+ data = {
185
+ 'image' => 'foo/bar:latest'
186
+ }
187
+ result = subject.send(:parse_data, data)
188
+ expect(result['cap_add']).to be_nil
189
+ end
190
+ end
191
+
192
+ context 'cap_drop' do
193
+ it 'returns cap_drop if set' do
194
+ data = {
195
+ 'image' => 'foo/bar:latest',
196
+ 'cap_drop' => [
197
+ 'NET_ADMIN'
198
+ ]
199
+ }
200
+ result = subject.send(:parse_data, data)
201
+ expect(result['cap_drop']).to eq(data['cap_drop'])
202
+ end
203
+
204
+ it 'does not return cap_drop if not set' do
205
+ data = {
206
+ 'image' => 'foo/bar:latest'
207
+ }
208
+ result = subject.send(:parse_data, data)
209
+ expect(result['cap_drop']).to be_nil
210
+ end
211
+ end
212
+
213
+ context 'net' do
214
+ it 'returns net if set' do
215
+ data = {
216
+ 'image' => 'foo/bar:latest',
217
+ 'net' => 'host'
218
+ }
219
+ result = subject.send(:parse_data, data)
220
+ expect(result['net']).to eq('host')
221
+ end
222
+
223
+ it 'does not return pid if not set' do
224
+ data = {
225
+ 'image' => 'foo/bar:latest'
226
+ }
227
+ result = subject.send(:parse_data, data)
228
+ expect(result['net']).to be_nil
229
+ end
230
+ end
231
+
232
+ context 'pid' do
233
+ it 'returns pid if set' do
234
+ data = {
235
+ 'image' => 'foo/bar:latest',
236
+ 'pid' => 'host'
237
+ }
238
+ result = subject.send(:parse_data, data)
239
+ expect(result['pid']).to eq('host')
240
+ end
241
+
242
+ it 'does not return pid if not set' do
243
+ data = {
244
+ 'image' => 'foo/bar:latest'
245
+ }
246
+ result = subject.send(:parse_data, data)
247
+ expect(result['pid']).to be_nil
248
+ end
249
+ end
250
+
251
+ context 'log_driver' do
252
+ it 'returns log_driver if set' do
253
+ data = {
254
+ 'image' => 'foo/bar:latest',
255
+ 'log_driver' => 'syslog'
256
+ }
257
+ result = subject.send(:parse_data, data)
258
+ expect(result['log_driver']).to eq('syslog')
259
+ end
260
+
261
+ it 'does not return log_driver if not set' do
262
+ data = {
263
+ 'image' => 'foo/bar:latest'
264
+ }
265
+ result = subject.send(:parse_data, data)
266
+ expect(result['log_driver']).to be_nil
267
+ end
268
+ end
269
+
270
+ context 'log_opt' do
271
+ it 'returns log_opts hash if log_opt is set' do
272
+ data = {
273
+ 'image' => 'foo/bar:latest',
274
+ 'log_driver' => 'fluentd',
275
+ 'log_opt' => {
276
+ 'fluentd-address' => '192.168.99.1:24224',
277
+ 'fluentd-tag' => 'docker.{{.Name}}'
278
+ }
279
+ }
280
+ result = subject.send(:parse_data, data)
281
+ expect(result['log_opts']).to eq(data['log_opt'])
282
+ end
283
+
284
+ it 'does not return log_opts if log_opt is not set' do
285
+ data = {
286
+ 'image' => 'foo/bar:latest'
287
+ }
288
+ result = subject.send(:parse_data, data)
289
+ expect(result['log_opts']).to be_nil
290
+ end
291
+ end
292
+
293
+ context 'deploy_opts' do
294
+ it 'returns deploy_opts if deploy.wait_for_port is defined' do
295
+ data = {
296
+ 'image' => 'foo/bar:latest',
297
+ 'deploy' => {
298
+ 'wait_for_port' => '8080'
299
+ }
300
+ }
301
+ result = subject.send(:parse_data, data)
302
+ expect(result['deploy_opts']['wait_for_port']).to eq('8080')
303
+ end
304
+
305
+ it 'returns deploy_opts if deploy.min_health is defined' do
306
+ data = {
307
+ 'image' => 'foo/bar:latest',
308
+ 'deploy' => {
309
+ 'min_health' => '0.5'
310
+ }
311
+ }
312
+ result = subject.send(:parse_data, data)
313
+ expect(result['deploy_opts']['min_health']).to eq('0.5')
314
+ end
315
+
316
+ it 'sets strategy if deploy.strategy is defined' do
317
+ data = {
318
+ 'image' => 'foo/bar:latest',
319
+ 'deploy' => {
320
+ 'strategy' => 'daemon'
321
+ }
322
+ }
323
+ result = subject.send(:parse_data, data)
324
+ expect(result['strategy']).to eq('daemon')
325
+ end
326
+
327
+ it 'does not return deploy_opts if no deploy options are defined' do
328
+ data = {
329
+ 'image' => 'foo/bar:latest'
330
+ }
331
+ result = subject.send(:parse_data, data)
332
+ expect(result['deploy_opts']).to be_nil
333
+ end
334
+ end
335
+
336
+ context 'hooks' do
337
+ it 'returns hooks hash if defined' do
338
+ data = {
339
+ 'image' => 'foo/bar:latest',
340
+ 'hooks' => {
341
+ 'post_start' => []
342
+ }
343
+ }
344
+ result = subject.send(:parse_data, data)
345
+ expect(result['hooks']).to eq(data['hooks'])
346
+ end
347
+
348
+ it 'does returns empty hook hash if not defined' do
349
+ data = {'image' => 'foo/bar:latest'}
350
+ result = subject.send(:parse_data, data)
351
+ expect(result['hooks']).to eq({})
352
+ end
353
+ end
354
+
355
+ context 'secrets' do
356
+ it 'returns secrets array if defined' do
357
+ data = {
358
+ 'image' => 'foo/bar:latest',
359
+ 'secrets' => [
360
+ {'secret' => 'MYSQL_ADMIN_PASSWORD', 'name' => 'WORDPRESS_DB_PASSWORD', 'type' => 'env'}
361
+ ]
362
+ }
363
+ result = subject.send(:parse_data, data)
364
+ expect(result['secrets']).to eq(data['secrets'])
365
+ end
366
+
367
+ it 'does not return secrets if not defined' do
368
+ data = {'image' => 'foo/bar:latest'}
369
+ result = subject.send(:parse_data, data)
370
+ expect(result['secrets']).to be_nil
371
+ end
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,74 @@
1
+ require_relative "../../../spec_helper"
2
+ require "kontena/cli/apps/service_generator_v2"
3
+ require 'ruby_dig'
4
+
5
+ describe Kontena::Cli::Apps::ServiceGeneratorV2 do
6
+ let(:subject) do
7
+ described_class.new({})
8
+ end
9
+
10
+ describe '#parse_data' do
11
+ it 'parses network_mode' do
12
+ data = {
13
+ 'image' => 'wordpress:latest',
14
+ 'network_mode' => 'bridge'
15
+ }
16
+ result = subject.send(:parse_data, data)
17
+ expect(result['net']).to eq('bridge')
18
+ end
19
+
20
+ it 'parses logging' do
21
+ data = {
22
+ 'image' => 'wordpress:latest',
23
+ 'logging' => {
24
+ 'driver' => 'influxdb',
25
+ 'options' => {
26
+ 'syslog-address' => 'tcp://192.168.0.42:123'
27
+ }
28
+ }
29
+ }
30
+ result = subject.send(:parse_data, data)
31
+ expect(result['log_driver']).to eq('influxdb')
32
+ expect(result['log_opts']).to eq({
33
+ 'syslog-address' => 'tcp://192.168.0.42:123'
34
+ })
35
+ end
36
+
37
+ it 'adds depends_on to links' do
38
+ data = {
39
+ 'image' => 'wordpress:latest',
40
+ 'depends_on' => ['mysql']
41
+ }
42
+ result = subject.send(:parse_data, data)
43
+ expect(result['links']).to eq([{
44
+ 'name' => 'mysql',
45
+ 'alias' => 'mysql'
46
+ }])
47
+ end
48
+ end
49
+
50
+ describe '#parse_build_options' do
51
+ context 'when build option is a string' do
52
+ it 'converts build option to hash' do
53
+ data = {
54
+ 'build' => '.',
55
+ 'image' => 'myapp'
56
+ }
57
+ result = subject.send(:parse_build_options, data)
58
+ expect(result).to eq({ 'context' => '.' })
59
+ end
60
+ end
61
+ context 'when build options is a hash' do
62
+ it 'uses it as build options' do
63
+ data = {
64
+ 'build' => {
65
+ 'context' => '.',
66
+ 'dockerfile' => 'alternate-dockerfile'
67
+ }
68
+ }
69
+ result = subject.send(:parse_build_options, data)
70
+ expect(result).to eq(data['build'])
71
+ end
72
+ end
73
+ end
74
+ end