# # Copyright:: Copyright (c) 2015 Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require "spec_helper" require "chef-cli/policyfile_services/show_policy" describe ChefCLI::PolicyfileServices::ShowPolicy do let(:chef_config) { double("Chef::Config") } let(:ui) { TestHelpers::TestUI.new } let(:policy_name) { nil } let(:policy_group) { nil } let(:show_orphans) { false } let(:summary_diff) { false } subject(:show_policy_service) do described_class.new(config: chef_config, ui: ui, policy_name: policy_name, policy_group: policy_group, show_orphans: show_orphans, summary_diff: summary_diff) end let(:policy_lister) do show_policy_service.policy_lister end let(:policyfile_locks_content) {} describe "show all" do let(:params) { [] } let(:policies_by_name) { {} } let(:policies_by_group) { {} } describe "when an error occurs fetching data from the server" do let(:http_client) { instance_double(Chef::ServerAPI) } let(:response) do Net::HTTPResponse.send(:response_class, "500").new("1.0", "500", "Internal Server Error").tap do |r| r.instance_variable_set(:@body, "oops") end end let(:http_exception) do begin response.error! rescue => e e end end let(:policies_url) { "/policies" } let(:policy_groups_url) { "/policy_groups" } before do allow(policy_lister).to receive(:http_client).and_return(http_client) end describe "when fetching policy revisions by policy group" do before do expect(http_client).to receive(:get).and_raise(http_exception) end it "raises an error" do expect { show_policy_service.run }.to raise_error(ChefCLI::PolicyfileListError) end end end context "when the server returns the data successfully" do before do policy_lister.set!(policies_by_name, policies_by_group) policy_lister.policy_lock_content = policyfile_locks_content show_policy_service.run end context "when there are no policies or groups on the server" do it "prints a message to stderr that there aren't any policies or groups" do expect(ui.output).to eq("No policies or policy groups exist on the server\n") end end context "when there are policies but no groups" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, }, "load-balancer" => { "5555555555555555555555555555555555555555555555555555555555555555" => {}, "6666666666666666666666666666666666666666666666666666666666666666" => {}, }, "db" => { "9999999999999999999999999999999999999999999999999999999999999999" => {}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}, }, } end it "prints a message to stderr that there are no active policies" do expected_output = <<~OUTPUT appserver ========= Policy appserver is not assigned to any groups load-balancer ============= Policy load-balancer is not assigned to any groups db == Policy db is not assigned to any groups OUTPUT expect(ui.output).to eq(expected_output) end context "with orphans shown" do let(:show_orphans) { true } it "shows all policies as orphaned" do expected_output = <<~OUTPUT appserver ========= Policy appserver is not assigned to any groups Orphaned: --------- * 1111111111 * 2222222222 load-balancer ============= Policy load-balancer is not assigned to any groups Orphaned: --------- * 5555555555 * 6666666666 db == Policy db is not assigned to any groups Orphaned: --------- * 9999999999 * aaaaaaaaaa OUTPUT expect(ui.output).to eq(expected_output) end end end context "when there are groups but no policies" do let(:policies_by_group) do { "dev" => {}, "staging" => {}, "prod" => {}, } end it "prints a message to stderr and exits" do expect(ui.output).to eq("No policies exist on the server\n") end end context "when there is a revision of each kind of policy assigned to every policy group" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, }, "load-balancer" => { "5555555555555555555555555555555555555555555555555555555555555555" => {}, "6666666666666666666666666666666666666666666666666666666666666666" => {}, }, "db" => { "9999999999999999999999999999999999999999999999999999999999999999" => {}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}, }, } end let(:policies_by_group) do { "dev" => { "appserver" => "1111111111111111111111111111111111111111111111111111111111111111", "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", }, "staging" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", }, "prod" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", "load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666", "db" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, } end it "shows each policy name, followed by a list of group_name -> revision" do expected_output = <<~OUTPUT appserver ========= * dev: 1111111111 * staging: 2222222222 * prod: 2222222222 load-balancer ============= * dev: 5555555555 * staging: 5555555555 * prod: 6666666666 db == * dev: 9999999999 * staging: 9999999999 * prod: aaaaaaaaaa OUTPUT expect(ui.output).to eq(expected_output) end end context "when there is a revision of each kind of policy assigned to every policy group, plus orphaned policies" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, "3333333333333333333333333333333333333333333333333333333333333333" => {}, }, "load-balancer" => { "5555555555555555555555555555555555555555555555555555555555555555" => {}, "6666666666666666666666666666666666666666666666666666666666666666" => {}, "7777777777777777777777777777777777777777777777777777777777777777" => {}, }, "db" => { "9999999999999999999999999999999999999999999999999999999999999999" => {}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" => {}, }, } end let(:policies_by_group) do { "dev" => { "appserver" => "1111111111111111111111111111111111111111111111111111111111111111", "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", }, "staging" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", }, "prod" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", "load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666", "db" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, } end it "shows each policy name, followed by a list of group_name -> revision, followed by a list of orphaned policies" do expected_output = <<~OUTPUT appserver ========= * dev: 1111111111 * staging: 2222222222 * prod: 2222222222 load-balancer ============= * dev: 5555555555 * staging: 5555555555 * prod: 6666666666 db == * dev: 9999999999 * staging: 9999999999 * prod: aaaaaaaaaa OUTPUT expect(ui.output).to eq(expected_output) end context "with orphans shown" do let(:show_orphans) { true } it "shows each policy name, followed by a list of group_name -> revision, followed by a list of orphaned policies" do expected_output = <<~OUTPUT appserver ========= * dev: 1111111111 * staging: 2222222222 * prod: 2222222222 Orphaned: --------- * 3333333333 load-balancer ============= * dev: 5555555555 * staging: 5555555555 * prod: 6666666666 Orphaned: --------- * 7777777777 db == * dev: 9999999999 * staging: 9999999999 * prod: aaaaaaaaaa Orphaned: --------- * bbbbbbbbbb OUTPUT expect(ui.output).to eq(expected_output) end end end context "when some groups do not have a revision of every policy" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, }, "load-balancer" => { "5555555555555555555555555555555555555555555555555555555555555555" => {}, "6666666666666666666666666666666666666666666666666666666666666666" => {}, }, "db" => { "9999999999999999999999999999999999999999999999999999999999999999" => {}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}, }, "memcache" => { "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" => {}, }, } end let(:policies_by_group) do { "dev" => { "appserver" => "1111111111111111111111111111111111111111111111111111111111111111", "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", "memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", }, "staging" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", "memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", }, "prod" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", "load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666", "db" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, } end it "shows each policy name, followed by a list of group_name -> revision, omitting groups that don't have that policy" do expected_output = <<~OUTPUT appserver ========= * dev: 1111111111 * staging: 2222222222 * prod: 2222222222 load-balancer ============= * dev: 5555555555 * staging: 5555555555 * prod: 6666666666 db == * dev: 9999999999 * staging: 9999999999 * prod: aaaaaaaaaa memcache ======== * dev: dddddddddd * staging: dddddddddd * prod: *NOT APPLIED* OUTPUT expect(ui.output).to eq(expected_output) end end end end describe "showing a single policy" do let(:policy_name) { "appserver" } let(:policies_by_name) { {} } let(:policies_by_group) { {} } before do policy_lister.set!(policies_by_name, policies_by_group) end context "when the server returns the data successfully" do before do policy_lister.set!(policies_by_name, policies_by_group) policy_lister.policy_lock_content = policyfile_locks_content show_policy_service.run end context "when there are no policies or groups on the server" do let(:policies_by_name) do {} end let(:policies_by_group) do {} end it "prints a message to stderr that there are no copies of the policy on the server" do expected_output = <<~OUTPUT appserver ========= No policies named 'appserver' are associated with a policy group OUTPUT expect(ui.output).to eq(expected_output) end end context "when there are no revisions of the policy on the server" do let(:policies_by_name) do { "load-balancer" => { "5555555555555555555555555555555555555555555555555555555555555555" => {}, "6666666666666666666666666666666666666666666666666666666666666666" => {}, }, "db" => { "9999999999999999999999999999999999999999999999999999999999999999" => {}, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {}, }, "memcache" => { "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" => {}, }, } end let(:policies_by_group) do { "dev" => { "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", "memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", }, "staging" => { "load-balancer" => "5555555555555555555555555555555555555555555555555555555555555555", "db" => "9999999999999999999999999999999999999999999999999999999999999999", "memcache" => "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", }, "prod" => { "load-balancer" => "6666666666666666666666666666666666666666666666666666666666666666", "db" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", }, } end it "prints a message to stderr that there are no copies of the policy on the server" do expected_output = <<~OUTPUT appserver ========= No policies named 'appserver' are associated with a policy group OUTPUT expect(ui.output).to eq(expected_output) end end context "when all policies are orphaned and orphans are not shown" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, "3333333333333333333333333333333333333333333333333333333333333333" => {}, }, } end let(:policies_by_group) do {} end it "explains that no policies are assigned to a group" do expected_output = <<~OUTPUT appserver ========= No policies named 'appserver' are associated with a policy group OUTPUT expect(ui.output).to eq(expected_output) end end context "when all policy groups have the same revision of the policy" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, "3333333333333333333333333333333333333333333333333333333333333333" => {}, }, } end let(:policies_by_group) do { "dev" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", }, "staging" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", }, "prod" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", }, } end it "lists each of the groups with the associated revision" do expected_output = <<~OUTPUT appserver ========= * dev: 2222222222 * staging: 2222222222 * prod: 2222222222 OUTPUT expect(ui.output).to eq(expected_output) end end context "when policy groups have revisions with differing cookbooks" do let(:policies_by_name) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => {}, "2222222222222222222222222222222222222222222222222222222222222222" => {}, "3333333333333333333333333333333333333333333333333333333333333333" => {}, }, } end let(:policies_by_group) do { "dev" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", }, "staging" => { "appserver" => "2222222222222222222222222222222222222222222222222222222222222222", }, "prod" => { "appserver" => "1111111111111111111111111111111111111111111111111111111111111111", }, } end it "lists each of the groups with the associated revision" do expected_output = <<~OUTPUT appserver ========= * dev: 2222222222 * staging: 2222222222 * prod: 1111111111 OUTPUT expect(ui.output).to eq(expected_output) end context "when the diff summary option is given" do let(:appserver_lock_contents_111) do { "cookbook_locks" => { "apache2" => { "version" => "2.1.3", "identifier" => "abcdef" + ("0" * 34), }, "yum" => { "version" => "4.5.6", "identifier" => "123abc" + ("0" * 34), }, "apt" => { "version" => "10.0.0", "identifier" => "ffffff" + ("0" * 34), }, }, } end let(:appserver_lock_contents_222) do { "cookbook_locks" => { "apache2" => { "version" => "2.0.5", "identifier" => "aaa123" + ("0" * 34), }, "yum" => { "version" => "4.5.2", "identifier" => "867530" + ("9" * 34), }, "apt" => { "version" => "10.0.0", "identifier" => "ffffff" + ("0" * 34), }, "other_cookbook" => { "version" => "9.8.7", "identifier" => "113113" + ("0" * 34), }, }, } end let(:policyfile_locks_content) do { "appserver" => { "1111111111111111111111111111111111111111111111111111111111111111" => appserver_lock_contents_111, "2222222222222222222222222222222222222222222222222222222222222222" => appserver_lock_contents_222, }, } end let(:summary_diff) { true } it "lists each of the groups and displays the version and identifier of the differing cookbooks" do expected_output = <<~OUTPUT appserver ========= dev: 2222222222 ------------------- * apache2: 2.0.5 (aaa1230000) * yum: 4.5.2 (8675309999) * other_cookbook: 9.8.7 (1131130000) staging: 2222222222 ------------------- * apache2: 2.0.5 (aaa1230000) * yum: 4.5.2 (8675309999) * other_cookbook: 9.8.7 (1131130000) prod: 1111111111 ------------------- * apache2: 2.1.3 (abcdef0000) * yum: 4.5.6 (123abc0000) * other_cookbook: *NONE* OUTPUT expect(ui.output).to eq(expected_output) end end context "when orphans are displayed" do let(:show_orphans) { true } it "lists each of the groups, then lists the orphaned revisions" do expected_output = <<~OUTPUT appserver ========= * dev: 2222222222 * staging: 2222222222 * prod: 1111111111 Orphaned: --------- * 3333333333 OUTPUT expect(ui.output).to eq(expected_output) end end end end end # showing a single policy describe "show policy in a specific policy group" do let(:policy_name) { "appserver" } let(:policy_group) { "dev" } let(:http_client) { instance_double("Chef::ServerAPI", url: "https://chef.example/organizations/monkeynews") } before do allow(show_policy_service).to receive(:http_client).and_return(http_client) end it "enables show_policy_revision" do expect(show_policy_service.show_policy_revision?).to be(true) end context "when there is no policy assigned for the given name and group" do let(:response) do Net::HTTPResponse.send(:response_class, "404").new("1.0", "404", "Not Found").tap do |r| r.instance_variable_set(:@body, "nope") end end let(:http_exception) do begin response.error! rescue => e e end end before do expect(http_client).to receive(:get).with("policy_groups/dev/policies/appserver").and_raise(http_exception) end it "prints a message saying there is no policy assigned" do message = "No Policyfile lock named 'appserver' found in policy_group 'dev' at https://chef.example/organizations/monkeynews" expect { show_policy_service.run }.to raise_error(ChefCLI::PolicyfileDownloadError, message) end end context "when the policy exists" do let(:policyfile_lock_data) do { "revision_id" => "cf5b8a020bdc1ba6914093a8a07a5514cce8a3a2979a967b1f32ea704a61785b", "name" => "example", "run_list" => [ "recipe[omnibus::default]" ], "cookbook_locks" => { "omnibus" => { "version" => "2.2.0", "identifier" => "64b3e64306cff223206348e46af545b19032b170", "dotted_decimal_identifier" => "28345299219435506.9887234981653237.76628930769264", "cache_key" => "omnibus-2cf98f9797cacce9c8688fc4e74858b858e2bc14", "origin" => "git@github.com:opscode-cookbooks/omnibus.git", "source_options" => { "git" => "git@github.com:opscode-cookbooks/omnibus.git", "revision" => "2cf98f9797cacce9c8688fc4e74858b858e2bc14", "branch" => "master", }, }, }, } end let(:policyfile_lock_json) { FFI_Yajl::Encoder.encode(policyfile_lock_data, pretty: true) } let(:pager) { instance_double("ChefCLI::Pager", ui: ui) } before do allow(ChefCLI::Pager).to receive(:new).and_return(pager) allow(pager).to receive(:with_pager).and_yield(pager) allow(http_client).to receive(:get).with("policy_groups/dev/policies/appserver").and_return(policyfile_lock_data) end it "displays the policy" do show_policy_service.run expect(ui.output).to include(policyfile_lock_json) end end end end