chef-dk 0.6.2 → 0.7.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 +4 -0
- data/lib/chef-dk/builtin_commands.rb +7 -0
- data/lib/chef-dk/command/env.rb +90 -0
- data/lib/chef-dk/command/export.rb +22 -1
- data/lib/chef-dk/command/generate.rb +1 -1
- data/lib/chef-dk/command/provision.rb +43 -0
- data/lib/chef-dk/command/push_archive.rb +126 -0
- data/lib/chef-dk/command/show_policy.rb +166 -0
- data/lib/chef-dk/command/verify.rb +58 -1
- data/lib/chef-dk/cookbook_omnifetch.rb +3 -2
- data/lib/chef-dk/exceptions.rb +27 -0
- data/lib/chef-dk/helpers.rb +29 -0
- data/lib/chef-dk/policyfile/chef_repo_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile/chef_server_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile/community_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile/cookbook_locks.rb +76 -6
- data/lib/chef-dk/policyfile/dsl.rb +10 -5
- data/lib/chef-dk/policyfile/lister.rb +230 -0
- data/lib/chef-dk/policyfile/null_cookbook_source.rb +8 -0
- data/lib/chef-dk/policyfile_compiler.rb +35 -2
- data/lib/chef-dk/policyfile_lock.rb +43 -0
- data/lib/chef-dk/policyfile_services/clean_policies.rb +94 -0
- data/lib/chef-dk/policyfile_services/export_repo.rb +103 -16
- data/lib/chef-dk/policyfile_services/push_archive.rb +173 -0
- data/lib/chef-dk/policyfile_services/show_policy.rb +237 -0
- data/lib/chef-dk/service_exceptions.rb +21 -0
- data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +1 -0
- data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +2 -40
- data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +0 -2
- data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen.yml.erb +2 -2
- data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +1 -1
- data/lib/chef-dk/version.rb +1 -1
- data/spec/unit/command/env_spec.rb +52 -0
- data/spec/unit/command/exec_spec.rb +2 -2
- data/spec/unit/command/export_spec.rb +13 -0
- data/spec/unit/command/provision_spec.rb +56 -0
- data/spec/unit/command/push_archive_spec.rb +153 -0
- data/spec/unit/command/show_policy_spec.rb +235 -0
- data/spec/unit/command/verify_spec.rb +1 -0
- data/spec/unit/helpers_spec.rb +68 -0
- data/spec/unit/policyfile/cookbook_locks_spec.rb +107 -1
- data/spec/unit/policyfile/lister_spec.rb +256 -0
- data/spec/unit/policyfile_demands_spec.rb +202 -10
- data/spec/unit/policyfile_evaluation_spec.rb +30 -4
- data/spec/unit/policyfile_lock_serialization_spec.rb +45 -0
- data/spec/unit/policyfile_services/clean_policies_spec.rb +236 -0
- data/spec/unit/policyfile_services/export_repo_spec.rb +99 -6
- data/spec/unit/policyfile_services/push_archive_spec.rb +345 -0
- data/spec/unit/policyfile_services/show_policy_spec.rb +839 -0
- metadata +139 -8
@@ -32,7 +32,7 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
|
|
32
32
|
p.default_source(*default_source) if default_source
|
33
33
|
p.run_list(*run_list)
|
34
34
|
|
35
|
-
allow(p.default_source).to receive(:universe_graph).and_return(external_cookbook_universe)
|
35
|
+
allow(p.default_source.first).to receive(:universe_graph).and_return(external_cookbook_universe)
|
36
36
|
end
|
37
37
|
|
38
38
|
policyfile
|
@@ -71,6 +71,11 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
|
|
71
71
|
"1.1.1" => [ ]
|
72
72
|
},
|
73
73
|
|
74
|
+
"remote-cb-two" => {
|
75
|
+
"0.1.0" => [ ],
|
76
|
+
"1.1.1" => [ ]
|
77
|
+
},
|
78
|
+
|
74
79
|
"local-cookbook-dep-one" => {
|
75
80
|
"1.5.0" => [ ]
|
76
81
|
},
|
@@ -207,6 +212,26 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
|
|
207
212
|
|
208
213
|
let(:run_list) { ["remote-cb"] }
|
209
214
|
|
215
|
+
context "with no default source" do
|
216
|
+
|
217
|
+
it "fails to locate the cookbook" do
|
218
|
+
expect { policyfile.graph_solution }.to raise_error(Solve::Errors::NoSolutionError)
|
219
|
+
end
|
220
|
+
|
221
|
+
context "when the policyfile also has a `cookbook` entry for the run list item" do
|
222
|
+
|
223
|
+
before do
|
224
|
+
policyfile.dsl.cookbook "remote-cb"
|
225
|
+
end
|
226
|
+
|
227
|
+
it "fails to locate the cookbook" do
|
228
|
+
expect { policyfile.graph_solution }.to raise_error(Solve::Errors::NoSolutionError)
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
210
235
|
context "And the default source is the community site" do
|
211
236
|
|
212
237
|
include_context "community default source"
|
@@ -632,12 +657,6 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
|
|
632
657
|
expect(policyfile.graph_solution).to eq({"local-cookbook" => "2.3.4", "remote-cb" => "0.1.0"})
|
633
658
|
end
|
634
659
|
|
635
|
-
it "builds a policyfile lock from the constraints" do
|
636
|
-
skip
|
637
|
-
expect(policyfile).to receive(:cache_path).and_return(Pathname.new("~/.nopenope/cache"))
|
638
|
-
expect(policyfile.lock).to eq(:wat)
|
639
|
-
end
|
640
|
-
|
641
660
|
it "includes the policyfile constraint in the solution dependencies" do
|
642
661
|
expected_solution_deps = {
|
643
662
|
"Policyfile" => [ [ "remote-cb", "~> 0.1" ], [ "local-cookbook", ">= 0.0.0"] ],
|
@@ -675,10 +694,183 @@ describe ChefDK::PolicyfileCompiler, "when expressing the Policyfile graph deman
|
|
675
694
|
|
676
695
|
end
|
677
696
|
|
678
|
-
context "
|
679
|
-
|
680
|
-
|
697
|
+
context "when using multiple default sources" do
|
698
|
+
|
699
|
+
include_context "community default source"
|
700
|
+
|
701
|
+
let(:run_list) { [ 'repo-cookbook-one', 'remote-cb', 'remote-cb-two' ] }
|
702
|
+
|
703
|
+
before do
|
704
|
+
policyfile.default_source(:chef_repo, "path/to/repo")
|
705
|
+
allow(policyfile.default_source.last).to receive(:universe_graph).and_return(repo_cookbook_universe)
|
681
706
|
end
|
707
|
+
|
708
|
+
context "when the graphs don't conflict" do
|
709
|
+
|
710
|
+
before do
|
711
|
+
# This is on the community site
|
712
|
+
policyfile.dsl.cookbook("remote-cb")
|
713
|
+
end
|
714
|
+
|
715
|
+
let(:repo_cookbook_universe) do
|
716
|
+
{
|
717
|
+
"repo-cookbook-one" => {
|
718
|
+
"1.0.0" => [ ]
|
719
|
+
},
|
720
|
+
|
721
|
+
"repo-cookbook-two" => {
|
722
|
+
"9.9.9" => [ ["repo-cookbook-on-community-dep", "= 1.0.0"] ]
|
723
|
+
},
|
724
|
+
|
725
|
+
"private-cookbook" => {
|
726
|
+
"0.1.0" => [ ]
|
727
|
+
}
|
728
|
+
}
|
729
|
+
end
|
730
|
+
|
731
|
+
it "merges the graphs" do
|
732
|
+
merged = policyfile.remote_artifacts_graph
|
733
|
+
expected = external_cookbook_universe.merge(repo_cookbook_universe)
|
734
|
+
|
735
|
+
expect(merged).to eq(expected)
|
736
|
+
end
|
737
|
+
|
738
|
+
it "solves the graph demands using cookbooks from both sources" do
|
739
|
+
expected = {"repo-cookbook-one" => "1.0.0", "remote-cb" => "1.1.1", "remote-cb-two" => "1.1.1"}
|
740
|
+
expect(policyfile.graph_solution).to eq(expected)
|
741
|
+
end
|
742
|
+
|
743
|
+
it "finds the location of a cookbook declared via explicit `cookbook` with no source options" do
|
744
|
+
community_source = policyfile.default_source.first
|
745
|
+
|
746
|
+
expected_source_options = { artifactserver: "https://chef.example/url", version: "1.1.1" }
|
747
|
+
|
748
|
+
expect(community_source).to be_a(ChefDK::Policyfile::CommunityCookbookSource)
|
749
|
+
expect(community_source).to receive(:source_options_for).
|
750
|
+
with("remote-cb", "1.1.1").
|
751
|
+
and_return(expected_source_options)
|
752
|
+
|
753
|
+
location_spec = policyfile.create_spec_for_cookbook("remote-cb", "1.1.1")
|
754
|
+
expect(location_spec.source_options).to eq(expected_source_options)
|
755
|
+
end
|
756
|
+
|
757
|
+
it "sources cookbooks from the correct source when the cookbook doesn't have a `cookbook` entry" do
|
758
|
+
# these don't have `cookbook` entries in the Policyfile.rb, so they are nil
|
759
|
+
expect(policyfile.cookbook_location_spec_for("repo-cookbook-one")).to be_nil
|
760
|
+
expect(policyfile.cookbook_location_spec_for("remote-cb-two")).to be_nil
|
761
|
+
|
762
|
+
# We have to stub #source_options_for or else we'd need to stub the
|
763
|
+
# source options data inside the source object. That's getting a bit
|
764
|
+
# too deep into the source object's internals.
|
765
|
+
|
766
|
+
expected_repo_options = { path: "path/to/cookbook", version: "1.0.0" }
|
767
|
+
repo_source = policyfile.default_source.last
|
768
|
+
expect(repo_source).to be_a(ChefDK::Policyfile::ChefRepoCookbookSource)
|
769
|
+
expect(repo_source).to receive(:source_options_for).
|
770
|
+
with("repo-cookbook-one", "1.0.0").
|
771
|
+
and_return(expected_repo_options)
|
772
|
+
|
773
|
+
|
774
|
+
repo_cb_location = policyfile.create_spec_for_cookbook("repo-cookbook-one", "1.0.0")
|
775
|
+
expect(repo_cb_location.source_options).to eq(expected_repo_options)
|
776
|
+
|
777
|
+
expected_server_options = { artifactserver: "https://chef.example/url", version: "1.1.1" }
|
778
|
+
community_source = policyfile.default_source.first
|
779
|
+
expect(community_source).to be_a(ChefDK::Policyfile::CommunityCookbookSource)
|
780
|
+
expect(community_source).to receive(:source_options_for).
|
781
|
+
with("remote-cb-two", "1.1.1").
|
782
|
+
and_return(expected_server_options)
|
783
|
+
|
784
|
+
remote_cb_location = policyfile.create_spec_for_cookbook("remote-cb-two", "1.1.1")
|
785
|
+
expect(remote_cb_location.source_options).to eq(expected_server_options)
|
786
|
+
end
|
787
|
+
|
788
|
+
end
|
789
|
+
|
790
|
+
context "when the graphs conflict" do
|
791
|
+
|
792
|
+
let(:repo_cookbook_universe) do
|
793
|
+
{
|
794
|
+
"repo-cookbook-one" => {
|
795
|
+
"1.0.0" => [ ]
|
796
|
+
},
|
797
|
+
|
798
|
+
"repo-cookbook-two" => {
|
799
|
+
"9.9.9" => [ ["repo-cookbook-on-community-dep", "= 1.0.0"] ]
|
800
|
+
},
|
801
|
+
|
802
|
+
"private-cookbook" => {
|
803
|
+
"0.1.0" => [ ]
|
804
|
+
},
|
805
|
+
|
806
|
+
# NOTE: cookbooks are considered to conflict when both sources have
|
807
|
+
# cookbooks with the same name, regardless of whether any version
|
808
|
+
# numbers overlap.
|
809
|
+
#
|
810
|
+
# The before block does the equivalent to putting this in the
|
811
|
+
# Policyfile.rb:
|
812
|
+
#
|
813
|
+
# cookbook "remote-cb"
|
814
|
+
#
|
815
|
+
# This makes the compiler take a slightly different code path than if
|
816
|
+
# the cookbook was just in the dep graphs.
|
817
|
+
"remote-cb" => {
|
818
|
+
"99.99.99" => [ ]
|
819
|
+
},
|
820
|
+
|
821
|
+
# This also conflicts, but only via the graphs
|
822
|
+
"remote-cb-two" => {
|
823
|
+
"1.2.3" => [ ]
|
824
|
+
}
|
825
|
+
|
826
|
+
}
|
827
|
+
end
|
828
|
+
|
829
|
+
context "and no explicit source is given for the conflicting cookbook" do
|
830
|
+
|
831
|
+
before do
|
832
|
+
# This is on the community site
|
833
|
+
policyfile.dsl.cookbook("remote-cb")
|
834
|
+
end
|
835
|
+
|
836
|
+
it "raises an error describing the conflict" do
|
837
|
+
expected_err = <<-ERROR
|
838
|
+
Source supermarket(https://supermarket.chef.io) and chef_repo(path/to/repo) contain conflicting cookbooks:
|
839
|
+
- remote-cb
|
840
|
+
- remote-cb-two
|
841
|
+
ERROR
|
842
|
+
|
843
|
+
expect { policyfile.remote_artifacts_graph }.to raise_error do |error|
|
844
|
+
expect(error).to be_a(ChefDK::CookbookSourceConflict)
|
845
|
+
expect(error.message).to eq(expected_err)
|
846
|
+
end
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
context "and the conflicting cookbook has an explicit source in the Policyfile" do
|
851
|
+
|
852
|
+
before do
|
853
|
+
# This is on the community site
|
854
|
+
policyfile.dsl.cookbook("remote-cb", path: "path/to/remote-cb")
|
855
|
+
policyfile.dsl.cookbook("remote-cb-two", git: "git://git.example:user/remote-cb-two.git")
|
856
|
+
policyfile.error!
|
857
|
+
end
|
858
|
+
|
859
|
+
it "solves the graph" do
|
860
|
+
expect { policyfile.remote_artifacts_graph }.to_not raise_error
|
861
|
+
end
|
862
|
+
|
863
|
+
it "assigns the correct source options to the cookbook" do
|
864
|
+
remote_cb_source_opts = policyfile.cookbook_location_spec_for("remote-cb").source_options
|
865
|
+
expect(remote_cb_source_opts).to eq(path: "path/to/remote-cb")
|
866
|
+
|
867
|
+
remote_cb_two_source_opts = policyfile.cookbook_location_spec_for("remote-cb-two").source_options
|
868
|
+
expect(remote_cb_two_source_opts).to eq(git: "git://git.example:user/remote-cb-two.git")
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
end
|
873
|
+
|
682
874
|
end
|
683
875
|
|
684
876
|
end
|
@@ -171,7 +171,9 @@ E
|
|
171
171
|
end
|
172
172
|
|
173
173
|
it "has no default cookbook source" do
|
174
|
-
expect(policyfile.default_source).to
|
174
|
+
expect(policyfile.default_source).to be_an(Array)
|
175
|
+
expect(policyfile.default_source.size).to eq(1)
|
176
|
+
expect(policyfile.default_source.first).to be_a(ChefDK::Policyfile::NullCookbookSource)
|
175
177
|
end
|
176
178
|
|
177
179
|
context "with the default source set to the community site" do
|
@@ -185,7 +187,7 @@ E
|
|
185
187
|
|
186
188
|
it "has a default source" do
|
187
189
|
expect(policyfile.errors).to eq([])
|
188
|
-
expected = ChefDK::Policyfile::CommunityCookbookSource.new("https://supermarket.chef.io")
|
190
|
+
expected = [ ChefDK::Policyfile::CommunityCookbookSource.new("https://supermarket.chef.io") ]
|
189
191
|
expect(policyfile.default_source).to eq(expected)
|
190
192
|
end
|
191
193
|
|
@@ -200,7 +202,7 @@ E
|
|
200
202
|
|
201
203
|
it "has a default source" do
|
202
204
|
expect(policyfile.errors).to eq([])
|
203
|
-
expected = ChefDK::Policyfile::CommunityCookbookSource.new("https://cookbook-api.example.com")
|
205
|
+
expected = [ ChefDK::Policyfile::CommunityCookbookSource.new("https://cookbook-api.example.com") ]
|
204
206
|
expect(policyfile.default_source).to eq(expected)
|
205
207
|
end
|
206
208
|
|
@@ -256,7 +258,31 @@ E
|
|
256
258
|
|
257
259
|
it "has a default source" do
|
258
260
|
expect(policyfile.errors).to eq([])
|
259
|
-
expected = ChefDK::Policyfile::ChefRepoCookbookSource.new(chef_repo)
|
261
|
+
expected = [ ChefDK::Policyfile::ChefRepoCookbookSource.new(chef_repo) ]
|
262
|
+
expect(policyfile.default_source).to eq(expected)
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
context "with multiple default sources" do
|
268
|
+
let(:chef_repo) { File.expand_path("spec/unit/fixtures/local_path_cookbooks", project_root) }
|
269
|
+
|
270
|
+
let(:policyfile_rb) do
|
271
|
+
<<-EOH
|
272
|
+
run_list "foo", "bar"
|
273
|
+
|
274
|
+
default_source :community
|
275
|
+
default_source :chef_repo, "#{chef_repo}"
|
276
|
+
EOH
|
277
|
+
end
|
278
|
+
|
279
|
+
it "has an array of sources" do
|
280
|
+
expect(policyfile.errors).to eq([])
|
281
|
+
|
282
|
+
community_source = ChefDK::Policyfile::CommunityCookbookSource.new("https://supermarket.chef.io")
|
283
|
+
repo_source = ChefDK::Policyfile::ChefRepoCookbookSource.new(chef_repo)
|
284
|
+
expected = [ community_source, repo_source ]
|
285
|
+
|
260
286
|
expect(policyfile.default_source).to eq(expected)
|
261
287
|
end
|
262
288
|
|
@@ -336,4 +336,49 @@ describe ChefDK::PolicyfileLock, "when reading a Policyfile.lock" do
|
|
336
336
|
end
|
337
337
|
end
|
338
338
|
|
339
|
+
describe "populating lock data from an archive" do
|
340
|
+
|
341
|
+
let(:valid_cookbook_lock) do
|
342
|
+
{
|
343
|
+
"version" => "1.0.0",
|
344
|
+
"identifier" => "68c13b136a49b4e66cfe9d8aa2b5a85167b5bf9b",
|
345
|
+
"dotted_decimal_identifier" => "111.222.333",
|
346
|
+
"cache_key" => nil,
|
347
|
+
"source" => "path/to/foo",
|
348
|
+
"source_options" => { path: "path/to/foo"},
|
349
|
+
"scm_info" => nil
|
350
|
+
}
|
351
|
+
end
|
352
|
+
|
353
|
+
let(:lock_data) do
|
354
|
+
valid_lock_with_cached_cookbook = valid_lock_data.dup
|
355
|
+
valid_cached_cookbook = valid_cookbook_lock.dup
|
356
|
+
valid_cached_cookbook["cache_key"] = nil
|
357
|
+
valid_cached_cookbook["source"] = "path/to/foo"
|
358
|
+
valid_lock_with_cached_cookbook["cookbook_locks"] = { "foo" => valid_cached_cookbook }
|
359
|
+
valid_lock_with_cached_cookbook
|
360
|
+
end
|
361
|
+
|
362
|
+
before do
|
363
|
+
lockfile.build_from_archive(lock_data)
|
364
|
+
end
|
365
|
+
|
366
|
+
it "creates cookbook locks as archived cookbooks" do
|
367
|
+
locks = lockfile.cookbook_locks
|
368
|
+
|
369
|
+
expect(locks).to have_key("foo")
|
370
|
+
|
371
|
+
cb_foo = locks["foo"]
|
372
|
+
expect(cb_foo).to be_a(ChefDK::Policyfile::ArchivedCookbook)
|
373
|
+
|
374
|
+
expected_path = File.join(storage_config.relative_paths_root, "cookbooks", "foo-111.222.333")
|
375
|
+
|
376
|
+
expect(cb_foo.cookbook_path).to eq(expected_path)
|
377
|
+
expect(cb_foo.dotted_decimal_identifier).to eq("111.222.333")
|
378
|
+
expect(locks["foo"].to_lock).to eq(valid_cookbook_lock)
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
end
|
383
|
+
|
339
384
|
end
|
@@ -0,0 +1,236 @@
|
|
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_policies'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileServices::CleanPolicies do
|
22
|
+
|
23
|
+
let(:chef_config) { double("Chef::Config") }
|
24
|
+
|
25
|
+
let(:policy_lister) do
|
26
|
+
clean_policies_service.policy_lister
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:policies_by_name) { {} }
|
30
|
+
let(:policies_by_group) { {} }
|
31
|
+
|
32
|
+
let(:ui) { TestHelpers::TestUI.new }
|
33
|
+
|
34
|
+
subject(:clean_policies_service) do
|
35
|
+
described_class.new(config: chef_config, ui: ui)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "when there is an error listing data from the server" do
|
39
|
+
|
40
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
41
|
+
|
42
|
+
let(:response) do
|
43
|
+
Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r|
|
44
|
+
r.instance_variable_set(:@body, "oops")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:http_exception) do
|
49
|
+
begin
|
50
|
+
response.error!
|
51
|
+
rescue => e
|
52
|
+
e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
before do
|
57
|
+
expect(policy_lister).to receive(:http_client).and_return(http_client)
|
58
|
+
expect(http_client).to receive(:get).and_raise(http_exception)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises an error" do
|
62
|
+
expect { clean_policies_service.run }.to raise_error(ChefDK::PolicyfileCleanError)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when existing policies are listed successfully" do
|
68
|
+
|
69
|
+
let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
|
70
|
+
|
71
|
+
before do
|
72
|
+
policy_lister.set!(policies_by_name, policies_by_group)
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "cleaning unused policy revisions" do
|
76
|
+
|
77
|
+
before do
|
78
|
+
allow(clean_policies_service).to receive(:http_client).and_return(http_client)
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when there are no policies" do
|
82
|
+
|
83
|
+
before do
|
84
|
+
expect(http_client).to_not receive(:delete)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "doesn't delete anything" do
|
88
|
+
clean_policies_service.run
|
89
|
+
expect(ui.output).to eq("No policy revisions deleted\n")
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when there are policies but none are orphans" do
|
95
|
+
|
96
|
+
let(:policies_by_name) do
|
97
|
+
{
|
98
|
+
"appserver" => {
|
99
|
+
"1111111111111111111111111111111111111111111111111111111111111111" => {},
|
100
|
+
"2222222222222222222222222222222222222222222222222222222222222222" => {}
|
101
|
+
},
|
102
|
+
"load-balancer" => {
|
103
|
+
"5555555555555555555555555555555555555555555555555555555555555555" => {},
|
104
|
+
"6666666666666666666666666666666666666666666666666666666666666666" => {}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:policies_by_group) do
|
110
|
+
{
|
111
|
+
"dev" => {
|
112
|
+
"appserver" => "1111111111111111111111111111111111111111111111111111111111111111",
|
113
|
+
"load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555"
|
114
|
+
},
|
115
|
+
"staging" => {
|
116
|
+
"appserver" => "2222222222222222222222222222222222222222222222222222222222222222",
|
117
|
+
"load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555"
|
118
|
+
},
|
119
|
+
"prod" => {
|
120
|
+
"appserver" => "2222222222222222222222222222222222222222222222222222222222222222",
|
121
|
+
"load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666"
|
122
|
+
}
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
before do
|
127
|
+
expect(http_client).to_not receive(:delete)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "doesn't delete anything" do
|
131
|
+
clean_policies_service.run
|
132
|
+
expect(ui.output).to eq("No policy revisions deleted\n")
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when there are policies and some are orphans" do
|
138
|
+
|
139
|
+
let(:policies_by_name) do
|
140
|
+
{
|
141
|
+
"appserver" => {
|
142
|
+
"1111111111111111111111111111111111111111111111111111111111111111" => {},
|
143
|
+
"2222222222222222222222222222222222222222222222222222222222222222" => {},
|
144
|
+
"4444444444444444444444444444444444444444444444444444444444444444" => {}
|
145
|
+
},
|
146
|
+
"load-balancer" => {
|
147
|
+
"5555555555555555555555555555555555555555555555555555555555555555" => {},
|
148
|
+
"6666666666666666666666666666666666666666666666666666666666666666" => {},
|
149
|
+
"7777777777777777777777777777777777777777777777777777777777777777" => {}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
let(:policies_by_group) do
|
155
|
+
{
|
156
|
+
"dev" => {
|
157
|
+
"appserver" => "1111111111111111111111111111111111111111111111111111111111111111",
|
158
|
+
"load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555"
|
159
|
+
},
|
160
|
+
"staging" => {
|
161
|
+
"appserver" => "2222222222222222222222222222222222222222222222222222222222222222",
|
162
|
+
"load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555"
|
163
|
+
},
|
164
|
+
"prod" => {
|
165
|
+
"appserver" => "2222222222222222222222222222222222222222222222222222222222222222",
|
166
|
+
"load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666"
|
167
|
+
}
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "and all deletes are successful" do
|
172
|
+
|
173
|
+
before do
|
174
|
+
expect(http_client).to receive(:delete).with("/policies/appserver/revisions/4444444444444444444444444444444444444444444444444444444444444444")
|
175
|
+
expect(http_client).to receive(:delete).with("/policies/load-balancer/revisions/7777777777777777777777777777777777777777777777777777777777777777")
|
176
|
+
end
|
177
|
+
|
178
|
+
it "deletes the orphaned policies" do
|
179
|
+
clean_policies_service.run
|
180
|
+
expected_message = <<-MESSAGE
|
181
|
+
DELETE appserver 4444444444444444444444444444444444444444444444444444444444444444
|
182
|
+
DELETE load-balancer 7777777777777777777777777777777777777777777777777777777777777777
|
183
|
+
MESSAGE
|
184
|
+
expect(ui.output).to eq(expected_message)
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
# For example, a user doesn't have permission on all policy_names
|
190
|
+
describe "when some deletes fail" do
|
191
|
+
|
192
|
+
let(:response) do
|
193
|
+
Net::HTTPResponse.send(:response_class, "403").new("1.0", "403", "Unauthorized").tap do |r|
|
194
|
+
r.instance_variable_set(:@body, "I can't let you do that Dave")
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
let(:http_exception) do
|
199
|
+
begin
|
200
|
+
response.error!
|
201
|
+
rescue => e
|
202
|
+
e
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
before do
|
207
|
+
expect(http_client).to receive(:delete).
|
208
|
+
with("/policies/appserver/revisions/4444444444444444444444444444444444444444444444444444444444444444").
|
209
|
+
and_raise(http_exception)
|
210
|
+
expect(http_client).to receive(:delete).with("/policies/load-balancer/revisions/7777777777777777777777777777777777777777777777777777777777777777")
|
211
|
+
end
|
212
|
+
|
213
|
+
it "deletes what it can, then raises an error" do
|
214
|
+
expected_message = <<-ERROR
|
215
|
+
Failed to delete some policy revisions:
|
216
|
+
- appserver (4444444444444444444444444444444444444444444444444444444444444444): Net::HTTPServerException 403 \"Unauthorized\"
|
217
|
+
ERROR
|
218
|
+
|
219
|
+
expect { clean_policies_service.run }.to raise_error do |error|
|
220
|
+
expect(error.message).to eq(expected_message)
|
221
|
+
end
|
222
|
+
expected_message = <<-MESSAGE
|
223
|
+
DELETE appserver 4444444444444444444444444444444444444444444444444444444444444444
|
224
|
+
DELETE load-balancer 7777777777777777777777777777777777777777777777777777777777777777
|
225
|
+
MESSAGE
|
226
|
+
expect(ui.output).to eq(expected_message)
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|