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
@@ -0,0 +1,266 @@
|
|
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'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileServices::RmPolicy do
|
22
|
+
|
23
|
+
let(:policy_name) { "appserver" }
|
24
|
+
|
25
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
26
|
+
|
27
|
+
let(:policy_revisions_data) do
|
28
|
+
{
|
29
|
+
"revisions" => {
|
30
|
+
"2222222222222222222222222222222222222222222222222222222222222222" => {},
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:ui) { TestHelpers::TestUI.new }
|
36
|
+
|
37
|
+
let(:chef_config) do
|
38
|
+
double("Chef::Config",
|
39
|
+
chef_server_url: "https://localhost:10443",
|
40
|
+
client_key: "/path/to/client/key.pem",
|
41
|
+
node_name: "deuce")
|
42
|
+
end
|
43
|
+
|
44
|
+
subject(:rm_policy_service) do
|
45
|
+
described_class.new(policy_name: policy_name, ui: ui, config: chef_config)
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:undo_record) do
|
49
|
+
rm_policy_service.undo_record
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:undo_stack) do
|
53
|
+
rm_policy_service.undo_stack
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
it "configures an HTTP client" do
|
58
|
+
expect(ChefDK::AuthenticatedHTTP).to receive(:new).with("https://localhost:10443",
|
59
|
+
signing_key_filename: "/path/to/client/key.pem",
|
60
|
+
client_name: "deuce")
|
61
|
+
rm_policy_service.http_client
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the server returns an error fetching the policy data" do
|
65
|
+
|
66
|
+
let(:response) do
|
67
|
+
Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
|
68
|
+
r.instance_variable_set(:@body, "oops")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:http_exception) do
|
73
|
+
begin
|
74
|
+
response.error!
|
75
|
+
rescue => e
|
76
|
+
e
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
before do
|
81
|
+
allow(rm_policy_service).to receive(:http_client).and_return(http_client)
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "when getting an error fetching policy revisions" do
|
85
|
+
|
86
|
+
before do
|
87
|
+
expect(http_client).to receive(:get).with("/policies/appserver").and_return(policy_revisions_data)
|
88
|
+
expect(http_client).to receive(:get).
|
89
|
+
with("/policies/appserver/revisions/2222222222222222222222222222222222222222222222222222222222222222").
|
90
|
+
and_raise(http_exception)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "re-raises the error with a standardized exception class" do
|
94
|
+
expect { rm_policy_service.run }.to raise_error(ChefDK::DeletePolicyError)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
context "when the given policy doesn't exist" do
|
103
|
+
|
104
|
+
let(:policy_group) { "incorrect_policy_group_name" }
|
105
|
+
|
106
|
+
let(:response) do
|
107
|
+
Net::HTTPResponse.send(:response_class, "404").new("1.0", "404", "Not Found").tap do |r|
|
108
|
+
r.instance_variable_set(:@body, "not found")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
let(:http_exception) do
|
113
|
+
begin
|
114
|
+
response.error!
|
115
|
+
rescue => e
|
116
|
+
e
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
before do
|
121
|
+
allow(rm_policy_service).to receive(:http_client).and_return(http_client)
|
122
|
+
expect(http_client).to receive(:get).with("/policies/appserver").and_raise(http_exception)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "prints a message stating that the policy doesn't exist" do
|
126
|
+
expect { rm_policy_service.run }.to_not raise_error
|
127
|
+
expect(ui.output).to eq("Policy 'appserver' does not exist on the server\n")
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
# This case makes undo impossible to implement, because there are no APIs to
|
133
|
+
# create a policy name without creating a revision (i.e., there is no
|
134
|
+
# `POST /policies`). Because of the separation between CLI and service
|
135
|
+
# objects, running `chef delete-policy empty-policy` will still tell the user
|
136
|
+
# they can undo that action by running `chef undelete --last`, but that isn't
|
137
|
+
# true. That said, a policy with no revisions is invisible to `chef-client`
|
138
|
+
# and `chef show-policy`; the only way to create that state with the CLI is to
|
139
|
+
# `chef delete-policy-group` all the groups that policy was applied to and
|
140
|
+
# then run `chef clean-policy-revisions`, which can be undone by running
|
141
|
+
# `chef undelete` multiple times. So we'll test this scenario to make sure we
|
142
|
+
# don't crash, but not worry about the slightly incorrect behavior.
|
143
|
+
context "when the policy exists but has no revisions" do
|
144
|
+
|
145
|
+
let(:empty_policy_data) { {} }
|
146
|
+
|
147
|
+
before do
|
148
|
+
allow(rm_policy_service).to receive(:http_client).and_return(http_client)
|
149
|
+
expect(http_client).to receive(:get).with("/policies/appserver").and_return(empty_policy_data)
|
150
|
+
expect(http_client).to receive(:delete).with("/policies/appserver")
|
151
|
+
|
152
|
+
expect(undo_stack).to receive(:push).with(undo_record)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "removes the policy" do
|
156
|
+
rm_policy_service.run
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
context "when the policy has several revisions" do
|
162
|
+
|
163
|
+
let(:policy_appserver_2) do
|
164
|
+
{
|
165
|
+
"name" => "appserver",
|
166
|
+
"revision_id" => "2222222222222222222222222222222222222222222222222222222222222222"
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
let(:policy_group_data) do
|
171
|
+
{
|
172
|
+
"dev" => {
|
173
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/dev"
|
174
|
+
},
|
175
|
+
"preprod" => {
|
176
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/preprod"
|
177
|
+
}
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
before do
|
182
|
+
allow(rm_policy_service).to receive(:http_client).and_return(http_client)
|
183
|
+
|
184
|
+
expect(http_client).to receive(:get).with("/policies/appserver").and_return(policy_revisions_data)
|
185
|
+
expect(http_client).to receive(:get).with("/policy_groups").and_return(policy_group_data)
|
186
|
+
|
187
|
+
expect(http_client).to receive(:get).
|
188
|
+
with("/policies/appserver/revisions/2222222222222222222222222222222222222222222222222222222222222222").
|
189
|
+
and_return(policy_appserver_2)
|
190
|
+
expect(http_client).to receive(:delete).with("/policies/appserver")
|
191
|
+
|
192
|
+
expect(undo_stack).to receive(:push).with(undo_record)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "removes the policy" do
|
196
|
+
rm_policy_service.run
|
197
|
+
expect(ui.output).to include("Removed policy 'appserver'.")
|
198
|
+
end
|
199
|
+
|
200
|
+
it "stores the policy revisions in the restore file" do
|
201
|
+
rm_policy_service.run
|
202
|
+
|
203
|
+
expect(undo_record.description).to eq("delete-policy appserver")
|
204
|
+
expect(undo_record.policy_groups).to eq( [ ] )
|
205
|
+
expect(undo_record.policy_revisions.size).to eq(1)
|
206
|
+
stored_revision_info = undo_record.policy_revisions.first
|
207
|
+
expect(stored_revision_info.policy_group).to be_nil
|
208
|
+
expect(stored_revision_info.policy_name).to eq("appserver")
|
209
|
+
expect(stored_revision_info.data).to eq(policy_appserver_2)
|
210
|
+
end
|
211
|
+
|
212
|
+
context "and some policy revisions are associated to policy groups" do
|
213
|
+
|
214
|
+
let(:policy_group_data) do
|
215
|
+
{
|
216
|
+
"dev" => {
|
217
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/dev",
|
218
|
+
"policies" => {
|
219
|
+
"appserver" => { "revision_id" => "2222222222222222222222222222222222222222222222222222222222222222" },
|
220
|
+
"load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
|
221
|
+
"db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
|
222
|
+
}
|
223
|
+
},
|
224
|
+
"preprod" => {
|
225
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/preprod",
|
226
|
+
"policies" => {
|
227
|
+
"appserver" => { "revision_id" => "2222222222222222222222222222222222222222222222222222222222222222" },
|
228
|
+
"load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
|
229
|
+
"db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
|
230
|
+
}
|
231
|
+
},
|
232
|
+
"prod" => {
|
233
|
+
"uri" => "https://chef.example/organizations/testorg/policy_groups/prod",
|
234
|
+
"policies" => {
|
235
|
+
"appserver" => { "revision_id" => "1111111111111111111111111111111111111111111111111111111111111111" },
|
236
|
+
"load-balancer" => { "revision_id" => "5555555555555555555555555555555555555555555555555555555555555555" },
|
237
|
+
"db" => { "revision_id" => "9999999999999999999999999999999999999999999999999999999999999999" }
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
243
|
+
it "maps the policy revisions to their groups in the restore file" do
|
244
|
+
rm_policy_service.run
|
245
|
+
|
246
|
+
expect(undo_record.description).to eq("delete-policy appserver")
|
247
|
+
expect(undo_record.policy_groups).to eq( [ ] )
|
248
|
+
expect(undo_record.policy_revisions.size).to eq(2)
|
249
|
+
|
250
|
+
stored_revision_info_1 = undo_record.policy_revisions.first
|
251
|
+
expect(stored_revision_info_1.policy_group).to eq("dev")
|
252
|
+
expect(stored_revision_info_1.policy_name).to eq("appserver")
|
253
|
+
expect(stored_revision_info_1.data).to eq(policy_appserver_2)
|
254
|
+
|
255
|
+
stored_revision_info_2 = undo_record.policy_revisions.last
|
256
|
+
expect(stored_revision_info_2.policy_group).to eq("preprod")
|
257
|
+
expect(stored_revision_info_2.policy_name).to eq("appserver")
|
258
|
+
expect(stored_revision_info_2.data).to eq(policy_appserver_2)
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
@@ -499,7 +499,7 @@ OUTPUT
|
|
499
499
|
show_policy_service.run
|
500
500
|
end
|
501
501
|
|
502
|
-
context "when there are no
|
502
|
+
context "when there are no policies or groups on the server" do
|
503
503
|
|
504
504
|
let(:policies_by_name) do
|
505
505
|
{}
|
@@ -516,6 +516,56 @@ appserver
|
|
516
516
|
|
517
517
|
No policies named 'appserver' are associated with a policy group
|
518
518
|
|
519
|
+
OUTPUT
|
520
|
+
|
521
|
+
expect(ui.output).to eq(expected_output)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
context "when there are no revisions of the policy on the server" do
|
526
|
+
|
527
|
+
let(:policies_by_name) do
|
528
|
+
{
|
529
|
+
"load-balancer" => {
|
530
|
+
"5555555555555555555555555555555555555555555555555555555555555555" => {},
|
531
|
+
"6666666666666666666666666666666666666666666666666666666666666666" => {},
|
532
|
+
},
|
533
|
+
"db" => {
|
534
|
+
"9999999999999999999999999999999999999999999999999999999999999999" => {},
|
535
|
+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}
|
536
|
+
},
|
537
|
+
"memcache" => {
|
538
|
+
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" => {}
|
539
|
+
}
|
540
|
+
}
|
541
|
+
end
|
542
|
+
|
543
|
+
let(:policies_by_group) do
|
544
|
+
{
|
545
|
+
"dev" => {
|
546
|
+
"load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555",
|
547
|
+
"db" => "9999999999999999999999999999999999999999999999999999999999999999",
|
548
|
+
"memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
|
549
|
+
},
|
550
|
+
"staging" => {
|
551
|
+
"load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555",
|
552
|
+
"db" => "9999999999999999999999999999999999999999999999999999999999999999",
|
553
|
+
"memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
|
554
|
+
},
|
555
|
+
"prod" => {
|
556
|
+
"load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666",
|
557
|
+
"db" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
558
|
+
}
|
559
|
+
}
|
560
|
+
end
|
561
|
+
|
562
|
+
it "prints a message to stderr that there are no copies of the policy on the server" do
|
563
|
+
expected_output = <<-OUTPUT
|
564
|
+
appserver
|
565
|
+
=========
|
566
|
+
|
567
|
+
No policies named 'appserver' are associated with a policy group
|
568
|
+
|
519
569
|
OUTPUT
|
520
570
|
|
521
571
|
expect(ui.output).to eq(expected_output)
|
@@ -782,7 +832,7 @@ OUTPUT
|
|
782
832
|
end
|
783
833
|
|
784
834
|
before do
|
785
|
-
|
835
|
+
expect(http_client).to receive(:get).with("policy_groups/dev/policies/appserver").and_raise(http_exception)
|
786
836
|
end
|
787
837
|
|
788
838
|
it "prints a message saying there is no policy assigned" do
|
@@ -0,0 +1,304 @@
|
|
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/undelete'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileServices::Undelete do
|
22
|
+
|
23
|
+
let(:chef_config) { double("Chef::Config") }
|
24
|
+
|
25
|
+
let(:ui) { TestHelpers::TestUI.new }
|
26
|
+
|
27
|
+
let(:policy_name) { nil }
|
28
|
+
|
29
|
+
let(:policy_group) { nil }
|
30
|
+
|
31
|
+
let(:show_orphans) { false }
|
32
|
+
|
33
|
+
let(:summary_diff) { false }
|
34
|
+
|
35
|
+
let(:undo_dir) { tempdir }
|
36
|
+
|
37
|
+
let(:undo_record_id) { nil }
|
38
|
+
|
39
|
+
subject(:undelete_service) do
|
40
|
+
described_class.new(config: chef_config,
|
41
|
+
ui: ui,
|
42
|
+
undo_record_id: undo_record_id)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "listing undo operations" do
|
46
|
+
|
47
|
+
before do
|
48
|
+
allow(undelete_service.undo_stack).to receive(:undo_dir).and_return(undo_dir)
|
49
|
+
end
|
50
|
+
|
51
|
+
after do
|
52
|
+
clear_tempdir
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when the undo dir doesn't exist" do
|
56
|
+
|
57
|
+
let(:undo_dir) { File.join(tempdir, "this", "isnt", "here") }
|
58
|
+
|
59
|
+
it "prints a message saying there aren't any things to undo to stderr" do
|
60
|
+
undelete_service.list
|
61
|
+
expect(ui.output).to eq("Nothing to undo.\n")
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when the undo dir exists, but it empty" do
|
67
|
+
|
68
|
+
it "prints a message saying there aren't any things to undo to stderr" do
|
69
|
+
undelete_service.list
|
70
|
+
expect(ui.output).to eq("Nothing to undo.\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when the undo dir exists and there are undo records in it" do
|
76
|
+
|
77
|
+
let(:policy_revision) do
|
78
|
+
{
|
79
|
+
"name" => "appserver",
|
80
|
+
"revision_id" => "1111111111111111111111111111111111111111111111111111111111111111"
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
let(:undo_record1) do
|
85
|
+
ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
|
86
|
+
undo_record.description = "delete-policy-group example1"
|
87
|
+
undo_record.add_policy_group("example1")
|
88
|
+
undo_record.add_policy_revision("appserver", "example1", policy_revision)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:undo_record2) do
|
93
|
+
ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
|
94
|
+
undo_record.description = "delete-policy-group example2"
|
95
|
+
undo_record.add_policy_group("example2")
|
96
|
+
undo_record.add_policy_revision("appserver", "example2", policy_revision)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:undo_record3) do
|
101
|
+
ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
|
102
|
+
undo_record.description = "delete-policy-group example3"
|
103
|
+
undo_record.add_policy_group("example3")
|
104
|
+
undo_record.add_policy_revision("appserver", "example3", policy_revision)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# `Time.new` is stubbed later on, need to force it to be evaluated before
|
109
|
+
# then.
|
110
|
+
let!(:start_time) { Time.new }
|
111
|
+
|
112
|
+
def next_time
|
113
|
+
@increment ||= 0
|
114
|
+
@increment += 1
|
115
|
+
|
116
|
+
start_time + @increment
|
117
|
+
end
|
118
|
+
|
119
|
+
let(:times) { [] }
|
120
|
+
|
121
|
+
before do
|
122
|
+
allow(Time).to receive(:new) do
|
123
|
+
t = next_time
|
124
|
+
times << t
|
125
|
+
t
|
126
|
+
end
|
127
|
+
undo_stack = ChefDK::Policyfile::UndoStack.new
|
128
|
+
allow(undo_stack).to receive(:undo_dir).and_return(undo_dir)
|
129
|
+
undo_stack.push(undo_record1).push(undo_record2).push(undo_record3)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "prints the items in reverse chronological order" do
|
133
|
+
undelete_service.list
|
134
|
+
|
135
|
+
timestamps = times.map { |t| t.utc.strftime("%Y%m%d%H%M%S") }
|
136
|
+
|
137
|
+
expected_output = <<-OUTPUT
|
138
|
+
#{timestamps[2]}: delete-policy-group example3
|
139
|
+
#{timestamps[1]}: delete-policy-group example2
|
140
|
+
#{timestamps[0]}: delete-policy-group example1
|
141
|
+
OUTPUT
|
142
|
+
expect(ui.output).to eq(expected_output)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "undoing a policy group delete" do
|
150
|
+
|
151
|
+
let(:policy_revision) do
|
152
|
+
{
|
153
|
+
"name" => "appserver",
|
154
|
+
"revision_id" => "1111111111111111111111111111111111111111111111111111111111111111"
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
let(:undo_record1) do
|
159
|
+
ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
|
160
|
+
undo_record.description = "delete-policy-group example1"
|
161
|
+
undo_record.add_policy_group("example1")
|
162
|
+
undo_record.add_policy_revision("appserver", "example1", policy_revision)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
let(:undo_stack) do
|
167
|
+
instance_double(ChefDK::Policyfile::UndoStack).tap do |s|
|
168
|
+
allow(s).to receive(:pop).and_yield(undo_record1)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
173
|
+
|
174
|
+
before do
|
175
|
+
allow(undelete_service).to receive(:http_client).and_return(http_client)
|
176
|
+
allow(undelete_service).to receive(:undo_stack).and_return(undo_stack)
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "when an error occurs posting data to the server" do
|
180
|
+
|
181
|
+
let(:response) do
|
182
|
+
Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
|
183
|
+
r.instance_variable_set(:@body, "oops")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
let(:http_exception) do
|
188
|
+
begin
|
189
|
+
response.error!
|
190
|
+
rescue => e
|
191
|
+
e
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
before do
|
196
|
+
expect(http_client).to receive(:put).
|
197
|
+
with("/policy_groups/example1/policies/appserver", policy_revision).
|
198
|
+
and_raise(http_exception)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "raises an error" do
|
202
|
+
expect { undelete_service.run }.to raise_error(ChefDK::UndeleteError)
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
context "when the undelete is successful" do
|
208
|
+
|
209
|
+
before do
|
210
|
+
expect(http_client).to receive(:put).
|
211
|
+
with("/policy_groups/example1/policies/appserver", policy_revision)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "uploads all policies to the server" do
|
215
|
+
undelete_service.run
|
216
|
+
expect(ui.output).to eq("Restored policy 'appserver'\nRestored policy group 'example1'\n")
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
context "when given a specific undo record id to undo" do
|
222
|
+
|
223
|
+
let(:undo_record_id) { "20150827172127" }
|
224
|
+
|
225
|
+
context "and the id doesn't exist" do
|
226
|
+
|
227
|
+
before do
|
228
|
+
expect(undo_stack).to receive(:has_id?).with(undo_record_id).and_return(false)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "prints an error message that the id doesn't exist" do
|
232
|
+
undelete_service.run
|
233
|
+
expect(ui.output).to eq("No undo record with id '#{undo_record_id}' exists\n")
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
context "and the id exists" do
|
239
|
+
before do
|
240
|
+
expect(undo_stack).to receive(:has_id?).with(undo_record_id).and_return(true)
|
241
|
+
expect(undo_stack).to receive(:delete).with(undo_record_id).and_yield(undo_record1)
|
242
|
+
expect(http_client).to receive(:put).
|
243
|
+
with("/policy_groups/example1/policies/appserver", policy_revision)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "uploads all policies to the server" do
|
247
|
+
undelete_service.run
|
248
|
+
expect(ui.output).to eq("Restored policy 'appserver'\nRestored policy group 'example1'\n")
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "undoing a policy delete" do
|
258
|
+
|
259
|
+
let(:policy_revision) do
|
260
|
+
{
|
261
|
+
"name" => "appserver",
|
262
|
+
"revision_id" => "1111111111111111111111111111111111111111111111111111111111111111"
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
let(:undo_record1) do
|
267
|
+
ChefDK::Policyfile::UndoRecord.new.tap do |undo_record|
|
268
|
+
undo_record.description = "delete-policy-group example1"
|
269
|
+
undo_record.add_policy_revision("appserver", nil, policy_revision)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
let(:undo_stack) do
|
274
|
+
instance_double(ChefDK::Policyfile::UndoStack).tap do |s|
|
275
|
+
allow(s).to receive(:pop).and_yield(undo_record1)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
280
|
+
|
281
|
+
before do
|
282
|
+
allow(undelete_service).to receive(:http_client).and_return(http_client)
|
283
|
+
allow(undelete_service).to receive(:undo_stack).and_return(undo_stack)
|
284
|
+
end
|
285
|
+
|
286
|
+
context "when the revision to create doesn't exist" do
|
287
|
+
|
288
|
+
before do
|
289
|
+
expect(http_client).to receive(:post).
|
290
|
+
with("/policies/appserver/revisions", policy_revision)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "uploads all policies to the server" do
|
294
|
+
undelete_service.run
|
295
|
+
expect(ui.output).to eq("Restored policy 'appserver'\n")
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
end
|
304
|
+
|