diff options
author | Thom May <thom@may.lt> | 2017-04-06 20:05:16 +0100 |
---|---|---|
committer | danielsdeleo <dan@chef.io> | 2017-04-10 14:36:44 -0700 |
commit | 4b87ea73692e31b9922e449b2a6e5ab3a3820ab6 (patch) | |
tree | 9052d9d8fe245bddf1783f54be6b95aa2aa5f129 | |
parent | 2dc827958371ccba4b83e8afa0e2205c12e57bc9 (diff) | |
download | chef-4b87ea73692e31b9922e449b2a6e5ab3a3820ab6.tar.gz |
Merge pull request #6032 from chef/sd/required-recipe
server enforced required recipe
Signed-off-by: Daniel DeLeo <dan@chef.io>
-rw-r--r-- | lib/chef/client.rb | 44 | ||||
-rw-r--r-- | spec/support/shared/context/client.rb | 7 | ||||
-rw-r--r-- | spec/unit/client_spec.rb | 49 |
3 files changed, 100 insertions, 0 deletions
diff --git a/lib/chef/client.rb b/lib/chef/client.rb index c064d33209..5bd79beac4 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -280,6 +280,8 @@ class Chef run_context = setup_run_context + load_required_recipe(@rest, run_context) unless Chef::Config[:solo_legacy_mode] + if Chef::Config[:audit_mode] != :audit_only converge_error = converge_and_save(run_context) end @@ -515,6 +517,48 @@ class Chef end # + # Adds a required recipe as specified by the Chef Server + # + # @return The modified run context + # + # @api private + # + # TODO: @rest doesn't appear to be used anywhere outside + # of client.register except for here. If it's common practice + # to create your own rest client, perhaps we should do that + # here but it seems more appropriate to reuse one that we + # know is already created. for ease of testing, we'll pass + # the existing rest client in as a parameter + # + def load_required_recipe(rest, run_context) + required_recipe_contents = rest.get("required_recipe") + Chef::FileCache.store("required_recipe", required_recipe_contents) + required_recipe_file = Chef::FileCache.load("required_recipe", false) + + # TODO: add integration tests with resource reporting turned on + # (presumably requires changes to chef-zero) + # + # Chef::Recipe.new takes a cookbook name and a recipe name along + # with the run context. These names are eventually used in the + # resource reporter, and if the cookbook name cannot be found in the + # cookbook collection then we will fail with an exception. Cases where + # we currently also fail: + # - specific recipes + # - chef-apply would fail if resource reporting was enabled + # + recipe = Chef::Recipe.new(nil, nil, run_context) + recipe.from_file(required_recipe_file) + run_context + rescue Net::HTTPServerException => e + case e.response + when Net::HTTPNotFound + Chef::Log.info("Required Recipe not found") + else + raise + end + end + + # # The PolicyBuilder strategy for figuring out run list and cookbooks. # # @return [Chef::PolicyBuilder::Policyfile, Chef::PolicyBuilder::ExpandNodeObject] diff --git a/spec/support/shared/context/client.rb b/spec/support/shared/context/client.rb index c65650e6b1..a241e59533 100644 --- a/spec/support/shared/context/client.rb +++ b/spec/support/shared/context/client.rb @@ -135,6 +135,12 @@ shared_context "a client run" do and_return({}) end + def stub_for_required_recipe + response = Net::HTTPNotFound.new("1.1", "404", "Not Found") + exception = Net::HTTPServerException.new('404 "Not Found"', response) + expect(http_node_load).to receive(:get).with("required_recipe").and_raise(exception) + end + def stub_for_converge # define me end @@ -165,6 +171,7 @@ shared_context "a client run" do stub_for_data_collector_init stub_for_node_load stub_for_sync_cookbooks + stub_for_required_recipe stub_for_converge stub_for_audit stub_for_node_save diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb index ec3f70b9b0..275f5cbbfc 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -402,6 +402,55 @@ describe Chef::Client do end end + describe "load_required_recipe" do + let(:rest) { double("Chef::ServerAPI (required recipe)") } + let(:run_context) { double("Chef::RunContext") } + let(:recipe) { double("Chef::Recipe (required recipe)") } + let(:required_recipe) do + <<EOM +fake_recipe_variable = "for reals" +EOM + end + + context "when required_recipe is configured" do + + before(:each) do + expect(rest).to receive(:get).with("required_recipe").and_return(required_recipe) + expect(Chef::Recipe).to receive(:new).with(nil, nil, run_context).and_return(recipe) + expect(recipe).to receive(:from_file) + end + + it "fetches the recipe and adds it to the run context" do + client.load_required_recipe(rest, run_context) + end + + context "when the required_recipe has bad contents" do + let(:required_recipe) do + <<EOM +this is not a recipe +EOM + end + it "should not raise an error" do + expect { client.load_required_recipe(rest, run_context) }.not_to raise_error() + end + end + end + + context "when required_recipe returns 404" do + let(:http_response) { Net::HTTPNotFound.new("1.1", "404", "Not Found") } + let(:http_exception) { Net::HTTPServerException.new('404 "Not Found"', http_response) } + + before(:each) do + expect(rest).to receive(:get).with("required_recipe").and_raise(http_exception) + end + + it "should log and continue on" do + expect(Chef::Log).to receive(:info) + client.load_required_recipe(rest, run_context) + end + end + end + describe "windows_admin_check" do context "platform is not windows" do before do |