kontena-cli 0.16.3 → 0.17.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +1 -0
  3. data/.gitignore +3 -1
  4. data/VERSION +1 -1
  5. data/lib/kontena/callbacks/master/deploy/40_install_ssl_certificate_after_deploy.rb +32 -0
  6. data/lib/kontena/cli/apps/deploy_command.rb +2 -2
  7. data/lib/kontena/cli/apps/scale_command.rb +2 -2
  8. data/lib/kontena/cli/apps/show_command.rb +3 -2
  9. data/lib/kontena/cli/apps/yaml/validations.rb +10 -6
  10. data/lib/kontena/cli/apps/yaml/validator.rb +1 -0
  11. data/lib/kontena/cli/apps/yaml/validator_v2.rb +1 -0
  12. data/lib/kontena/cli/cloud/login_command.rb +66 -64
  13. data/lib/kontena/cli/common.rb +0 -10
  14. data/lib/kontena/cli/grids/logs_command.rb +0 -1
  15. data/lib/kontena/cli/localhost_web_server.rb +11 -3
  16. data/lib/kontena/cli/master/login_command.rb +213 -163
  17. data/lib/kontena/cli/nodes/label_command.rb +2 -0
  18. data/lib/kontena/cli/nodes/labels/add_command.rb +7 -8
  19. data/lib/kontena/cli/nodes/labels/list_command.rb +17 -0
  20. data/lib/kontena/cli/nodes/labels/remove_command.rb +7 -12
  21. data/lib/kontena/cli/nodes/show_command.rb +1 -0
  22. data/lib/kontena/cli/plugins/common.rb +8 -0
  23. data/lib/kontena/cli/plugins/install_command.rb +21 -2
  24. data/lib/kontena/cli/plugins/list_command.rb +4 -2
  25. data/lib/kontena/cli/plugins/search_command.rb +4 -2
  26. data/lib/kontena/cli/registry/create_command.rb +19 -12
  27. data/lib/kontena/cli/registry/remove_command.rb +4 -4
  28. data/lib/kontena/cli/registry_command.rb +0 -1
  29. data/lib/kontena/cli/services/create_command.rb +6 -6
  30. data/lib/kontena/cli/services/deploy_command.rb +8 -4
  31. data/lib/kontena/cli/services/list_command.rb +34 -21
  32. data/lib/kontena/cli/services/logs_command.rb +1 -1
  33. data/lib/kontena/cli/services/scale_command.rb +3 -3
  34. data/lib/kontena/cli/services/services_helper.rb +18 -14
  35. data/lib/kontena/cli/services/show_command.rb +1 -0
  36. data/lib/kontena/cli/services/update_command.rb +6 -6
  37. data/lib/kontena/cli/stack_command.rb +12 -6
  38. data/lib/kontena/cli/stacks/build_command.rb +110 -0
  39. data/lib/kontena/cli/stacks/common.rb +85 -20
  40. data/lib/kontena/cli/stacks/deploy_command.rb +30 -7
  41. data/lib/kontena/cli/stacks/install_command.rb +30 -0
  42. data/lib/kontena/cli/stacks/list_command.rb +74 -14
  43. data/lib/kontena/cli/stacks/logs_command.rb +31 -0
  44. data/lib/kontena/cli/stacks/monitor_command.rb +91 -0
  45. data/lib/kontena/cli/stacks/remove_command.rb +24 -7
  46. data/lib/kontena/cli/stacks/service_generator.rb +115 -0
  47. data/lib/kontena/cli/stacks/service_generator_v2.rb +27 -0
  48. data/lib/kontena/cli/stacks/show_command.rb +65 -13
  49. data/lib/kontena/cli/stacks/upgrade_command.rb +28 -0
  50. data/lib/kontena/cli/stacks/yaml/custom_validators/affinities_validator.rb +19 -0
  51. data/lib/kontena/cli/stacks/yaml/custom_validators/build_validator.rb +22 -0
  52. data/lib/kontena/cli/stacks/yaml/custom_validators/extends_validator.rb +21 -0
  53. data/lib/kontena/cli/stacks/yaml/custom_validators/hooks_validator.rb +54 -0
  54. data/lib/kontena/cli/stacks/yaml/custom_validators/secrets_validator.rb +22 -0
  55. data/lib/kontena/cli/stacks/yaml/reader.rb +219 -0
  56. data/lib/kontena/cli/stacks/yaml/service_extender.rb +78 -0
  57. data/lib/kontena/cli/stacks/yaml/validations.rb +71 -0
  58. data/lib/kontena/cli/stacks/yaml/validator_v3.rb +52 -0
  59. data/lib/kontena/cli/version_command.rb +5 -1
  60. data/lib/kontena/cli/vpn/create_command.rb +20 -17
  61. data/lib/kontena/cli/vpn/remove_command.rb +4 -3
  62. data/lib/kontena/client.rb +21 -20
  63. data/lib/kontena/machine/cert_helper.rb +4 -0
  64. data/lib/kontena/machine/cloud_config/cloudinit.yml +1 -1
  65. data/lib/kontena/main_command.rb +1 -1
  66. data/spec/fixtures/kontena-build.yml +2 -2
  67. data/spec/fixtures/kontena-invalid.yml +1 -1
  68. data/spec/fixtures/kontena-not-hash-service-config.yml +1 -1
  69. data/spec/fixtures/kontena-with-env-file.yml +2 -2
  70. data/spec/fixtures/kontena_build_v3.yml +23 -0
  71. data/spec/fixtures/kontena_v3.yml +20 -0
  72. data/spec/fixtures/stack-internal-extend.yml +11 -0
  73. data/spec/fixtures/stack-with-env-file.yml +21 -0
  74. data/spec/fixtures/stack-with-variables.yml +22 -0
  75. data/spec/kontena/cli/app/scale_spec.rb +3 -1
  76. data/spec/kontena/cli/cloud/login_command_spec.rb +283 -0
  77. data/spec/kontena/cli/master/login_command_spec.rb +324 -145
  78. data/spec/kontena/cli/services/link_command_spec.rb +1 -1
  79. data/spec/kontena/cli/services/secrets/link_command_spec.rb +4 -4
  80. data/spec/kontena/cli/services/secrets/unlink_command_spec.rb +2 -2
  81. data/spec/kontena/cli/services/services_helper_spec.rb +15 -11
  82. data/spec/kontena/cli/services/unlink_command_spec.rb +1 -1
  83. data/spec/kontena/cli/stacks/deploy_command_spec.rb +26 -0
  84. data/spec/kontena/cli/stacks/install_command_spec.rb +54 -0
  85. data/spec/kontena/cli/stacks/list_command_spec.rb +27 -0
  86. data/spec/kontena/cli/stacks/remove_command_spec.rb +45 -0
  87. data/spec/kontena/cli/stacks/service_generator_spec.rb +385 -0
  88. data/spec/kontena/cli/stacks/service_generator_v2_spec.rb +74 -0
  89. data/spec/kontena/cli/stacks/show_command_spec.rb +26 -0
  90. data/spec/kontena/cli/stacks/upgrade_command_spec.rb +50 -0
  91. data/spec/kontena/cli/stacks/yaml/reader_spec.rb +370 -0
  92. data/spec/kontena/cli/stacks/yaml/service_extender_spec.rb +128 -0
  93. data/spec/kontena/cli/stacks/yaml/validator_v3_spec.rb +302 -0
  94. data/spec/spec_helper.rb +6 -4
  95. data/spec/support/client_helpers.rb +1 -0
  96. metadata +57 -7
  97. data/lib/kontena/cli/registry/delete_command.rb +0 -18
  98. data/lib/kontena/cli/stacks/create_command.rb +0 -27
  99. data/lib/kontena/cli/stacks/update_command.rb +0 -27
@@ -0,0 +1,128 @@
1
+ require_relative '../../../../spec_helper'
2
+ require 'kontena/cli/stacks/yaml/service_extender'
3
+
4
+ describe Kontena::Cli::Stacks::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_from(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(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(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(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(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(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(from)
100
+ expect(result['secrets']).to eq([to_secret, from_secret])
101
+ end
102
+ end
103
+
104
+ context 'build args' do
105
+ it 'inherits build args from upper level' do
106
+ from = { 'build' => { 'args' => {'foo' => 'bar'}} }
107
+ to = {}
108
+ result = described_class.new(to).extend_from(from)
109
+ expect(result['build']['args']).to eq({'foo' => 'bar'})
110
+ end
111
+
112
+ it 'overrides values' do
113
+ from = { 'build' => { 'args' => {'foo' => 'bar'}} }
114
+ to = { 'build' => { 'args' => {'foo' => 'baz'}} }
115
+ result = described_class.new(to).extend_from(from)
116
+ expect(result['build']['args']).to eq({'foo' => 'baz'})
117
+ end
118
+
119
+ it 'combines variables' do
120
+ from = { 'build' => { 'args' => {'foo' => 'bar'}} }
121
+ to = { 'build' => { 'args' => {'baz' => 'baf'}} }
122
+ result = described_class.new(to).extend_from(from)
123
+ expect(result['build']['args']).to eq({'foo' => 'bar', 'baz' => 'baf'})
124
+ end
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,302 @@
1
+ require_relative '../../../../spec_helper'
2
+ require 'kontena/cli/stacks/yaml/validator_v3'
3
+
4
+ describe Kontena::Cli::Stacks::YAML::ValidatorV3 do
5
+ describe '#validate_options' do
6
+ context 'build' do
7
+ it 'can be string' do
8
+ result = subject.validate_options('build' => '.')
9
+ expect(result.valid?).to be_truthy
10
+ expect(result.errors.key?('build')).to be_falsey
11
+ end
12
+
13
+ it 'can be hash' do
14
+ result = subject.validate_options('build' => { 'context' => '.' })
15
+ expect(result.valid?).to be_truthy
16
+ expect(result.errors.key?('build')).to be_falsey
17
+ end
18
+
19
+ it 'returns error if build is hash and context is missing' do
20
+ result = subject.validate_options('build' => {})
21
+ expect(result.valid?).to be_falsey
22
+ expect(result.errors.key?('build')).to be_truthy
23
+ end
24
+
25
+ it 'returns error if optional dockerfile is not string' do
26
+ result = subject.validate_options('build' => {
27
+ 'context' => '.',
28
+ 'dockerfile' => 123
29
+ })
30
+ expect(result.valid?).to be_falsey
31
+ expect(result.errors.key?('build')).to be_truthy
32
+ end
33
+ end
34
+ it 'validates image is string' do
35
+ result = subject.validate_options('image' => true)
36
+ expect(result.valid?).to be_falsey
37
+ expect(result.errors.key?('image')).to be_truthy
38
+ end
39
+
40
+ it 'validates stateful is boolean' do
41
+ result = subject.validate_options('stateful' => 'bool')
42
+ expect(result.errors.key?('stateful')).to be_truthy
43
+ end
44
+
45
+ it 'validates network_mode is host or bridge' do
46
+ result = subject.validate_options('network_mode' => 'invalid')
47
+ expect(result.errors.key?('network_mode')).to be_truthy
48
+
49
+ result = subject.validate_options('network_mode' => 'bridge')
50
+ expect(result.errors.key?('network_mode')).to be_falsey
51
+
52
+ result = subject.validate_options('network_mode' => 'host')
53
+ expect(result.errors.key?('network_mode')).to be_falsey
54
+ end
55
+
56
+ context 'affinity' do
57
+ it 'is optional' do
58
+ result = subject.validate_options({})
59
+ expect(result.errors.key?('affinity')).to be_falsey
60
+ end
61
+
62
+ it 'must be array' do
63
+ result = subject.validate_options('affinity' => 'node==node1')
64
+ expect(result.errors.key?('affinity')).to be_truthy
65
+ result = subject.validate_options('affinity' => ['node==node1'])
66
+ expect(result.errors.key?('affinity')).to be_falsey
67
+ end
68
+
69
+ it 'validates format' do
70
+ result = subject.validate_options('affinity' => ['node=node1'])
71
+ expect(result.errors.key?('affinity')).to be_truthy
72
+
73
+ result = subject.validate_options('affinity' => ['node==node1', 'service!=mariadb'])
74
+ expect(result.errors.key?('affinity')).to be_falsey
75
+ end
76
+ end
77
+
78
+ context 'command' do
79
+ it 'is optional' do
80
+ result = subject.validate_options({})
81
+ expect(result.errors.key?('command')).to be_falsey
82
+ end
83
+
84
+ it 'must be string or empty' do
85
+ result = subject.validate_options('command' => 1234)
86
+ expect(result.errors.key?('command')).to be_truthy
87
+
88
+ result = subject.validate_options('command' => nil)
89
+ expect(result.errors.key?('command')).to be_falsey
90
+
91
+ result = subject.validate_options('command' => 'bundle exec rails s')
92
+ expect(result.errors.key?('command')).to be_falsey
93
+ end
94
+ end
95
+
96
+ it 'validates cpu_shares is integer' do
97
+ result = subject.validate_options('cpu_shares' => '1m')
98
+ expect(result.errors.key?('cpu_shares')).to be_truthy
99
+ result = subject.validate_options('cpu_shares' => 1024)
100
+ expect(result.errors.key?('cpu_shares')).to be_falsey
101
+ result = subject.validate_options({})
102
+ expect(result.errors.key?('cpu_shares')).to be_falsey
103
+ end
104
+
105
+ it 'validates environment is array or hash' do
106
+ result = subject.validate_options('environment' => 'KEY=VALUE')
107
+ expect(result.errors.key?('environment')).to be_truthy
108
+ result = subject.validate_options('environment' => ['KEY=VALUE'])
109
+ expect(result.errors.key?('environment')).to be_falsey
110
+ result = subject.validate_options('environment' => { 'KEY' => 'VALUE' })
111
+ expect(result.errors.key?('environment')).to be_falsey
112
+ end
113
+
114
+ context 'validates secrets' do
115
+ it 'must be array' do
116
+ result = subject.validate_options('secrets' => {})
117
+ expect(result.errors.key?('secrets')).to be_truthy
118
+ end
119
+
120
+ context 'item' do
121
+ it 'must contain secret' do
122
+ result = subject.validate_options('secrets' => [{ 'name' => 'test', 'type' => 'env' }])
123
+ expect(result.errors.key?('secrets')).to be_truthy
124
+ end
125
+
126
+ it 'must contain name' do
127
+ result = subject.validate_options('secrets' => [{ 'secret' => 'test', 'type' => 'env' }])
128
+ expect(result.errors.key?('secrets')).to be_truthy
129
+ end
130
+
131
+ it 'must contain type' do
132
+ result = subject.validate_options('secrets' => [{ 'secret' => 'test', 'name' => 'test' }])
133
+ expect(result.errors.key?('secrets')).to be_truthy
134
+ end
135
+
136
+ it 'accepts valid input' do
137
+ result = subject.validate_options('secrets' =>
138
+ [
139
+ {
140
+ 'secret' => 'test',
141
+ 'name' => 'test',
142
+ 'type' => 'env'
143
+ }
144
+ ])
145
+ expect(result.errors.key?('secrets')).to be_falsey
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'logging' do
151
+ context 'options' do
152
+ it 'must be hash' do
153
+ result = subject.validate_options('logging' => { 'options' => [] })
154
+ expect(result.errors.key?('logging')).to be_truthy
155
+ data = {
156
+ 'logging' => {
157
+ 'options' => {
158
+ 'syslog-address' => "tcp://192.168.0.42:123"
159
+ }
160
+ }
161
+ }
162
+ result = subject.validate_options(data)
163
+ expect(result.errors.key?('logging')).to be_falsey
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'hooks' do
169
+ context 'validates pre_build' do
170
+ it 'must be array' do
171
+ result = subject.validate_options('hooks' => { 'pre_build' => {} })
172
+ expect(result.errors.key?('hooks')).to be_truthy
173
+ data = {
174
+ 'hooks' => {
175
+ 'pre_build' => [
176
+ {
177
+ 'cmd' => 'rake db:migrate'
178
+ }
179
+ ]
180
+ }
181
+ }
182
+ result = subject.validate_options(data)
183
+ expect(result.errors.key?('hooks')).to be_falsey
184
+ end
185
+ end
186
+ context 'post_start' do
187
+ it 'must be array' do
188
+ result = subject.validate_options('hooks' => { 'post_start' => {} })
189
+ expect(result.errors.key?('hooks')).to be_truthy
190
+ data = {
191
+ 'hooks' => {
192
+ 'post_start' => [
193
+ {
194
+ 'name' => 'migrate',
195
+ 'cmd' => 'rake db:migrate',
196
+ 'instances' => '*'
197
+ }
198
+ ]
199
+ }
200
+ }
201
+ result = subject.validate_options(data)
202
+ expect(result.errors.key?('hooks')).to be_falsey
203
+ end
204
+
205
+ context 'item' do
206
+ it 'must contain name' do
207
+ result = subject.validate_options('hooks' =>
208
+ {
209
+ 'post_start' => [
210
+ {
211
+ 'cmd' => 'rake db:migrate',
212
+ 'instances' => '1'
213
+ }
214
+ ]
215
+ })
216
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
217
+ end
218
+
219
+ it 'must contain cmd' do
220
+ result = subject.validate_options('hooks' =>
221
+ {
222
+ 'post_start' => [
223
+ {
224
+ 'name' => 'migrate',
225
+ 'instances' => '1'
226
+ }
227
+ ]
228
+ })
229
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
230
+ end
231
+
232
+ it 'must contain instance number or *' do
233
+ result = subject.validate_options('hooks' =>
234
+ {
235
+ 'post_start' => [
236
+ { 'name' => 'migrate',
237
+ 'cmd' => 'rake db:migrate'
238
+ }
239
+ ]
240
+ })
241
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
242
+ data = {
243
+ 'hooks' => {
244
+ 'post_start' => [
245
+ {
246
+ 'name' => 'migrate',
247
+ 'cmd' => 'rake db:migrate',
248
+ 'instances' => 'all',
249
+ 'oneshot' => true
250
+ }
251
+ ]
252
+ }
253
+ }
254
+ result = subject.validate_options(data)
255
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
256
+ end
257
+
258
+ it 'may contain boolean oneshot' do
259
+ data = {
260
+ 'hooks' => {
261
+ 'post_start' => [
262
+ {
263
+ 'name' => 'migrate',
264
+ 'cmd' => 'rake db:migrate',
265
+ 'instances' => '*',
266
+ 'oneshot' => 'true'
267
+ }
268
+ ]
269
+ }
270
+ }
271
+ result = subject.validate_options(data)
272
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
273
+ end
274
+ end
275
+
276
+ it 'validates depends_on is array' do
277
+ result = subject.validate_options('depends_on' => 'web')
278
+ expect(result.errors.key?('depends_on')).to be_truthy
279
+
280
+ result = subject.validate_options('depends_on' => ['web'])
281
+ expect(result.errors.key?('depends_on')).to be_falsey
282
+ end
283
+
284
+ it 'validates volumes is array' do
285
+ result = subject.validate_options('volumes' => '/app')
286
+ expect(result.errors.key?('volumes')).to be_truthy
287
+
288
+ result = subject.validate_options('volumes' => ['/app'])
289
+ expect(result.errors.key?('volumes')).to be_falsey
290
+ end
291
+
292
+ it 'validates volumes_from is array' do
293
+ result = subject.validate_options('volumes_from' => 'mysql_data')
294
+ expect(result.errors.key?('volumes_from')).to be_truthy
295
+
296
+ result = subject.validate_options('volumes_from' => ['mysql_data'])
297
+ expect(result.errors.key?('volumes_from')).to be_falsey
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
data/spec/spec_helper.rb CHANGED
@@ -40,10 +40,12 @@ RSpec.configure do |config|
40
40
  end
41
41
 
42
42
  config.around(:each) do |example|
43
- begin
44
- example.run
45
- rescue SystemExit
46
- puts "Got SystemExit: #{$!.message} - Exit code: #{$!.status}"
43
+ catch :exit_with_error do
44
+ begin
45
+ example.run
46
+ rescue SystemExit
47
+ puts "Got SystemExit: #{$!.message} - Exit code: #{$!.status}"
48
+ end
47
49
  end
48
50
  end
49
51
 
@@ -27,6 +27,7 @@ module ClientHelpers
27
27
  RSpec::Mocks.space.proxy_for(File).reset
28
28
  allow(subject).to receive(:client).and_return(client)
29
29
  allow(subject).to receive(:current_grid).and_return('test-grid')
30
+ allow(File).to receive(:exist?).with(File.join(Dir.home, '.kontena/certs/.pem')).and_return(false)
30
31
  allow(File).to receive(:exist?).with(File.join(Dir.home, '.kontena_client.json')).and_return(true)
31
32
  allow(File).to receive(:readable?).with(File.join(Dir.home, '.kontena_client.json')).and_return(true)
32
33
  allow(File).to receive(:read).and_call_original