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
@@ -0,0 +1,249 @@
1
+ require_relative '../../../../spec_helper'
2
+ require 'kontena/cli/apps/yaml/reader'
3
+
4
+ describe Kontena::Cli::Apps::YAML::Reader do
5
+ include FixturesHelpers
6
+
7
+ def absolute_yaml_path(file = 'kontena.yml')
8
+ "#{Dir.pwd}/#{file}"
9
+ end
10
+
11
+ let(:subject) do
12
+ described_class.new('kontena.yml')
13
+ end
14
+
15
+ let(:service_extender) do
16
+ spy
17
+ end
18
+
19
+ let(:valid_result) do
20
+ {
21
+ 'wordpress' => {
22
+ 'extends' => {
23
+ 'file' => 'docker-compose.yml',
24
+ 'service' => 'wordpress'
25
+ },
26
+ 'image' => 'wordpress:4.1',
27
+ 'ports' => ['80:80'],
28
+ 'links' => ['mysql:mysql'],
29
+ 'stateful' => true,
30
+ 'environment' => ['WORDPRESS_DB_PASSWORD=test_secret'],
31
+ 'instances' => 2,
32
+ 'deploy' => { 'strategy' => 'ha' },
33
+ 'secrets' => []
34
+ },
35
+ 'mysql' => {
36
+ 'extends' => {
37
+ 'file' => 'docker-compose.yml',
38
+ 'service' => 'mysql'
39
+ },
40
+ 'image' => 'mysql:5.6',
41
+ 'stateful' => true,
42
+ 'environment' => ['MYSQL_ROOT_PASSWORD=test_secret'],
43
+ 'secrets' => []
44
+ }
45
+ }
46
+ end
47
+
48
+ let(:valid_v2_result) do
49
+ {
50
+ 'version' => '2',
51
+ 'services' => {
52
+ 'wordpress' => {
53
+ 'image' => 'wordpress:4.1',
54
+ 'ports' => ['80:80'],
55
+ 'depends_on' => ['mysql'],
56
+ 'stateful' => true,
57
+ 'environment' => ['WORDPRESS_DB_PASSWORD=test_secret'],
58
+ 'instances' => 2,
59
+ 'deploy' => { 'strategy' => 'ha' },
60
+ 'networks' => ['front-tier', 'back-tier'],
61
+ 'secrets' => []
62
+ },
63
+ 'mysql' => {
64
+ 'image' => 'mysql:5.6',
65
+ 'stateful' => true,
66
+ 'environment' => ['MYSQL_ROOT_PASSWORD=test_secret'],
67
+ 'volumes' => ['mysql-data:/var/lib/mysql'],
68
+ 'networks' => ['back-tier'],
69
+ 'secrets' => []
70
+ }
71
+ },
72
+ 'volumes' => {
73
+ 'mysql-data' => { 'driver' => 'local' }
74
+ },
75
+ 'networks' => {
76
+ 'front-tier' => { 'driver' => 'bridge' },
77
+ 'back-tier' => { 'driver' => 'bridge' }
78
+ }
79
+ }
80
+ end
81
+
82
+ describe '#initialize' do
83
+ before(:each) do
84
+ allow(File).to receive(:read)
85
+ .with(absolute_yaml_path('kontena.yml'))
86
+ .and_return(fixture('kontena.yml'))
87
+ allow(File).to receive(:read)
88
+ .with(absolute_yaml_path('docker-compose.yml'))
89
+ .and_return(fixture('docker-compose.yml'))
90
+ end
91
+
92
+ it 'reads given file' do
93
+ expect(File).to receive(:read)
94
+ .with(absolute_yaml_path)
95
+ .and_return(fixture('kontena.yml'))
96
+ subject
97
+ end
98
+
99
+ context 'variable interpolation' do
100
+ before(:each) do
101
+ allow(ENV).to receive(:key?).and_return(true)
102
+ allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
103
+ allow(ENV).to receive(:[]).with('project').and_return('test')
104
+ allow(ENV).to receive(:[]).with('grid').and_return('test-grid')
105
+ allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
106
+ end
107
+
108
+ it 'interpolates $VAR variables' do
109
+ allow(File).to receive(:read)
110
+ .with(absolute_yaml_path)
111
+ .and_return(fixture('kontena-with-variables.yml'))
112
+ services = subject.yaml
113
+ expect(services['wordpress']['image']).to eq('wordpress:4.1')
114
+ end
115
+
116
+ it 'interpolates ${VAR} variables' do
117
+ allow(File).to receive(:read)
118
+ .with(absolute_yaml_path)
119
+ .and_return(fixture('kontena-with-variables.yml'))
120
+ services = subject.yaml
121
+ expect(services['mysql']['image']).to eq('mariadb:latest')
122
+ end
123
+
124
+ it 'warns about empty variables' do
125
+ allow(File).to receive(:read)
126
+ .with(absolute_yaml_path)
127
+ .and_return(fixture('kontena-with-variables.yml'))
128
+ allow(ENV).to receive(:key?)
129
+ .with('MYSQL_IMAGE')
130
+ .and_return(false)
131
+
132
+ expect {
133
+ subject
134
+ }.to output('The MYSQL_IMAGE is not set. Substituting an empty string.
135
+ ').to_stdout
136
+ end
137
+ end
138
+
139
+ it 'replaces $$VAR variables to $VAR format' do
140
+ allow(ENV).to receive(:key?).and_return(true)
141
+ allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
142
+ allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
143
+ allow(ENV).to receive(:[]).with('project').and_return('test')
144
+ allow(ENV).to receive(:[]).with('grid').and_return('test-grid')
145
+ allow(File).to receive(:read)
146
+ .with(absolute_yaml_path)
147
+ .and_return(fixture('kontena-with-variables.yml'))
148
+ services = subject.execute[:services]
149
+ expect(services['mysql']['environment'].first).to eq('INTERNAL_VAR=$INTERNAL_VAR')
150
+ end
151
+ end
152
+
153
+ describe '#v2?' do
154
+ context 'version 1' do
155
+ it 'returns false' do
156
+ allow(File).to receive(:read)
157
+ .with(absolute_yaml_path('docker-compose.yml'))
158
+ .and_return(fixture('docker-compose.yml'))
159
+ allow(File).to receive(:read)
160
+ .with(absolute_yaml_path('kontena.yml'))
161
+ .and_return(fixture('kontena.yml'))
162
+ expect(subject.v2?).to be_falsey
163
+ end
164
+ end
165
+ context 'version 2' do
166
+ it 'returns true' do
167
+ allow(File).to receive(:read)
168
+ .with(absolute_yaml_path('docker-compose_v2.yml'))
169
+ .and_return(fixture('docker-compose_v2.yml'))
170
+ allow(File).to receive(:read)
171
+ .with(absolute_yaml_path('kontena.yml'))
172
+ .and_return(fixture('kontena_v2.yml'))
173
+ expect(subject.v2?).to be_truthy
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '#execute' do
179
+ before(:each) do
180
+ allow(ENV).to receive(:[]).with('project').and_return('test')
181
+ allow(ENV).to receive(:[]).with('grid').and_return('test-grid')
182
+
183
+ allow(File).to receive(:read)
184
+ .with(absolute_yaml_path('kontena.yml'))
185
+ .and_return(fixture('kontena.yml'))
186
+ allow(File).to receive(:read)
187
+ .with(absolute_yaml_path('docker-compose.yml'))
188
+ .and_return(fixture('docker-compose.yml'))
189
+ end
190
+
191
+ it 'extends services' do
192
+ docker_compose_yml = YAML.load(fixture('docker-compose.yml') % { project: 'test' })
193
+ wordpress_options = {
194
+ 'extends' => {
195
+ 'file' => 'docker-compose.yml',
196
+ 'service' => 'wordpress'
197
+ },
198
+ 'stateful' => true,
199
+ 'environment' => ['WORDPRESS_DB_PASSWORD=test_secret'],
200
+ 'instances' => 2,
201
+ 'deploy' => { 'strategy' => 'ha' }
202
+ }
203
+ mysql_options = {
204
+ 'extends' => {
205
+ 'file' => 'docker-compose.yml',
206
+ 'service' => 'mysql'
207
+ },
208
+ 'stateful' => true,
209
+ 'environment' => ['MYSQL_ROOT_PASSWORD=test_secret']
210
+ }
211
+ expect(Kontena::Cli::Apps::YAML::ServiceExtender).to receive(:new)
212
+ .with(wordpress_options)
213
+ .once
214
+ .and_return(service_extender)
215
+ expect(Kontena::Cli::Apps::YAML::ServiceExtender).to receive(:new)
216
+ .with(mysql_options)
217
+ .once
218
+ .and_return(service_extender)
219
+ expect(service_extender).to receive(:extend).with(docker_compose_yml['wordpress'])
220
+ expect(service_extender).to receive(:extend).with(docker_compose_yml['mysql'])
221
+
222
+ subject.execute
223
+ end
224
+
225
+ context 'environment variables' do
226
+ it 'converts env hash to array' do
227
+ result = subject.execute[:services]
228
+ expect(result['wordpress']['environment']).to eq(['WORDPRESS_DB_PASSWORD=test_secret'])
229
+ end
230
+
231
+ it 'does nothing to env array' do
232
+ result = subject.execute[:services]
233
+ expect(result['mysql']['environment']).to eq(['MYSQL_ROOT_PASSWORD=test_secret'])
234
+ end
235
+ end
236
+ it 'returns result hash' do
237
+ outcome = subject.execute
238
+ expect(outcome[:services]).to eq(valid_result)
239
+ end
240
+
241
+ it 'returns validation errors' do
242
+ allow(File).to receive(:read)
243
+ .with(absolute_yaml_path('kontena.yml'))
244
+ .and_return(fixture('kontena-invalid.yml'))
245
+ outcome = subject.execute
246
+ expect(outcome[:errors].size).to eq(1)
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,104 @@
1
+ require_relative '../../../../spec_helper'
2
+ require 'kontena/cli/apps/yaml/service_extender'
3
+
4
+ describe Kontena::Cli::Apps::YAML::ServiceExtender do
5
+ let(:options) do
6
+ {
7
+ 'image' => 'alpine:latest',
8
+ 'ports' => '80:80'
9
+ }
10
+ end
11
+
12
+ let(:parent_options) do
13
+ {
14
+ 'build' => '.'
15
+ }
16
+ end
17
+
18
+ describe '#extend' do
19
+ it 'merges options' do
20
+ result = described_class.new(options).extend(parent_options)
21
+ expected_result = {
22
+ 'image' => 'alpine:latest',
23
+ 'build' => '.',
24
+ 'ports' => '80:80',
25
+ 'environment' => [],
26
+ 'secrets' => []
27
+ }
28
+ expect(result).to eq(expected_result)
29
+ end
30
+
31
+ context 'environment variables' do
32
+ it 'inherites env vars from upper level' do
33
+ from = { 'environment' => ['FOO=bar'] }
34
+ to = {}
35
+ result = described_class.new(to).extend(from)
36
+ expect(result['environment']).to eq(['FOO=bar'])
37
+ end
38
+
39
+ it 'overrides values' do
40
+ from = { 'environment' => ['FOO=bar'] }
41
+ to = { 'environment' => ['FOO=baz'] }
42
+ result = described_class.new(to).extend(from)
43
+ expect(result['environment']).to eq(['FOO=baz'])
44
+ end
45
+
46
+ it 'combines variables' do
47
+ from = { 'environment' => ['FOO=bar'] }
48
+ to = { 'environment' => ['BAR=baz'] }
49
+ result = described_class.new(to).extend(from)
50
+ expect(result['environment']).to eq(['BAR=baz', 'FOO=bar'])
51
+ end
52
+ end
53
+
54
+ context 'secrets' do
55
+ it 'inherites secrets from upper level' do
56
+ secret = {
57
+ 'secret' => 'CUSTOMER_DB_PASSWORD',
58
+ 'name' => 'MYSQL_PASSWORD',
59
+ 'type' => 'env'
60
+ }
61
+ from = { 'secrets' => [secret] }
62
+ to = {}
63
+ result = described_class.new(to).extend(from)
64
+ expect(result['secrets']).to eq([secret])
65
+ end
66
+
67
+ it 'overrides secrets' do
68
+ from_secret = {
69
+ 'secret' => 'CUSTOMER_DB_PASSWORD',
70
+ 'name' => 'MYSQL_PASSWORD',
71
+ 'type' => 'env'
72
+ }
73
+
74
+ to_secret = {
75
+ 'secret' => 'CUSTOMER_DB_PASSWORD',
76
+ 'name' => 'MYSQL_ROOT_PASSWORD',
77
+ 'type' => 'env'
78
+ }
79
+ from = { 'secrets' => [from_secret] }
80
+ to = { 'secrets' => [to_secret] }
81
+ result = described_class.new(to).extend(from)
82
+ expect(result['secrets']).to eq([to_secret])
83
+ end
84
+
85
+ it 'combines secrets' do
86
+ from_secret = {
87
+ 'secret' => 'CUSTOMER_DB_PASSWORD',
88
+ 'name' => 'MYSQL_PASSWORD',
89
+ 'type' => 'env'
90
+ }
91
+
92
+ to_secret = {
93
+ 'secret' => 'CUSTOMER_API_TOKEN',
94
+ 'name' => 'API_TOKEN',
95
+ 'type' => 'env'
96
+ }
97
+ from = { 'secrets' => [from_secret] }
98
+ to = { 'secrets' => [to_secret] }
99
+ result = described_class.new(to).extend(from)
100
+ expect(result['secrets']).to eq([to_secret, from_secret])
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,263 @@
1
+ require_relative '../../../../spec_helper'
2
+ require 'kontena/cli/apps/yaml/validator'
3
+
4
+ describe Kontena::Cli::Apps::YAML::Validator do
5
+ describe '#validate_keys' do
6
+ it 'returns error on invalid key' do
7
+ result = subject.validate_keys('name' => 'wordpress')
8
+ expect(result['name'].size).to eq(1)
9
+ end
10
+ end
11
+
12
+ describe '#validate_options' do
13
+ context 'image' do
14
+ it 'image is optional' do
15
+ result = subject.validate_options('build' => '.')
16
+ expect(result.success?).to be_truthy
17
+ expect(result.messages.key?('image')).to be_falsey
18
+ end
19
+
20
+ it 'must be string' do
21
+ result = subject.validate_options('image' => 10)
22
+ expect(result.success?).to be_falsey
23
+ expect(result.messages.key?('image')).to be_truthy
24
+ end
25
+ end
26
+
27
+ it 'validates stateful is boolean' do
28
+ result = subject.validate_options('stateful' => 'bool')
29
+ expect(result.messages.key?('stateful')).to be_truthy
30
+ end
31
+
32
+ it 'validates net is host or bridge' do
33
+ result = subject.validate_options('net' => 'invalid')
34
+ expect(result.messages.key?('net')).to be_truthy
35
+
36
+ result = subject.validate_options('net' => 'bridge')
37
+ expect(result.messages.key?('net')).to be_falsey
38
+
39
+ result = subject.validate_options('net' => 'host')
40
+ expect(result.messages.key?('net')).to be_falsey
41
+ end
42
+
43
+ context 'affinity' do
44
+ it 'is optional' do
45
+ result = subject.validate_options({})
46
+ expect(result.messages.key?('affinity')).to be_falsey
47
+ end
48
+
49
+ it 'must be array' do
50
+ result = subject.validate_options('affinity' => 'node==node1')
51
+ expect(result.messages.key?('affinity')).to be_truthy
52
+ result = subject.validate_options('affinity' => ['node==node1'])
53
+ expect(result.messages.key?('affinity')).to be_falsey
54
+ end
55
+
56
+ it 'validates format' do
57
+ result = subject.validate_options('affinity' => ['node=node1'])
58
+ expect(result.messages.key?('affinity')).to be_truthy
59
+
60
+ result = subject.validate_options('affinity' => ['node==node1', 'service!=mariadb'])
61
+ expect(result.messages.key?('affinity')).to be_falsey
62
+ end
63
+ end
64
+
65
+ context 'command' do
66
+ it 'is optional' do
67
+ result = subject.validate_options({})
68
+ expect(result.messages.key?('command')).to be_falsey
69
+ end
70
+
71
+ it 'must be string or empty' do
72
+ result = subject.validate_options('command' => 1234)
73
+ expect(result.messages.key?('command')).to be_truthy
74
+
75
+ result = subject.validate_options('command' => nil)
76
+ expect(result.messages.key?('command')).to be_falsey
77
+
78
+ result = subject.validate_options('command' => 'bundle exec rails s')
79
+ expect(result.messages.key?('command')).to be_falsey
80
+ end
81
+ end
82
+
83
+ it 'validates cpu_shares is integer' do
84
+ result = subject.validate_options('cpu_shares' => '1m')
85
+ expect(result.messages.key?('cpu_shares')).to be_truthy
86
+ result = subject.validate_options('cpu_shares' => 1024)
87
+ expect(result.messages.key?('cpu_shares')).to be_falsey
88
+ result = subject.validate_options({})
89
+ expect(result.messages.key?('cpu_shares')).to be_falsey
90
+ end
91
+
92
+ it 'validates environment is array or hash' do
93
+ result = subject.validate_options('environment' => 'KEY=VALUE')
94
+ expect(result.messages.key?('environment')).to be_truthy
95
+ result = subject.validate_options('environment' => ['KEY=VALUE'])
96
+ expect(result.messages.key?('environment')).to be_falsey
97
+ result = subject.validate_options('environment' => { 'KEY' => 'VALUE' })
98
+ expect(result.messages.key?('environment')).to be_falsey
99
+ end
100
+
101
+ context 'validates secrets' do
102
+ it 'must be array' do
103
+ result = subject.validate_options('secrets' => {})
104
+ expect(result.messages.key?('secrets')).to be_truthy
105
+ end
106
+
107
+ context 'item' do
108
+ it 'must contain secret' do
109
+ result = subject.validate_options('secrets' => [{ 'name' => 'test', 'type' => 'env' }])
110
+ expect(result.messages.key?('secrets')).to be_truthy
111
+ end
112
+
113
+ it 'must contain name' do
114
+ result = subject.validate_options('secrets' => [{ 'secret' => 'test', 'type' => 'env' }])
115
+ expect(result.messages.key?('secrets')).to be_truthy
116
+ end
117
+
118
+ it 'must contain type' do
119
+ result = subject.validate_options('secrets' => [{ 'secret' => 'test', 'name' => 'test' }])
120
+ expect(result.messages.key?('secrets')).to be_truthy
121
+ end
122
+
123
+ it 'accepts valid input' do
124
+ result = subject.validate_options('secrets' =>
125
+ [
126
+ {
127
+ 'secret' => 'test',
128
+ 'name' => 'test',
129
+ 'type' => 'env'
130
+ }
131
+ ])
132
+ expect(result.messages.key?('secrets')).to be_falsey
133
+ end
134
+ end
135
+ end
136
+
137
+ context 'validates hooks' do
138
+ context 'validates pre_build' do
139
+ it 'must be array' do
140
+ result = subject.validate_options('hooks' => { 'pre_build' => {} })
141
+ expect(result.messages.key?('hooks')).to be_truthy
142
+ data = {
143
+ 'hooks' => {
144
+ 'pre_build' => [
145
+ {
146
+ 'cmd' => 'rake db:migrate'
147
+ }
148
+ ]
149
+ }
150
+ }
151
+ result = subject.validate_options(data)
152
+ expect(result.messages.key?('hooks')).to be_falsey
153
+ end
154
+ end
155
+ context 'validates post_start' do
156
+ it 'must be array' do
157
+ result = subject.validate_options('hooks' => { 'post_start' => {} })
158
+ expect(result.messages.key?('hooks')).to be_truthy
159
+ data = {
160
+ 'hooks' => {
161
+ 'post_start' => [
162
+ {
163
+ 'name' => 'migrate',
164
+ 'cmd' => 'rake db:migrate',
165
+ 'instances' => '*'
166
+ }
167
+ ]
168
+ }
169
+ }
170
+ result = subject.validate_options(data)
171
+ expect(result.messages.key?('hooks')).to be_falsey
172
+ end
173
+
174
+ context 'item' do
175
+ it 'must contain name' do
176
+ result = subject.validate_options('hooks' =>
177
+ {
178
+ 'post_start' => [
179
+ {
180
+ 'cmd' => 'rake db:migrate',
181
+ 'instances' => '1'
182
+ }
183
+ ]
184
+ })
185
+ expect(result.messages.key?('hooks')).to be_truthy
186
+ end
187
+
188
+ it 'must contain cmd' do
189
+ result = subject.validate_options('hooks' =>
190
+ {
191
+ 'post_start' => [
192
+ {
193
+ 'name' => 'migrate',
194
+ 'instances' => '1'
195
+ }
196
+ ]
197
+ })
198
+ expect(result.messages.key?('hooks')).to be_truthy
199
+ end
200
+
201
+ it 'must contain instance number or *' do
202
+ result = subject.validate_options('hooks' =>
203
+ {
204
+ 'post_start' => [
205
+ { 'name' => 'migrate',
206
+ 'cmd' => 'rake db:migrate'
207
+ }
208
+ ]
209
+ })
210
+ expect(result.messages.key?('hooks')).to be_truthy
211
+ data = {
212
+ 'hooks' => {
213
+ 'post_start' => [
214
+ {
215
+ 'name' => 'migrate',
216
+ 'cmd' => 'rake db:migrate',
217
+ 'instances' => 'all',
218
+ 'oneshot' => true
219
+ }
220
+ ]
221
+ }
222
+ }
223
+ result = subject.validate_options(data)
224
+ expect(result.messages.key?('hooks')).to be_truthy
225
+ end
226
+
227
+ it 'may contain boolean oneshot' do
228
+ data = {
229
+ 'hooks' => {
230
+ 'post_start' => [
231
+ {
232
+ 'name' => 'migrate',
233
+ 'cmd' => 'rake db:migrate',
234
+ 'instances' => '*',
235
+ 'oneshot' => 'true'
236
+ }
237
+ ]
238
+ }
239
+ }
240
+ result = subject.validate_options(data)
241
+ expect(result.messages.key?('hooks')).to be_truthy
242
+ end
243
+ end
244
+
245
+ it 'validates volumes is array' do
246
+ result = subject.validate_options('volumes' => '/app')
247
+ expect(result.messages.key?('volumes')).to be_truthy
248
+
249
+ result = subject.validate_options('volumes' => ['/app'])
250
+ expect(result.messages.key?('volumes')).to be_falsey
251
+ end
252
+
253
+ it 'validates volumes_from is array' do
254
+ result = subject.validate_options('volumes_from' => 'mysql_data')
255
+ expect(result.messages.key?('volumes_from')).to be_truthy
256
+
257
+ result = subject.validate_options('volumes_from' => ['mysql_data'])
258
+ expect(result.messages.key?('volumes_from')).to be_falsey
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end