chef-dk 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chef-dk/builtin_commands.rb +2 -0
- data/lib/chef-dk/chef_runner.rb +5 -1
- data/lib/chef-dk/command/provision.rb +399 -0
- data/lib/chef-dk/cookbook_profiler/git.rb +59 -3
- data/lib/chef-dk/exceptions.rb +1 -13
- data/lib/chef-dk/service_exceptions.rb +38 -22
- data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +1 -1
- data/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +5 -6
- data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +0 -3
- data/lib/chef-dk/skeletons/code_generator/templates/default/serverspec_default_spec.rb.erb +1 -4
- data/lib/chef-dk/version.rb +1 -1
- data/spec/unit/chef_runner_spec.rb +8 -1
- data/spec/unit/command/provision_spec.rb +536 -0
- data/spec/unit/cookbook_profiler/git_spec.rb +99 -62
- metadata +107 -5
data/lib/chef-dk/exceptions.rb
CHANGED
@@ -68,19 +68,7 @@ module ChefDK
|
|
68
68
|
class InvalidPolicyfileFilename < StandardError
|
69
69
|
end
|
70
70
|
|
71
|
-
class
|
72
|
-
|
73
|
-
attr_reader :cause
|
74
|
-
|
75
|
-
def initialize(message, cause)
|
76
|
-
super(message)
|
77
|
-
@cause = cause
|
78
|
-
end
|
79
|
-
|
71
|
+
class BUG < RuntimeError
|
80
72
|
end
|
81
73
|
|
82
|
-
class CookbookNotFound < ChefRunnerError; end
|
83
|
-
|
84
|
-
class ChefConvergeError < ChefRunnerError; end
|
85
|
-
|
86
74
|
end
|
@@ -24,28 +24,7 @@ require 'chef-dk/service_exception_inspectors'
|
|
24
24
|
|
25
25
|
module ChefDK
|
26
26
|
|
27
|
-
|
28
|
-
# raise this directly, create a descriptively-named subclass. You can rescue
|
29
|
-
# this to catch all errors from PolicyfileServices objects though.
|
30
|
-
class PolicyfileServiceError < StandardError
|
31
|
-
end
|
32
|
-
|
33
|
-
class PolicyfileNotFound < PolicyfileServiceError
|
34
|
-
end
|
35
|
-
|
36
|
-
class LockfileNotFound < PolicyfileServiceError
|
37
|
-
end
|
38
|
-
|
39
|
-
class MalformedLockfile < PolicyfileServiceError
|
40
|
-
end
|
41
|
-
|
42
|
-
class GitError < PolicyfileServiceError
|
43
|
-
end
|
44
|
-
|
45
|
-
class ExportDirNotEmpty < PolicyfileServiceError
|
46
|
-
end
|
47
|
-
|
48
|
-
class PolicyfileNestedException < PolicyfileServiceError
|
27
|
+
module NestedExceptionWithInspector
|
49
28
|
|
50
29
|
attr_reader :cause
|
51
30
|
attr_reader :inspector
|
@@ -76,6 +55,33 @@ module ChefDK
|
|
76
55
|
|
77
56
|
end
|
78
57
|
|
58
|
+
# Base class for errors raised by ChefDK::PolicyfileServices objects. Don't
|
59
|
+
# raise this directly, create a descriptively-named subclass. You can rescue
|
60
|
+
# this to catch all errors from PolicyfileServices objects though.
|
61
|
+
class PolicyfileServiceError < StandardError
|
62
|
+
end
|
63
|
+
|
64
|
+
class PolicyfileNotFound < PolicyfileServiceError
|
65
|
+
end
|
66
|
+
|
67
|
+
class LockfileNotFound < PolicyfileServiceError
|
68
|
+
end
|
69
|
+
|
70
|
+
class MalformedLockfile < PolicyfileServiceError
|
71
|
+
end
|
72
|
+
|
73
|
+
class GitError < PolicyfileServiceError
|
74
|
+
end
|
75
|
+
|
76
|
+
class ExportDirNotEmpty < PolicyfileServiceError
|
77
|
+
end
|
78
|
+
|
79
|
+
class PolicyfileNestedException < PolicyfileServiceError
|
80
|
+
|
81
|
+
include NestedExceptionWithInspector
|
82
|
+
|
83
|
+
end
|
84
|
+
|
79
85
|
class PolicyfileDownloadError < PolicyfileNestedException
|
80
86
|
end
|
81
87
|
|
@@ -91,5 +97,15 @@ module ChefDK
|
|
91
97
|
class PolicyfileExportRepoError < PolicyfileNestedException
|
92
98
|
end
|
93
99
|
|
100
|
+
class ChefRunnerError < StandardError
|
101
|
+
|
102
|
+
include NestedExceptionWithInspector
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
class CookbookNotFound < ChefRunnerError; end
|
107
|
+
|
108
|
+
class ChefConvergeError < ChefRunnerError; end
|
109
|
+
|
94
110
|
end
|
95
111
|
|
@@ -1,8 +1,7 @@
|
|
1
|
-
name
|
2
|
-
maintainer
|
1
|
+
name '<%= cookbook_name %>'
|
2
|
+
maintainer '<%= copyright_holder %>'
|
3
3
|
maintainer_email '<%= email %>'
|
4
|
-
license
|
5
|
-
description
|
4
|
+
license '<%= license %>'
|
5
|
+
description 'Installs/Configures <%= cookbook_name %>'
|
6
6
|
long_description 'Installs/Configures <%= cookbook_name %>'
|
7
|
-
version
|
8
|
-
|
7
|
+
version '0.1.0'
|
@@ -7,9 +7,7 @@
|
|
7
7
|
require 'spec_helper'
|
8
8
|
|
9
9
|
describe '<%= cookbook_name %>::<%= recipe_name %>' do
|
10
|
-
|
11
10
|
context 'When all attributes are default, on an unspecified platform' do
|
12
|
-
|
13
11
|
let(:chef_run) do
|
14
12
|
runner = ChefSpec::ServerRunner.new
|
15
13
|
runner.converge(described_recipe)
|
@@ -18,6 +16,5 @@ describe '<%= cookbook_name %>::<%= recipe_name %>' do
|
|
18
16
|
it 'converges successfully' do
|
19
17
|
chef_run # This should not raise an error
|
20
18
|
end
|
21
|
-
|
22
19
|
end
|
23
20
|
end
|
data/lib/chef-dk/version.rb
CHANGED
@@ -54,7 +54,14 @@ describe ChefDK::ChefRunner do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it "configures a formatter for the chef run" do
|
57
|
-
expect(chef_runner.formatter).to be_a(Chef::
|
57
|
+
expect(chef_runner.formatter).to be_a(Chef::EventDispatch::Dispatcher)
|
58
|
+
|
59
|
+
# TODO: Once https://github.com/chef/chef/pull/3340 is merged/released,
|
60
|
+
# just use `formatter.subscribers`
|
61
|
+
subscribers = chef_runner.formatter.instance_variable_get(:@subscribers)
|
62
|
+
|
63
|
+
expect(subscribers.size).to eq(1)
|
64
|
+
expect(subscribers.first).to be_a(Chef::Formatters::Doc)
|
58
65
|
end
|
59
66
|
|
60
67
|
it "detects the platform with ohai" do
|
@@ -0,0 +1,536 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2015 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'spec_helper'
|
19
|
+
require 'shared/command_with_ui_object'
|
20
|
+
require 'chef-dk/command/provision'
|
21
|
+
|
22
|
+
describe ChefDK::Command::Provision do
|
23
|
+
|
24
|
+
it_behaves_like "a command with a UI object"
|
25
|
+
|
26
|
+
let(:command) do
|
27
|
+
described_class.new
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:push_service) { instance_double(ChefDK::PolicyfileServices::Push) }
|
31
|
+
|
32
|
+
let(:chef_config_loader) { instance_double("Chef::WorkstationConfigLoader") }
|
33
|
+
|
34
|
+
let(:chef_config) { double("Chef::Config") }
|
35
|
+
|
36
|
+
let(:config_arg) { nil }
|
37
|
+
|
38
|
+
before do
|
39
|
+
ChefDK::ProvisioningData.reset
|
40
|
+
|
41
|
+
stub_const("Chef::Config", chef_config)
|
42
|
+
allow(Chef::WorkstationConfigLoader).to receive(:new).with(config_arg).and_return(chef_config_loader)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "evaluating CLI options and arguments" do
|
46
|
+
|
47
|
+
let(:ui) { TestHelpers::TestUI.new }
|
48
|
+
|
49
|
+
before do
|
50
|
+
command.ui = ui
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "when input is invalid" do
|
54
|
+
|
55
|
+
context "when not enough arguments are given" do
|
56
|
+
|
57
|
+
let(:params) { [] }
|
58
|
+
|
59
|
+
it "prints usage and exits non-zero" do
|
60
|
+
expect(command.run(params)).to eq(1)
|
61
|
+
expect(ui.output).to include("You must specify a POLICY_GROUP or disable policyfiles with --no-policy")
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when --no-policy is combined with policy arguments" do
|
67
|
+
|
68
|
+
let(:params) { %w[ --no-policy some-policy-group ] }
|
69
|
+
|
70
|
+
it "prints usage and exits non-zero" do
|
71
|
+
expect(command.run(params)).to eq(1)
|
72
|
+
expect(ui.output).to include("The --no-policy flag cannot be combined with policyfile arguments")
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when a POLICY_GROUP is given but neither of --sync or --policy-name are given" do
|
78
|
+
|
79
|
+
let(:params) { %w[ some-policy-group ] }
|
80
|
+
|
81
|
+
it "prints usage and exits non-zero" do
|
82
|
+
expect(command.run(params)).to eq(1)
|
83
|
+
expect(ui.output).to include("You must pass either --sync or --policy-name to provision machines in policyfile mode")
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when both --sync and --policy-name are given" do
|
89
|
+
|
90
|
+
let(:params) { %w[ some-policy-group --policy-name foo --sync] }
|
91
|
+
|
92
|
+
it "prints usage and exits non-zero" do
|
93
|
+
expect(command.run(params)).to eq(1)
|
94
|
+
expect(ui.output).to include("The --policy-name and --sync arguments cannot be combined")
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when too many arguments are given" do
|
100
|
+
|
101
|
+
let(:params) { %w[ policygroup extraneous-argument --sync ] }
|
102
|
+
|
103
|
+
it "prints usage and exits non-zero" do
|
104
|
+
expect(command.run(params)).to eq(1)
|
105
|
+
expect(ui.output).to include("Too many arguments")
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "when input is valid" do
|
112
|
+
|
113
|
+
let(:context) { ChefDK::ProvisioningData.context }
|
114
|
+
|
115
|
+
shared_examples "common_optional_options" do
|
116
|
+
|
117
|
+
context "with default option values" do
|
118
|
+
|
119
|
+
it "node name is not specified" do
|
120
|
+
expect(command.node_name).to eq(nil)
|
121
|
+
expect(context.node_name).to eq(nil)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "sets the cookbook path to CWD" do
|
125
|
+
# this is cookbook_path in the chef sense, a directory with cookbooks in it.
|
126
|
+
expect(command.provisioning_cookbook_path).to eq(Dir.pwd)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "sets the cookbook name to 'provision'" do
|
130
|
+
expect(command.provisioning_cookbook_name).to eq('provision')
|
131
|
+
end
|
132
|
+
|
133
|
+
it "sets the recipe to 'default'" do
|
134
|
+
expect(command.recipe).to eq("default")
|
135
|
+
expect(command.chef_runner.run_list).to eq(["recipe[provision::default]"])
|
136
|
+
end
|
137
|
+
|
138
|
+
it "sets the default action to converge" do
|
139
|
+
expect(command.default_action).to eq(:converge)
|
140
|
+
expect(context.action).to eq(:converge)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
context "with -n NODE_NAME" do
|
146
|
+
|
147
|
+
let(:extra_params) { %w[ -n example-node ] }
|
148
|
+
|
149
|
+
it "sets the default requested node name" do
|
150
|
+
expect(command.node_name).to eq("example-node")
|
151
|
+
expect(context.node_name).to eq("example-node")
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
context "with --cookbook COOKBOOK_PATH" do
|
157
|
+
|
158
|
+
let(:extra_params) { %w[ --cookbook ~/mystuff/my-provision-cookbook ] }
|
159
|
+
|
160
|
+
let(:expected_cookbook_path) { File.expand_path("~/mystuff") }
|
161
|
+
let(:expected_cookbook_name) { "my-provision-cookbook" }
|
162
|
+
|
163
|
+
it "sets the cookbook path" do
|
164
|
+
# this is cookbook_path in the chef sense, a directory with cookbooks in it.
|
165
|
+
expect(command.provisioning_cookbook_path).to eq(expected_cookbook_path)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "sets the cookbook name" do
|
169
|
+
expect(command.provisioning_cookbook_name).to eq(expected_cookbook_name)
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
context "with -c CONFIG_FILE" do
|
175
|
+
|
176
|
+
let(:config_arg) { "~/somewhere_else/knife.rb" }
|
177
|
+
|
178
|
+
let(:extra_params) { [ "-c", config_arg ] }
|
179
|
+
|
180
|
+
it "loads config from the specified location" do
|
181
|
+
# The configurable module uses config[:config_file]
|
182
|
+
expect(command.config[:config_file]).to eq("~/somewhere_else/knife.rb")
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
context "with -r MACHINE_RECIPE" do
|
188
|
+
|
189
|
+
let(:extra_params) { %w[ -r ec2cluster ] }
|
190
|
+
|
191
|
+
it "sets the recipe to run as specified" do
|
192
|
+
expect(command.recipe).to eq("ec2cluster")
|
193
|
+
expect(command.chef_runner.run_list).to eq(["recipe[provision::ec2cluster]"])
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
context "with -d" do
|
199
|
+
|
200
|
+
let(:extra_params) { %w[ -d ] }
|
201
|
+
|
202
|
+
it "sets the default action to destroy" do
|
203
|
+
expect(command.default_action).to eq(:destroy)
|
204
|
+
expect(context.action).to eq(:destroy)
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end # shared examples
|
210
|
+
|
211
|
+
context "when --no-policy is given" do
|
212
|
+
|
213
|
+
before do
|
214
|
+
allow(chef_config_loader).to receive(:load)
|
215
|
+
allow(command).to receive(:push).and_return(push_service)
|
216
|
+
|
217
|
+
allow(chef_config).to receive(:ssl_verify_mode).and_return(:verify_peer)
|
218
|
+
|
219
|
+
command.apply_params!(params)
|
220
|
+
command.setup_context
|
221
|
+
end
|
222
|
+
|
223
|
+
let(:extra_params) { [] }
|
224
|
+
let(:params) { %w[ --no-policy ] + extra_params }
|
225
|
+
|
226
|
+
it "disables policyfile integration" do
|
227
|
+
expect(command.enable_policyfile?).to be(false)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "generates chef config with no policyfile options" do
|
231
|
+
expected_config = <<-CONFIG
|
232
|
+
# SSL Settings:
|
233
|
+
ssl_verify_mode :verify_peer
|
234
|
+
|
235
|
+
CONFIG
|
236
|
+
expect(context.chef_config).to eq(expected_config)
|
237
|
+
end
|
238
|
+
|
239
|
+
include_examples "common_optional_options"
|
240
|
+
|
241
|
+
end # when --no-policy is given
|
242
|
+
|
243
|
+
context "when --sync POLICYFILE argument is given" do
|
244
|
+
|
245
|
+
let(:policy_data) { { "name" => "myapp" } }
|
246
|
+
|
247
|
+
before do
|
248
|
+
allow(chef_config_loader).to receive(:load)
|
249
|
+
|
250
|
+
allow(ChefDK::PolicyfileServices::Push).to receive(:new).
|
251
|
+
with(policyfile: given_policyfile_path, ui: ui, policy_group: given_policy_group, config: chef_config, root_dir: Dir.pwd).
|
252
|
+
and_return(push_service)
|
253
|
+
|
254
|
+
allow(push_service).to receive(:policy_data).and_return(policy_data)
|
255
|
+
|
256
|
+
command.apply_params!(params)
|
257
|
+
command.setup_context
|
258
|
+
end
|
259
|
+
|
260
|
+
context "with explicit policyfile relative path" do
|
261
|
+
|
262
|
+
let(:given_policyfile_path) { "policies/OtherPolicy.rb" }
|
263
|
+
|
264
|
+
let(:given_policy_group) { "some-policy-group" }
|
265
|
+
|
266
|
+
let(:params) { [ given_policy_group, '--sync', given_policyfile_path ] }
|
267
|
+
|
268
|
+
it "sets policy group" do
|
269
|
+
expect(command.policy_group).to eq(given_policy_group)
|
270
|
+
expect(context.policy_group).to eq(given_policy_group)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "sets policy name" do
|
274
|
+
expect(command.policy_name).to eq("myapp")
|
275
|
+
expect(context.policy_name).to eq("myapp")
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
context "with implicit policyfile relative path" do
|
281
|
+
|
282
|
+
let(:given_policyfile_path) { nil }
|
283
|
+
|
284
|
+
let(:given_policy_group) { "some-policy-group" }
|
285
|
+
|
286
|
+
let(:extra_params) { [] }
|
287
|
+
|
288
|
+
let(:params) { [ given_policy_group, '--sync' ] + extra_params }
|
289
|
+
|
290
|
+
before do
|
291
|
+
allow(chef_config).to receive(:ssl_verify_mode).and_return(:verify_peer)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "sets policy group" do
|
295
|
+
expect(command.policy_group).to eq(given_policy_group)
|
296
|
+
expect(context.policy_group).to eq(given_policy_group)
|
297
|
+
end
|
298
|
+
|
299
|
+
it "sets policy name" do
|
300
|
+
expect(command.policy_name).to eq("myapp")
|
301
|
+
expect(context.policy_name).to eq("myapp")
|
302
|
+
end
|
303
|
+
|
304
|
+
it "generates chef config with policyfile options" do
|
305
|
+
expected_config = <<-CONFIG
|
306
|
+
# SSL Settings:
|
307
|
+
ssl_verify_mode :verify_peer
|
308
|
+
|
309
|
+
# Policyfile Settings:
|
310
|
+
use_policyfile true
|
311
|
+
policy_document_native_api true
|
312
|
+
|
313
|
+
policy_group "some-policy-group"
|
314
|
+
policy_name "myapp"
|
315
|
+
|
316
|
+
CONFIG
|
317
|
+
expect(context.chef_config).to eq(expected_config)
|
318
|
+
end
|
319
|
+
|
320
|
+
|
321
|
+
include_examples "common_optional_options"
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
end # when --sync POLICYFILE argument is given
|
326
|
+
|
327
|
+
context "when a --policy-name is given" do
|
328
|
+
|
329
|
+
let(:given_policy_group) { "some-policy-group" }
|
330
|
+
|
331
|
+
let(:extra_params) { [] }
|
332
|
+
|
333
|
+
let(:params) { [ given_policy_group, '--policy-name', "myapp" ] + extra_params }
|
334
|
+
|
335
|
+
|
336
|
+
before do
|
337
|
+
command.apply_params!(params)
|
338
|
+
command.setup_context
|
339
|
+
|
340
|
+
allow(chef_config).to receive(:ssl_verify_mode).and_return(:verify_peer)
|
341
|
+
end
|
342
|
+
|
343
|
+
it "sets policy group" do
|
344
|
+
expect(command.policy_group).to eq(given_policy_group)
|
345
|
+
expect(context.policy_group).to eq(given_policy_group)
|
346
|
+
end
|
347
|
+
|
348
|
+
it "sets policy name" do
|
349
|
+
expect(command.policy_name).to eq("myapp")
|
350
|
+
expect(context.policy_name).to eq("myapp")
|
351
|
+
end
|
352
|
+
|
353
|
+
it "generates chef config with policyfile options" do
|
354
|
+
expected_config = <<-CONFIG
|
355
|
+
# SSL Settings:
|
356
|
+
ssl_verify_mode :verify_peer
|
357
|
+
|
358
|
+
# Policyfile Settings:
|
359
|
+
use_policyfile true
|
360
|
+
policy_document_native_api true
|
361
|
+
|
362
|
+
policy_group "some-policy-group"
|
363
|
+
policy_name "myapp"
|
364
|
+
|
365
|
+
CONFIG
|
366
|
+
expect(context.chef_config).to eq(expected_config)
|
367
|
+
end
|
368
|
+
|
369
|
+
include_examples "common_optional_options"
|
370
|
+
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
|
376
|
+
describe "running the provision cookbook" do
|
377
|
+
|
378
|
+
let(:ui) { TestHelpers::TestUI.new }
|
379
|
+
|
380
|
+
before do
|
381
|
+
allow(chef_config_loader).to receive(:load)
|
382
|
+
allow(command).to receive(:push).and_return(push_service)
|
383
|
+
command.ui = ui
|
384
|
+
end
|
385
|
+
|
386
|
+
let(:provision_cookbook_path) { File.expand_path("provision", Dir.pwd) }
|
387
|
+
let(:provision_recipe_path) { File.join(provision_cookbook_path, "recipes", "default.rb") }
|
388
|
+
|
389
|
+
let(:chef_runner) { instance_double("ChefDK::ChefRunner") }
|
390
|
+
|
391
|
+
let(:params) { %w[ policygroup --sync ] }
|
392
|
+
|
393
|
+
context "when the provision cookbook doesn't exist" do
|
394
|
+
|
395
|
+
before do
|
396
|
+
allow(File).to receive(:exist?).with(provision_cookbook_path).and_return(false)
|
397
|
+
end
|
398
|
+
|
399
|
+
it "prints an error and exits non-zero" do
|
400
|
+
expect(command.run(params)).to eq(1)
|
401
|
+
expect(ui.output).to include("Provisioning cookbook not found at path #{provision_cookbook_path}")
|
402
|
+
end
|
403
|
+
|
404
|
+
end
|
405
|
+
|
406
|
+
context "when the provision cookbook doesn't have the requested recipe" do
|
407
|
+
|
408
|
+
before do
|
409
|
+
allow(File).to receive(:exist?).with(provision_cookbook_path).and_return(true)
|
410
|
+
allow(File).to receive(:exist?).with(provision_recipe_path).and_return(false)
|
411
|
+
end
|
412
|
+
|
413
|
+
it "prints an error and exits non-zero" do
|
414
|
+
expect(command.run(params)).to eq(1)
|
415
|
+
expect(ui.output).to include("Provisioning recipe not found at path #{provision_recipe_path}")
|
416
|
+
end
|
417
|
+
|
418
|
+
end
|
419
|
+
|
420
|
+
context "when the policyfile upload fails" do
|
421
|
+
|
422
|
+
let(:backtrace) { caller[0...3] }
|
423
|
+
|
424
|
+
let(:cause) do
|
425
|
+
e = StandardError.new("some operation failed")
|
426
|
+
e.set_backtrace(backtrace)
|
427
|
+
e
|
428
|
+
end
|
429
|
+
|
430
|
+
let(:exception) do
|
431
|
+
ChefDK::PolicyfilePushError.new("push failed", cause)
|
432
|
+
end
|
433
|
+
|
434
|
+
before do
|
435
|
+
allow(File).to receive(:exist?).with(provision_cookbook_path).and_return(true)
|
436
|
+
allow(File).to receive(:exist?).with(provision_recipe_path).and_return(true)
|
437
|
+
|
438
|
+
expect(push_service).to receive(:run).and_raise(exception)
|
439
|
+
end
|
440
|
+
|
441
|
+
it "prints an error and exits non-zero" do
|
442
|
+
expected_output=<<-E
|
443
|
+
Error: push failed
|
444
|
+
Reason: (StandardError) some operation failed
|
445
|
+
|
446
|
+
E
|
447
|
+
expect(command.run(params)).to eq(1)
|
448
|
+
expect(ui.output).to include(expected_output)
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
|
453
|
+
context "when the chef run fails" do
|
454
|
+
|
455
|
+
let(:base_exception) { StandardError.new("Something went wrong") }
|
456
|
+
let(:exception) { ChefDK::ChefConvergeError.new("Chef failed to converge: #{base_exception}", base_exception) }
|
457
|
+
|
458
|
+
let(:policy_data) { { "name" => "myapp" } }
|
459
|
+
|
460
|
+
before do
|
461
|
+
allow(File).to receive(:exist?).with(provision_cookbook_path).and_return(true)
|
462
|
+
allow(File).to receive(:exist?).with(provision_recipe_path).and_return(true)
|
463
|
+
|
464
|
+
allow(push_service).to receive(:policy_data).and_return(policy_data)
|
465
|
+
|
466
|
+
expect(push_service).to receive(:run)
|
467
|
+
|
468
|
+
allow(command).to receive(:chef_runner).and_return(chef_runner)
|
469
|
+
allow(chef_runner).to receive(:cookbook_path).and_return(Dir.pwd)
|
470
|
+
expect(chef_runner).to receive(:converge).and_raise(exception)
|
471
|
+
end
|
472
|
+
|
473
|
+
it "prints an error and exits non-zero" do
|
474
|
+
expect(command.run(params)).to eq(1)
|
475
|
+
expect(ui.output).to include("Error: Chef failed to converge")
|
476
|
+
expect(ui.output).to include("Reason: (StandardError) Something went wrong")
|
477
|
+
end
|
478
|
+
|
479
|
+
end
|
480
|
+
|
481
|
+
context "when the chef run is successful" do
|
482
|
+
|
483
|
+
before do
|
484
|
+
allow(File).to receive(:exist?).with(provision_cookbook_path).and_return(true)
|
485
|
+
allow(File).to receive(:exist?).with(provision_recipe_path).and_return(true)
|
486
|
+
allow(command).to receive(:chef_runner).and_return(chef_runner)
|
487
|
+
allow(chef_runner).to receive(:cookbook_path).and_return(Dir.pwd)
|
488
|
+
|
489
|
+
expect(chef_runner).to receive(:converge)
|
490
|
+
end
|
491
|
+
|
492
|
+
context "when using --no-policy" do
|
493
|
+
|
494
|
+
let(:params) { %w[ --no-policy ] }
|
495
|
+
|
496
|
+
it "exits 0" do
|
497
|
+
return_value = command.run(params)
|
498
|
+
expect(ui.output).to eq("")
|
499
|
+
expect(return_value).to eq(0)
|
500
|
+
end
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
context "with --policy-name" do
|
505
|
+
|
506
|
+
let(:params) { %w[ policygroup --policy-name otherapp ] }
|
507
|
+
|
508
|
+
it "exits 0" do
|
509
|
+
return_value = command.run(params)
|
510
|
+
expect(ui.output).to eq("")
|
511
|
+
expect(return_value).to eq(0)
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
context "with --sync" do
|
516
|
+
|
517
|
+
let(:policy_data) { { "name" => "myapp" } }
|
518
|
+
|
519
|
+
before do
|
520
|
+
allow(push_service).to receive(:policy_data).and_return(policy_data)
|
521
|
+
expect(push_service).to receive(:run)
|
522
|
+
end
|
523
|
+
|
524
|
+
it "exits 0" do
|
525
|
+
return_value = command.run(params)
|
526
|
+
expect(ui.output).to eq("")
|
527
|
+
expect(return_value).to eq(0)
|
528
|
+
end
|
529
|
+
|
530
|
+
end
|
531
|
+
|
532
|
+
end
|
533
|
+
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|