# # Copyright:: Copyright (c) 2014-2018 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/install" describe ChefCLI::PolicyfileServices::Install do include ChefCLI::Helpers let(:working_dir) do path = File.join(tempdir, "policyfile_services_test_working_dir") Dir.mkdir(path) path end let(:policyfile_rb_explicit_name) { nil } let(:policyfile_rb_name) { policyfile_rb_explicit_name || "Policyfile.rb" } let(:policyfile_lock_name) { "Policyfile.lock.json" } let(:policyfile_rb_path) { File.join(working_dir, policyfile_rb_name) } let(:policyfile_lock_path) { File.join(working_dir, policyfile_lock_name) } let(:local_cookbooks_root) do File.join(fixtures_path, "local_path_cookbooks") end let(:policyfile_content) do <<~E name 'install-example' run_list 'local-cookbook' cookbook 'local-cookbook', path: '#{local_cookbooks_root}/local-cookbook' E end let(:overwrite) { false } let(:cookbooks_to_update) { [] || [ "my_cookbook" ] } let(:cookbooks_to_update_empty) { false } let(:ui) { TestHelpers::TestUI.new } let(:install_service) { described_class.new(policyfile: policyfile_rb_name, ui: ui, root_dir: working_dir, overwrite: overwrite) } let(:storage_config) do ChefCLI::Policyfile::StorageConfig.new( cache_path: nil, relative_paths_root: local_cookbooks_root ) end def result_policyfile_lock expect(File).to exist(policyfile_lock_path) content = IO.read(policyfile_lock_path) lock_data = FFI_Yajl::Parser.parse(content) ChefCLI::PolicyfileLock.new(storage_config).build_from_lock_data(lock_data) end context "when no Policyfile is present or specified" do it "errors out" do expect { install_service.run }.to raise_error(ChefCLI::PolicyfileNotFound, "Policyfile not found at path #{policyfile_rb_path}") end end context "When an explicit Policfyfile lock name is given and does not exist" do let(:policyfile_rb_explicit_name) { "i_do_not_exist.lock.json" } it "errors out" do expect { install_service.run }.to raise_error(ChefCLI::PolicyfileNotFound, "Policyfile lock not found at path #{policyfile_rb_path}") end end context "When an explicit Policfyfile name is given and does not exist" do let(:policyfile_rb_explicit_name) { "i_do_not_exist.rb" } it "errors out" do expect { install_service.run }.to raise_error(ChefCLI::PolicyfileNotFound, "Policyfile not found at path #{policyfile_rb_path}") end end context "when a Policyfile exists" do before do with_file(policyfile_rb_path) { |f| f.print(policyfile_content) } end it "infers that the Policyfile.rb is located at $CWD/Policyfile.rb" do expect(install_service.policyfile_expanded_path).to eq(policyfile_rb_path) end it "reads the policyfile from disk" do expect(install_service.policyfile_content).to eq(policyfile_content) end context "and the policyfile has an error" do let(:policyfile_content) { 'raise "borkbork"' } it "errors out and creates no lockfile" do expect { install_service.run }.to raise_error(ChefCLI::PolicyfileInstallError) expect(File).to_not exist(policyfile_lock_path) end end context "and no lockfile exists" do it "solves the Policyfile demands, installs cookbooks, emits a lockfile" do install_service.run generated_lock = result_policyfile_lock expect(generated_lock.name).to eq("install-example") expect(generated_lock.cookbook_locks).to have_key("local-cookbook") end it "prints the policy name" do install_service.run expect(ui.output).to include("Building policy install-example") end it "prints the expanded run list" do install_service.run expect(ui.output).to include("Expanded run list: recipe[local-cookbook]") end it "prints the lockfile path" do install_service.run expect(ui.output).to include("Lockfile written to #{working_dir}/Policyfile.lock.json") end it "prints the lockfile's revision id" do install_service.run expect(ui.output).to include("Policy revision id: 7da81d2c7bb97f904637f97e7f8b487fa4bb1ed682edea7087743dec84c254ec") end end context "when cookbook to update is empty and no policy lock exist" do let(:cookbooks_to_update_empty) { true } it "create the policy lock" do install_service.run(:cookbooks_to_update) generated_lock = result_policyfile_lock expect(generated_lock.name).to eq("install-example") expect(generated_lock.cookbook_locks).to have_key("local-cookbook") end it "checks for policy lock" do lock = install_service.policyfile_lock expect(lock).to eq(nil) end end context "when cookbook to update is empty and policy lock exist" do before do install_service.dup.run end let(:cookbooks_to_update_empty) { true } it "create the policy lock" do install_service.run(:cookbooks_to_update) generated_lock = result_policyfile_lock expect(generated_lock.name).to eq("install-example") expect(generated_lock.cookbook_locks).to have_key("local-cookbook") end it "checks for policy lock" do lock = install_service.policyfile_lock expect(lock).to be_an_instance_of(ChefCLI::PolicyfileLock) expect(lock.name).to eq("install-example") expect(lock.cookbook_locks).to have_key("local-cookbook") end end context "when cookbook to update is not empty and no policy lock exist" do let(:cookbooks_to_update) { [ "my_cookbook" ] } it "create the policy lock" do install_service.run(:cookbooks_to_update) generated_lock = result_policyfile_lock expect(generated_lock.name).to eq("install-example") expect(generated_lock.cookbook_locks).to have_key("local-cookbook") end it "checks for policy lock" do lock = install_service.policyfile_lock expect(lock).to eq(nil) end end context "when cookbook to update is not empty and policy lock exist" do before do install_service.dup.run end let(:cookbooks_to_update) { [ "my_cookbook" ] } it "create the policy lock" do install_service.run(:cookbooks_to_update) generated_lock = result_policyfile_lock expect(generated_lock.name).to eq("install-example") expect(generated_lock.cookbook_locks).to have_key("local-cookbook") end it "create the policy lock" do lock = install_service.policyfile_lock expect(lock).to be_an_instance_of(ChefCLI::PolicyfileLock) expect(lock.name).to eq("install-example") expect(lock.cookbook_locks).to have_key("local-cookbook") end end context "and a lockfile exists and `overwrite` is specified" do let(:overwrite) { true } before do File.binwrite(policyfile_lock_path, "This is the old lockfile content") end it "solves the Policyfile demands, installs cookbooks, emits a lockfile" do install_service.run generated_lock = result_policyfile_lock expect(generated_lock.name).to eq("install-example") expect(generated_lock.cookbook_locks).to have_key("local-cookbook") end end context "and a lockfile exists" do before do install_service.dup.run end it "reads the policyfile lock from disk" do lock = install_service.policyfile_lock expect(lock).to be_an_instance_of(ChefCLI::PolicyfileLock) expect(lock.name).to eq("install-example") expect(lock.cookbook_locks).to have_key("local-cookbook") end it "ensures that cookbooks are installed" do expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_call_original install_service.run end describe "when an error occurs during the install" do before do expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_raise("some error") end it "raises a PolicyfileInstallError" do expect { install_service.run }.to raise_error(ChefCLI::PolicyfileInstallError) end end context "and the Policyfile has updated dependendencies" do # For very first iteration, we won't tackle this case if it's hard it "Conservatively updates deps, recomputes lock, and installs" end end context "and an explicit Policyfile name is given" do let(:policyfile_rb_explicit_name) { "MyPolicy.rb" } let(:policyfile_lock_name) { "MyPolicy.lock.json" } it "infers that the Policyfile.rb is located at $CWD/$POLICYFILE_NAME" do expect(install_service.policyfile_expanded_path).to eq(policyfile_rb_path) end it "reads the policyfile from disk" do expect(install_service.policyfile_content).to eq(policyfile_content) end end end end