chef-dk 0.7.0 → 0.8.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 +4 -4
- data/README.md +2 -2
- data/lib/chef-dk/builtin_commands.rb +10 -0
- data/lib/chef-dk/command/base.rb +2 -2
- data/lib/chef-dk/command/clean_policy_cookbooks.rb +116 -0
- data/lib/chef-dk/command/clean_policy_revisions.rb +113 -0
- data/lib/chef-dk/command/delete_policy.rb +122 -0
- data/lib/chef-dk/command/delete_policy_group.rb +122 -0
- data/lib/chef-dk/command/export.rb +3 -3
- data/lib/chef-dk/command/generate.rb +8 -0
- data/lib/chef-dk/command/generator_commands/app.rb +1 -1
- data/lib/chef-dk/command/generator_commands/cookbook.rb +1 -1
- data/lib/chef-dk/command/generator_commands/policyfile.rb +1 -1
- data/lib/chef-dk/command/generator_commands/repo.rb +1 -1
- data/lib/chef-dk/command/install.rb +22 -5
- data/lib/chef-dk/command/provision.rb +0 -4
- data/lib/chef-dk/command/push.rb +1 -2
- data/lib/chef-dk/command/shell_init.rb +65 -6
- data/lib/chef-dk/command/show_policy.rb +1 -2
- data/lib/chef-dk/command/undelete.rb +155 -0
- data/lib/chef-dk/command/update.rb +5 -5
- data/lib/chef-dk/command/verify.rb +61 -17
- data/lib/chef-dk/completions/bash.sh.erb +5 -0
- data/lib/chef-dk/completions/chef.fish.erb +10 -0
- data/lib/chef-dk/completions/zsh.zsh.erb +21 -0
- data/lib/chef-dk/exceptions.rb +12 -0
- data/lib/chef-dk/helpers.rb +17 -0
- data/lib/chef-dk/policyfile/community_cookbook_source.rb +0 -3
- data/lib/chef-dk/policyfile/lister.rb +3 -1
- data/lib/chef-dk/policyfile/undo_record.rb +142 -0
- data/lib/chef-dk/policyfile/undo_stack.rb +130 -0
- data/lib/chef-dk/policyfile_lock.rb +30 -0
- data/lib/chef-dk/policyfile_services/clean_policies.rb +5 -4
- data/lib/chef-dk/policyfile_services/clean_policy_cookbooks.rb +125 -0
- data/lib/chef-dk/policyfile_services/rm_policy.rb +142 -0
- data/lib/chef-dk/policyfile_services/rm_policy_group.rb +86 -0
- data/lib/chef-dk/policyfile_services/show_policy.rb +1 -1
- data/lib/chef-dk/policyfile_services/undelete.rb +108 -0
- data/lib/chef-dk/service_exceptions.rb +11 -0
- data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +6 -2
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +1 -1
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/attributes/default.rb +1 -1
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/recipes/default.rb +1 -1
- data/lib/chef-dk/version.rb +1 -1
- data/lib/kitchen/provisioner/policyfile_zero.rb +4 -1
- data/spec/unit/command/base_spec.rb +26 -1
- data/spec/unit/command/clean_policy_cookbooks_spec.rb +181 -0
- data/spec/unit/command/clean_policy_revisions_spec.rb +181 -0
- data/spec/unit/command/delete_policy_group_spec.rb +207 -0
- data/spec/unit/command/delete_policy_spec.rb +207 -0
- data/spec/unit/command/generate_spec.rb +41 -1
- data/spec/unit/command/generator_commands/cookbook_spec.rb +1 -1
- data/spec/unit/command/generator_commands/policyfile_spec.rb +1 -1
- data/spec/unit/command/install_spec.rb +24 -0
- data/spec/unit/command/shell_init_spec.rb +176 -5
- data/spec/unit/command/undelete_spec.rb +246 -0
- data/spec/unit/helpers_spec.rb +24 -0
- data/spec/unit/policyfile/lister_spec.rb +16 -0
- data/spec/unit/policyfile/undo_record_spec.rb +260 -0
- data/spec/unit/policyfile/undo_stack_spec.rb +266 -0
- data/spec/unit/policyfile_lock_serialization_spec.rb +41 -0
- data/spec/unit/policyfile_services/clean_policy_cookbooks_spec.rb +275 -0
- data/spec/unit/policyfile_services/rm_policy_group_spec.rb +241 -0
- data/spec/unit/policyfile_services/rm_policy_spec.rb +266 -0
- data/spec/unit/policyfile_services/show_policy_spec.rb +52 -2
- data/spec/unit/policyfile_services/undelete_spec.rb +304 -0
- metadata +43 -91
@@ -24,6 +24,9 @@ describe ChefDK::PolicyfileLock, "when reading a Policyfile.lock" do
|
|
24
24
|
{
|
25
25
|
"name" => "example",
|
26
26
|
"run_list" => [ "recipe[cookbook::recipe_name]" ],
|
27
|
+
"named_run_lists" => {
|
28
|
+
"fast-deploy" => [ "recipe[cookbook::deployit]" ]
|
29
|
+
},
|
27
30
|
"cookbook_locks" => {
|
28
31
|
# TODO: add some valid locks
|
29
32
|
},
|
@@ -50,6 +53,10 @@ describe ChefDK::PolicyfileLock, "when reading a Policyfile.lock" do
|
|
50
53
|
expect(lockfile.run_list).to eq(["recipe[cookbook::recipe_name]"])
|
51
54
|
end
|
52
55
|
|
56
|
+
it "includes the named run lists" do
|
57
|
+
expect(lockfile.named_run_lists).to eq({ "fast-deploy" => [ "recipe[cookbook::deployit]" ] })
|
58
|
+
end
|
59
|
+
|
53
60
|
it "includes the cookbook locks" do
|
54
61
|
expect(lockfile.cookbook_locks).to eq({})
|
55
62
|
end
|
@@ -100,6 +107,40 @@ describe ChefDK::PolicyfileLock, "when reading a Policyfile.lock" do
|
|
100
107
|
expect { lockfile.build_from_lock_data(bad_run_list) }.to raise_error(ChefDK::InvalidLockfile)
|
101
108
|
end
|
102
109
|
|
110
|
+
it "allows the named_run_lists field to be absent" do
|
111
|
+
missing_named_run_lists = valid_lock_data.dup
|
112
|
+
missing_named_run_lists.delete("named_run_lists")
|
113
|
+
|
114
|
+
expect { lockfile.build_from_lock_data(missing_named_run_lists) }.to_not raise_error
|
115
|
+
end
|
116
|
+
|
117
|
+
it "requires the named_run_lists field to be a Hash if present" do
|
118
|
+
bad_named_run_lists = valid_lock_data.dup
|
119
|
+
bad_named_run_lists["named_run_lists"] = false
|
120
|
+
|
121
|
+
expect { lockfile.build_from_lock_data(bad_named_run_lists) }.to raise_error(ChefDK::InvalidLockfile)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "requires the keys in named_run_lists to be strings" do
|
125
|
+
bad_named_run_lists = valid_lock_data.dup
|
126
|
+
bad_named_run_lists["named_run_lists"] = { 42 => [] }
|
127
|
+
|
128
|
+
expect { lockfile.build_from_lock_data(bad_named_run_lists) }.to raise_error(ChefDK::InvalidLockfile)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "requires the values in named_run_lists to be arrays" do
|
132
|
+
bad_named_run_lists = valid_lock_data.dup
|
133
|
+
bad_named_run_lists["named_run_lists"] = { "bad" => 42 }
|
134
|
+
|
135
|
+
expect { lockfile.build_from_lock_data(bad_named_run_lists) }.to raise_error(ChefDK::InvalidLockfile)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "requires the values in named_run_lists to be valid run lists" do
|
139
|
+
bad_named_run_lists = valid_lock_data.dup
|
140
|
+
bad_named_run_lists["named_run_lists"] = { "bad" => [ 42 ] }
|
141
|
+
|
142
|
+
expect { lockfile.build_from_lock_data(bad_named_run_lists) }.to raise_error(ChefDK::InvalidLockfile)
|
143
|
+
end
|
103
144
|
it "requires the `cookbook_locks` section be present and its value is a Hash" do
|
104
145
|
missing_locks = valid_lock_data.dup
|
105
146
|
missing_locks.delete("cookbook_locks")
|
@@ -0,0 +1,275 @@
|
|
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 'chef-dk/policyfile_services/clean_policy_cookbooks'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileServices::CleanPolicyCookbooks do
|
22
|
+
|
23
|
+
let(:cookbook_artifacts_list) do
|
24
|
+
{
|
25
|
+
"mysql" => {
|
26
|
+
"versions" => [
|
27
|
+
{
|
28
|
+
"identifier" => "6b506252cae939c874bd59b560c339b01c31326b"
|
29
|
+
}
|
30
|
+
]
|
31
|
+
},
|
32
|
+
"build-essential" => {
|
33
|
+
"versions" => [
|
34
|
+
{
|
35
|
+
"identifier" => "2db3df121028894f45497f847de91b91fbf76824"
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"identifier" => "d8ce58401d154378599b0fead81d2c390615602b"
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:cookbook_ids_by_name) do
|
46
|
+
{
|
47
|
+
"mysql" => [ "6b506252cae939c874bd59b560c339b01c31326b" ],
|
48
|
+
"build-essential" => [ "2db3df121028894f45497f847de91b91fbf76824", "d8ce58401d154378599b0fead81d2c390615602b" ]
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:cookbook_ids_in_sets_by_name) do
|
53
|
+
cookbook_ids_by_name.inject({}) do |map, (name, id_list)|
|
54
|
+
map[name] = Set.new(id_list)
|
55
|
+
map
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
let(:policies_list) do
|
60
|
+
{
|
61
|
+
"aar" => {
|
62
|
+
"revisions" => {
|
63
|
+
"37f9b658cdd1d9319bac8920581723efcc2014304b5f3827ee0779e10ffbdcc9" => { }
|
64
|
+
}
|
65
|
+
},
|
66
|
+
"jenkins" => {
|
67
|
+
"revisions" => {
|
68
|
+
"613f803bdd035d574df7fa6da525b38df45a74ca82b38b79655efed8a189e073" => { },
|
69
|
+
"6fe753184c8946052d3231bb4212116df28d89a3a5f7ae52832ad408419dd5eb" => { }
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
76
|
+
|
77
|
+
let(:ui) { TestHelpers::TestUI.new }
|
78
|
+
|
79
|
+
let(:chef_config) do
|
80
|
+
double("Chef::Config",
|
81
|
+
chef_server_url: "https://localhost:10443",
|
82
|
+
client_key: "/path/to/client/key.pem",
|
83
|
+
node_name: "deuce")
|
84
|
+
end
|
85
|
+
|
86
|
+
subject(:clean_policy_cookbooks_service) do
|
87
|
+
described_class.new(ui: ui, config: chef_config)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
it "configures an HTTP client with the user's credentials" do
|
92
|
+
expect(ChefDK::AuthenticatedHTTP).to receive(:new).with("https://localhost:10443",
|
93
|
+
signing_key_filename: "/path/to/client/key.pem",
|
94
|
+
client_name: "deuce")
|
95
|
+
clean_policy_cookbooks_service.http_client
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when an error occurs fetching cookbook data from the server" do
|
99
|
+
|
100
|
+
let(:response) do
|
101
|
+
Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
|
102
|
+
r.instance_variable_set(:@body, "oops")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
let(:http_exception) do
|
107
|
+
begin
|
108
|
+
response.error!
|
109
|
+
rescue => e
|
110
|
+
e
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
before do
|
115
|
+
allow(clean_policy_cookbooks_service).to receive(:http_client).and_return(http_client)
|
116
|
+
expect(http_client).to receive(:get).with("/policies").and_return({})
|
117
|
+
expect(http_client).to receive(:get).with("/cookbook_artifacts").and_raise(http_exception)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "raises a standardized nested exception" do
|
121
|
+
expect { clean_policy_cookbooks_service.run }.to raise_error(ChefDK::PolicyCookbookCleanError)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when the server returns cookbook data successfully" do
|
127
|
+
|
128
|
+
before do
|
129
|
+
allow(clean_policy_cookbooks_service).to receive(:http_client).and_return(http_client)
|
130
|
+
|
131
|
+
allow(http_client).to receive(:get).with("/cookbook_artifacts").and_return(cookbook_artifacts_list)
|
132
|
+
allow(http_client).to receive(:get).with("/policies").and_return(policies_list)
|
133
|
+
end
|
134
|
+
|
135
|
+
context "when the server has no policy cookbooks" do
|
136
|
+
|
137
|
+
let(:cookbook_artifacts_list) { {} }
|
138
|
+
let(:policies_list) { {} }
|
139
|
+
|
140
|
+
it "has an empty list for all cookbooks" do
|
141
|
+
expect(clean_policy_cookbooks_service.all_cookbooks).to eq({})
|
142
|
+
end
|
143
|
+
|
144
|
+
it "has no in-use cookbook artifacts" do
|
145
|
+
expect(clean_policy_cookbooks_service.active_cookbooks).to eq({})
|
146
|
+
end
|
147
|
+
|
148
|
+
it "has no cookbooks to clean" do
|
149
|
+
expect(clean_policy_cookbooks_service.cookbooks_to_clean).to eq({})
|
150
|
+
end
|
151
|
+
|
152
|
+
it "does not clean any cookbooks" do
|
153
|
+
expect(http_client).to_not receive(:delete)
|
154
|
+
clean_policy_cookbooks_service.run
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when the server has policy cookbooks" do
|
159
|
+
|
160
|
+
let(:policy_aar_37f9b65) do
|
161
|
+
{
|
162
|
+
"cookbook_locks" => {
|
163
|
+
"mysql" => { "identifier" => "6b506252cae939c874bd59b560c339b01c31326b" }
|
164
|
+
}
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
let(:policy_jenkins_613f803) do
|
169
|
+
{
|
170
|
+
"cookbook_locks" => {
|
171
|
+
"mysql" => { "identifier" => "6b506252cae939c874bd59b560c339b01c31326b" },
|
172
|
+
"build-essential" => { "identifier" => "2db3df121028894f45497f847de91b91fbf76824" }
|
173
|
+
}
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
let(:policy_jenkins_6fe7531) do
|
178
|
+
{
|
179
|
+
"cookbook_locks" => {
|
180
|
+
"mysql" => { "identifier" => "6b506252cae939c874bd59b560c339b01c31326b" },
|
181
|
+
"build-essential" => { "identifier" => "d8ce58401d154378599b0fead81d2c390615602b" }
|
182
|
+
}
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
before do
|
187
|
+
allow(http_client).to receive(:get).
|
188
|
+
with("/policies/aar/revisions/37f9b658cdd1d9319bac8920581723efcc2014304b5f3827ee0779e10ffbdcc9").
|
189
|
+
and_return(policy_aar_37f9b65)
|
190
|
+
allow(http_client).to receive(:get).
|
191
|
+
with("/policies/jenkins/revisions/613f803bdd035d574df7fa6da525b38df45a74ca82b38b79655efed8a189e073").
|
192
|
+
and_return(policy_jenkins_613f803)
|
193
|
+
allow(http_client).to receive(:get).
|
194
|
+
with("/policies/jenkins/revisions/6fe753184c8946052d3231bb4212116df28d89a3a5f7ae52832ad408419dd5eb").
|
195
|
+
and_return(policy_jenkins_6fe7531)
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
context "and all cookbooks are active" do
|
200
|
+
|
201
|
+
it "lists all the cookbooks" do
|
202
|
+
expect(clean_policy_cookbooks_service.all_cookbooks).to eq(cookbook_ids_by_name)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "lists all active cookbooks" do
|
206
|
+
expect(clean_policy_cookbooks_service.active_cookbooks).to eq(cookbook_ids_in_sets_by_name)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "has no cookbooks to clean" do
|
210
|
+
expect(clean_policy_cookbooks_service.cookbooks_to_clean).to eq({})
|
211
|
+
end
|
212
|
+
|
213
|
+
it "does not clean any cookbooks" do
|
214
|
+
expect(http_client).to_not receive(:delete)
|
215
|
+
clean_policy_cookbooks_service.run
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "and some cookbooks can be GC'd" do
|
220
|
+
|
221
|
+
let(:policy_jenkins_6fe7531) do
|
222
|
+
{
|
223
|
+
"cookbook_locks" => {
|
224
|
+
"mysql" => { "identifier" => "6b506252cae939c874bd59b560c339b01c31326b" },
|
225
|
+
# this is changed to reference the same cookbook as policy_jenkins_613f803
|
226
|
+
"build-essential" => { "identifier" => "2db3df121028894f45497f847de91b91fbf76824" }
|
227
|
+
}
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
let(:expected_active_cookbooks) do
|
232
|
+
{
|
233
|
+
"mysql" => Set.new([ "6b506252cae939c874bd59b560c339b01c31326b" ]),
|
234
|
+
"build-essential" => Set.new([ "2db3df121028894f45497f847de91b91fbf76824" ])
|
235
|
+
}
|
236
|
+
end
|
237
|
+
|
238
|
+
it "lists all the cookbooks" do
|
239
|
+
expect(clean_policy_cookbooks_service.all_cookbooks).to eq(cookbook_ids_by_name)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "lists all active cookbooks" do
|
243
|
+
expect(clean_policy_cookbooks_service.active_cookbooks).to eq(expected_active_cookbooks)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "lists non-active cookbooks" do
|
247
|
+
expected = { "build-essential" => Set.new([ "d8ce58401d154378599b0fead81d2c390615602b" ]) }
|
248
|
+
expect(clean_policy_cookbooks_service.cookbooks_to_clean).to eq(expected)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "deletes the non-active cookbooks" do
|
252
|
+
expect(http_client).to receive(:delete).with("/cookbook_artifacts/build-essential/d8ce58401d154378599b0fead81d2c390615602b")
|
253
|
+
clean_policy_cookbooks_service.run
|
254
|
+
end
|
255
|
+
|
256
|
+
# Regression test. This was giving us an Argument error for `<Set> - nil`
|
257
|
+
context "when there are no active revisions of a given cookbook" do
|
258
|
+
|
259
|
+
let(:policies_list) { {} }
|
260
|
+
|
261
|
+
it "deletes the non-active cookbooks" do
|
262
|
+
expect(http_client).to receive(:delete).with("/cookbook_artifacts/build-essential/d8ce58401d154378599b0fead81d2c390615602b")
|
263
|
+
expect(http_client).to receive(:delete).with("/cookbook_artifacts/build-essential/2db3df121028894f45497f847de91b91fbf76824")
|
264
|
+
expect(http_client).to receive(:delete).with("/cookbook_artifacts/mysql/6b506252cae939c874bd59b560c339b01c31326b")
|
265
|
+
clean_policy_cookbooks_service.run
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
@@ -0,0 +1,241 @@
|
|
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 'chef-dk/policyfile_services/rm_policy_group'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileServices::RmPolicyGroup do
|
22
|
+
|
23
|
+
let(:policy_group) { "preprod" }
|
24
|
+
|
25
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
26
|
+
|
27
|
+
let(:ui) { TestHelpers::TestUI.new }
|
28
|
+
|
29
|
+
let(:non_empty_policy_groups) do
|
30
|
+
{
|
31
|
+
"dev" => {
|
32
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/dev",
|
33
|
+
"policies" => {
|
34
|
+
"appserver" => { "revision_id" => "1111111111111111111111111111111111111111111111111111111111111111" },
|
35
|
+
"load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
|
36
|
+
"db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
|
37
|
+
}
|
38
|
+
},
|
39
|
+
"preprod" => {
|
40
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/preprod",
|
41
|
+
"policies" => {
|
42
|
+
"appserver" => { "revision_id" => "2222222222222222222222222222222222222222222222222222222222222222" },
|
43
|
+
"load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
|
44
|
+
"db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
let(:empty_policy_groups) do
|
52
|
+
{
|
53
|
+
"dev" => {
|
54
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/dev"
|
55
|
+
},
|
56
|
+
"preprod" => {
|
57
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/preprod"
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:chef_config) do
|
63
|
+
double("Chef::Config",
|
64
|
+
chef_server_url: "https://localhost:10443",
|
65
|
+
client_key: "/path/to/client/key.pem",
|
66
|
+
node_name: "deuce")
|
67
|
+
end
|
68
|
+
|
69
|
+
subject(:rm_policy_group_service) do
|
70
|
+
described_class.new(policy_group: policy_group, ui: ui, config: chef_config)
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:undo_record) do
|
74
|
+
rm_policy_group_service.undo_record
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:undo_stack) do
|
78
|
+
rm_policy_group_service.undo_stack
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
it "configures an HTTP client" do
|
83
|
+
expect(ChefDK::AuthenticatedHTTP).to receive(:new).with("https://localhost:10443",
|
84
|
+
signing_key_filename: "/path/to/client/key.pem",
|
85
|
+
client_name: "deuce")
|
86
|
+
rm_policy_group_service.http_client
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when the server returns an error fetching the policy data" do
|
90
|
+
|
91
|
+
let(:response) do
|
92
|
+
Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
|
93
|
+
r.instance_variable_set(:@body, "oops")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
let(:http_exception) do
|
98
|
+
begin
|
99
|
+
response.error!
|
100
|
+
rescue => e
|
101
|
+
e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
before do
|
106
|
+
allow(rm_policy_group_service).to receive(:http_client).and_return(http_client)
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "when getting an error response fetching the policy group" do
|
110
|
+
|
111
|
+
before do
|
112
|
+
expect(http_client).to receive(:get).with("/policy_groups").and_raise(http_exception)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "re-raises the error with a standardized exception class" do
|
116
|
+
expect { rm_policy_group_service.run }.to raise_error(ChefDK::DeletePolicyGroupError)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "when getting an error fetching policy revisions" do
|
122
|
+
|
123
|
+
before do
|
124
|
+
expect(http_client).to receive(:get).with("/policy_groups").and_return(non_empty_policy_groups)
|
125
|
+
expect(http_client).to receive(:get).
|
126
|
+
with("/policies/appserver/revisions/2222222222222222222222222222222222222222222222222222222222222222").
|
127
|
+
and_raise(http_exception)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "re-raises the error with a standardized exception class" do
|
131
|
+
expect { rm_policy_group_service.run }.to raise_error(ChefDK::DeletePolicyGroupError)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when the given group doesn't exist" do
|
140
|
+
|
141
|
+
let(:policy_group) { "incorrect_policy_group_name" }
|
142
|
+
|
143
|
+
before do
|
144
|
+
allow(rm_policy_group_service).to receive(:http_client).and_return(http_client)
|
145
|
+
expect(http_client).to receive(:get).with("/policy_groups").and_return(non_empty_policy_groups)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "prints a message stating that the group doesn't exist" do
|
149
|
+
expect { rm_policy_group_service.run }.to_not raise_error
|
150
|
+
expect(ui.output).to eq("Policy group 'incorrect_policy_group_name' does not exist on the server\n")
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
context "when the group exists but has no policies assigned to it" do
|
156
|
+
|
157
|
+
before do
|
158
|
+
allow(rm_policy_group_service).to receive(:http_client).and_return(http_client)
|
159
|
+
expect(http_client).to receive(:get).with("/policy_groups").and_return(empty_policy_groups)
|
160
|
+
expect(http_client).to receive(:delete).with("/policy_groups/preprod")
|
161
|
+
expect(undo_stack).to receive(:push).with(undo_record)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "removes the group" do
|
165
|
+
rm_policy_group_service.run
|
166
|
+
expect(ui.output).to include("Removed policy group 'preprod'.")
|
167
|
+
end
|
168
|
+
|
169
|
+
it "stores the group in the restore file" do
|
170
|
+
rm_policy_group_service.run
|
171
|
+
expect(undo_record.description).to eq("delete-policy-group preprod")
|
172
|
+
expect(undo_record.policy_groups).to eq( [ policy_group ] )
|
173
|
+
expect(undo_record.policy_revisions).to be_empty
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when the group exists and has policies assigned to it" do
|
179
|
+
|
180
|
+
let(:policy_appserver_2) do
|
181
|
+
{
|
182
|
+
"name" => "appserver",
|
183
|
+
"revision_id" => "2222222222222222222222222222222222222222222222222222222222222222"
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
let(:policy_load_balancer_5) do
|
188
|
+
{
|
189
|
+
"name" => "load-balancer",
|
190
|
+
"revision_id" => "5555555555555555555555555555555555555555555555555555555555555555"
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
let(:policy_db_9) do
|
195
|
+
{
|
196
|
+
"name" => "db",
|
197
|
+
"revision_id" => "9999999999999999999999999999999999999999999999999999999999999999"
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
before do
|
202
|
+
allow(rm_policy_group_service).to receive(:http_client).and_return(http_client)
|
203
|
+
expect(http_client).to receive(:get).with("/policy_groups").and_return(non_empty_policy_groups)
|
204
|
+
expect(http_client).to receive(:get).
|
205
|
+
with("/policies/appserver/revisions/2222222222222222222222222222222222222222222222222222222222222222").
|
206
|
+
and_return(policy_appserver_2)
|
207
|
+
expect(http_client).to receive(:get).
|
208
|
+
with("/policies/load-balancer/revisions/5555555555555555555555555555555555555555555555555555555555555555").
|
209
|
+
and_return(policy_load_balancer_5)
|
210
|
+
expect(http_client).to receive(:get).
|
211
|
+
with("/policies/db/revisions/9999999999999999999999999999999999999999999999999999999999999999").
|
212
|
+
and_return(policy_db_9)
|
213
|
+
|
214
|
+
expect(http_client).to receive(:delete).with("/policy_groups/preprod")
|
215
|
+
expect(undo_stack).to receive(:push).with(undo_record)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "removes the group" do
|
219
|
+
rm_policy_group_service.run
|
220
|
+
expect(ui.output).to include("Removed policy group 'preprod'.")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "stores the group and policyfile revision contents in the restore file" do
|
224
|
+
rm_policy_group_service.run
|
225
|
+
expect(undo_record.description).to eq("delete-policy-group preprod")
|
226
|
+
expect(undo_record.policy_groups).to eq( [ policy_group ] )
|
227
|
+
|
228
|
+
expected_policy_revision_undo_data =
|
229
|
+
[
|
230
|
+
{ policy_name: "appserver", policy_group: "preprod", data: policy_appserver_2},
|
231
|
+
{ policy_name: "load-balancer", policy_group: "preprod", data: policy_load_balancer_5},
|
232
|
+
{ policy_name: "db", policy_group: "preprod", data: policy_db_9}
|
233
|
+
]
|
234
|
+
|
235
|
+
expect(undo_record.policy_revisions.map(&:to_h)).to match_array(expected_policy_revision_undo_data)
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|