diff options
-rw-r--r-- | chef/lib/chef/client.rb | 66 | ||||
-rw-r--r-- | chef/lib/chef/file_cache.rb | 39 | ||||
-rw-r--r-- | chef/lib/chef/node.rb | 45 | ||||
-rw-r--r-- | chef/lib/chef/run_context.rb | 3 | ||||
-rw-r--r-- | chef/spec/unit/client_spec.rb | 6 | ||||
-rw-r--r-- | chef/spec/unit/file_cache_spec.rb | 147 | ||||
-rw-r--r-- | chef/spec/unit/node_spec.rb | 88 | ||||
-rw-r--r-- | features/chef-client/attribute_settings.feature | 13 | ||||
-rw-r--r-- | features/data/json_attribs/attribute_settings.json | 3 | ||||
-rw-r--r-- | features/steps/run_client_steps.rb | 9 | ||||
-rw-r--r-- | features/support/env.rb | 3 |
11 files changed, 261 insertions, 161 deletions
diff --git a/chef/lib/chef/client.rb b/chef/lib/chef/client.rb index 0169e855b5..7d35262bb6 100644 --- a/chef/lib/chef/client.rb +++ b/chef/lib/chef/client.rb @@ -146,9 +146,9 @@ class Chef def determine_node_name unless node_name if Chef::Config[:node_name] - self.node_name = Chef::Config[:node_name] + @node_name = Chef::Config[:node_name] else - self.node_name = ohai[:fqdn] ? ohai[:fqdn] : ohai[:hostname] + @node_name = ohai[:fqdn] ? ohai[:fqdn] : ohai[:hostname] Chef::Config[:node_name] = node_name end @@ -163,37 +163,29 @@ class Chef # === Returns # node<Chef::Node>:: Returns the created node object, also stored in @node def build_node - Chef::Log.debug("Building node object for #{node_name}") + Chef::Log.debug("Building node object for #{@node_name}") unless Chef::Config[:solo] - self.node = begin - rest.get_rest("nodes/#{node_name}") - rescue Net::HTTPServerException => e - raise unless e.message =~ /^404/ - end + begin + @node = rest.get_rest("nodes/#{@node_name}") + rescue Net::HTTPServerException => e + raise unless e.message =~ /^404/ + end end unless node @node_exists = false - self.node = Chef::Node.new - node.name(node_name) + @node = Chef::Node.new + @node.name(node_name) end - node.consume_attributes(json_attribs) - - node.automatic_attrs = ohai.data + @node.prepare_for_run(ohai.data, @json_attribs) + # Need to nil-ify the json attribs so they are not applied on subsequent runs + @json_attribs = nil - platform, version = Chef::Platform.find_platform_and_version(node) - Chef::Log.debug("Platform is #{platform} version #{version}") - @node.automatic_attrs[:platform] = platform - @node.automatic_attrs[:platform_version] = version - # We clear defaults and overrides, so that any deleted attributes between runs are - # still gone. - @node.default_attrs = Mash.new - @node.override_attrs = Mash.new @node end - + # # === Returns # rest<Chef::REST>:: returns Chef::REST connection object @@ -255,7 +247,7 @@ class Chef def cleanup_file_cache(valid_cache_entries) # Delete each file in the cache that we didn't encounter in the # manifest. - Chef::FileCache.list.each do |cache_filename| + Chef::FileCache.find(File.join(%w{cookbooks ** *})).each do |cache_filename| unless valid_cache_entries[cache_filename] Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer on the server.") Chef::FileCache.delete(cache_filename) @@ -273,13 +265,13 @@ class Chef Chef::Log.debug("Cookbooks to load: #{cookbook_hash.inspect}") # Remove all cookbooks no longer relevant to this node - Chef::FileCache.list.each do |cache_file| - if cache_file =~ /^cookbooks\/(.+?)\// - unless cookbook_hash.has_key?($1) - Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.") - Chef::FileCache.delete(cache_file) - end + Chef::FileCache.find(File.join(%w{cookbooks ** *})).each do |cache_file| + #if cache_file =~ /^cookbooks\/(.+?)\// + unless cookbook_hash.has_key?($1) + Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.") + Chef::FileCache.delete(cache_file) end + #end end # Synchronize each of the node's cookbooks @@ -297,16 +289,16 @@ class Chef # Updates the current node configuration on the server. # # === Returns - # true:: Always returns true + # Chef::Node - the current node def save_node Chef::Log.debug("Saving the current state of node #{node_name}") - self.node = if node_exists - rest.put_rest("nodes/#{node_name}", node) - else - result = rest.post_rest("nodes", node) - @node_exists = true - rest.get_rest(result['uri']) - end + if node_exists + @node = @node.save + else + result = rest.post_rest("nodes", node) + @node_exists = true + @node = rest.get_rest(result['uri']) + end end # Converges the node. diff --git a/chef/lib/chef/file_cache.rb b/chef/lib/chef/file_cache.rb index 18ecd66512..9f326ce0d5 100644 --- a/chef/lib/chef/file_cache.rb +++ b/chef/lib/chef/file_cache.rb @@ -31,7 +31,7 @@ class Chef # # === Parameters # path<String>:: The path to the file you want to put in the cache - should - # be relative to Chef::Config[:file_cache_path] + # be relative to file_cache_path # contents<String>:: A string with the contents you want written to the file # # === Returns @@ -51,9 +51,9 @@ class Chef file_path_array = File.split(path) file_name = file_path_array.pop cache_path = create_cache_path(File.join(file_path_array)) - io = File.open(File.join(cache_path, file_name), "w") - io.print(contents) - io.close + File.open(File.join(cache_path, file_name), "w") do |io| + io.print(contents) + end true end @@ -90,7 +90,7 @@ class Chef # # === Parameters # path<String>:: The path to the file you want to load - should - # be relative to Chef::Config[:file_cache_path] + # be relative to file_cache_path # read<True/False>:: Whether to return the file contents, or the path. # Defaults to true. # @@ -121,7 +121,7 @@ class Chef # # === Parameters # path<String>:: The path to the file you want to delete - should - # be relative to Chef::Config[:file_cache_path] + # be relative to file_cache_path # # === Returns # true @@ -145,12 +145,19 @@ class Chef # # === Returns # Array:: An array of files in the cache, suitable for use with load, delete and store - def list() + def list + find("**#{File::Separator}*") + end + + ## + # Find files in the cache by +glob_pattern+ + # === Returns + # [String] - An array of file cache keys matching the glob + def find(glob_pattern) keys = Array.new - Dir[File.join(Chef::Config[:file_cache_path], '**', '*')].each do |f| + Dir[File.join(file_cache_path, glob_pattern)].each do |f| if File.file?(f) - path = f.match("^#{Dir[Chef::Config[:file_cache_path]].first}\/(.+)")[1] - keys << path + keys << f[/^#{Regexp.escape(Dir[file_cache_path].first) + File::Separator}(.+)/, 1] end end keys @@ -160,7 +167,7 @@ class Chef # # === Parameters # path:: The path to the file you want to check - is relative - # to Chef::Config[:file_cache_path] + # to file_cache_path # # === Returns # True:: If the file exists @@ -186,13 +193,13 @@ class Chef # also creates the path if it does not exist. # # === Parameters - # path:: The path to create, relative to Chef::Config[:file_cache_path] + # path:: The path to create, relative to file_cache_path # create_if_missing:: True by default - whether to create the path if it does not exist # # === Returns # String:: The fully expanded path def create_cache_path(path, create_if_missing=true) - cache_dir = File.expand_path(File.join(Chef::Config[:file_cache_path], path)) + cache_dir = File.expand_path(File.join(file_cache_path, path)) if create_if_missing create_path(cache_dir) else @@ -200,6 +207,12 @@ class Chef end end + private + + def file_cache_path + Chef::Config[:file_cache_path] + end + end end end diff --git a/chef/lib/chef/node.rb b/chef/lib/chef/node.rb index 443915633c..0ce2c816ef 100644 --- a/chef/lib/chef/node.rb +++ b/chef/lib/chef/node.rb @@ -352,18 +352,24 @@ class Chef run_list.detect { |r| r == item } ? true : false end - def consume_attributes(attrs) - attrs ||= {} - Chef::Log.debug("Adding JSON Attributes") + def consume_run_list(attrs) + attrs = attrs ? attrs.dup : {} if new_run_list = attrs.delete("recipes") || attrs.delete("run_list") if attrs.key?("recipes") || attrs.key?("run_list") raise Chef::Exceptions::AmbiguousRunlistSpecification, "please set the node's run list using the 'run_list' attribute only." end - Chef::Log.info("Replacing the run_list with #{new_run_list.inspect} from JSON") + Chef::Log.info("Setting the run_list to #{new_run_list.inspect} from JSON") run_list(new_run_list) end - Chef::Mixin::DeepMerge.merge(@normal_attrs, attrs) + attrs + end + # TODO: this code should be killed and removed, but it is used by shef and + # I don't have time to fix it right now. [Dan - 09/Jun/2010] + def consume_attributes(attrs) + attrs = consume_run_list(attrs) + Chef::Log.debug("Applying attributes from json file") + @normal_attrs = Chef::Mixin::DeepMerge.merge(@normal_attrs,attrs) self[:tags] = Array.new unless attribute?(:tags) end @@ -530,8 +536,25 @@ class Chef self end + def prepare_for_run(ohai_data, json_cli_attrs) + Chef::Log.debug("Extracting run list from JSON attributes provided on command line") + @json_attrib_for_expansion = consume_run_list(json_cli_attrs) + + node.automatic_attrs = ohai_data + + platform, version = Chef::Platform.find_platform_and_version(self) + Chef::Log.debug("Platform is #{platform} version #{version}") + @automatic_attrs[:platform] = platform + @automatic_attrs[:platform_version] = version + # We clear defaults and overrides, so that any deleted attributes between runs are + # still gone. + @default_attrs = Mash.new + @override_attrs = Mash.new + end + # Expands the node's run list and deep merges the default and - # override attributes. + # override attributes. Also applies stored attributes (from json provided + # on the command line) # # Returns the fully-expanded list of recipes. # @@ -540,11 +563,15 @@ class Chef # run_list is mutated? Or perhaps do something smarter like # on-demand generation of default_attrs and override_attrs, # invalidated only when run_list is mutated? - def expand_node! + def expand! # This call should only be called on a chef-client run. recipes, expanded_default_attrs, expanded_override_attrs = run_list.expand('server') - self.default_attrs = Chef::Mixin::DeepMerge.merge(default_attrs, expanded_default_attrs) - self.override_attrs = Chef::Mixin::DeepMerge.merge(override_attrs, expanded_override_attrs) + Chef::Log.debug("Applying attributes from json file") + @normal_attrs = Chef::Mixin::DeepMerge.merge(@normal_attrs, @json_attrib_for_expansion) + self[:tags] = Array.new unless attribute?(:tags) + @default_attrs = Chef::Mixin::DeepMerge.merge(default_attrs, expanded_default_attrs) + @override_attrs = Chef::Mixin::DeepMerge.merge(override_attrs, expanded_override_attrs) + recipes end diff --git a/chef/lib/chef/run_context.rb b/chef/lib/chef/run_context.rb index e88e66591c..d9ee566fbe 100644 --- a/chef/lib/chef/run_context.rb +++ b/chef/lib/chef/run_context.rb @@ -85,7 +85,8 @@ class Chef # Retrieve the fully expanded list of recipes for the node by # resolving roles; this step also merges attributes into the # node from the roles/recipes included. - recipe_names = node.expand_node! + recipe_names = node.expand! + recipe_names.each do |recipe_name| # TODO: timh/cw, 5-14-2010: It's distasteful to be including # the DSL in a class outside the context of the DSL diff --git a/chef/spec/unit/client_spec.rb b/chef/spec/unit/client_spec.rb index 7bfc158bc3..4232f75492 100644 --- a/chef/spec/unit/client_spec.rb +++ b/chef/spec/unit/client_spec.rb @@ -41,9 +41,12 @@ describe Chef::Client, "run" do # Fake node node = Chef::Node.new(HOSTNAME) + node.name(FQDN) node[:platform] = "example-platform" node[:platform_version] = "example-platform-1.0" - + + #node.stub!(:expand!) + mock_chef_rest_for_node = OpenStruct.new({ }) mock_chef_rest_for_client = OpenStruct.new({ }) mock_couchdb = OpenStruct.new({ }) @@ -59,6 +62,7 @@ describe Chef::Client, "run" do Chef::Config[:client_key] = temp_client_key_file.path # Client.register will register with the validation client name. + Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url]).at_least(1).times.and_return(mock_chef_rest_for_node) Chef::REST.should_receive(:new).with(Chef::Config[:client_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key]).and_return(mock_chef_rest_for_client) mock_chef_rest_for_client.should_receive(:register).with(FQDN, Chef::Config[:client_key]).and_return(true) # Client.register will then turn around create another diff --git a/chef/spec/unit/file_cache_spec.rb b/chef/spec/unit/file_cache_spec.rb index 7026a4368b..311ab1d712 100644 --- a/chef/spec/unit/file_cache_spec.rb +++ b/chef/spec/unit/file_cache_spec.rb @@ -18,108 +18,97 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) -describe Chef::FileCache, "store method" do - before(:each) do - Chef::Config[:file_cache_path] = "/tmp/foo" - Dir.stub!(:mkdir).and_return(true) - File.stub!(:directory?).and_return(true) - @io = mock("IO", { :print => true, :close => true }) - File.stub!(:open).and_return(@io) +describe Chef::FileCache do + before do + @file_cache_path = Dir.mktmpdir + Chef::Config[:file_cache_path] = @file_cache_path + @io = StringIO.new end - it "should create the directories leading up to bang" do - File.stub!(:directory?).and_return(false) - Dir.should_receive(:mkdir).with("/tmp").and_return(true) - Dir.should_receive(:mkdir).with("/tmp/foo").and_return(true) - Dir.should_receive(:mkdir).with("/tmp/foo/whiz").and_return(true) - Dir.should_not_receive(:mkdir).with("/tmp/foo/whiz/bang").and_return(true) - Chef::FileCache.store("whiz/bang", "I found a poop") + after do + FileUtils.rm_rf(Chef::Config[:file_cache_path]) end - it "should create a file at /tmp/foo/whiz/bang" do - File.should_receive(:open).with("/tmp/foo/whiz/bang", "w").and_return(@io) - Chef::FileCache.store("whiz/bang", "I found a poop") - end + describe "when the relative path to the cache file doesn't exist" do + it "creates intermediate directories as needed" do + Chef::FileCache.store("whiz/bang", "I found a poop") + File.should exist(File.join(@file_cache_path, 'whiz')) + end - it "should print the contents to the file" do - @io.should_receive(:print).with("I found a poop") - Chef::FileCache.store("whiz/bang", "I found a poop") - end + it "creates the cached file at the correct relative path" do + File.should_receive(:open).with(File.join(@file_cache_path, 'whiz', 'bang'), "w").and_yield(@io) + Chef::FileCache.store("whiz/bang", "borkborkbork") + end - it "should close the file" do - @io.should_receive(:close) - Chef::FileCache.store("whiz/bang", "I found a poop") end + + describe "when storing a file" do + before do + File.stub!(:open).and_yield(@io) + end -end + it "should print the contents to the file" do + Chef::FileCache.store("whiz/bang", "borkborkbork") + @io.string.should == "borkborkbork" + end -describe Chef::FileCache, "load method" do - before(:each) do - Chef::Config[:file_cache_path] = "/tmp/foo" - Dir.stub!(:mkdir).and_return(true) - File.stub!(:directory?).and_return(true) - File.stub!(:exists?).and_return(true) - File.stub!(:read).and_return("I found a poop") end - it "should find the full path to whiz/bang" do - File.should_receive(:read).with("/tmp/foo/whiz/bang").and_return(true) - Chef::FileCache.load('whiz/bang') - end + describe "when loading cached files" do + it "finds and reads the cached file" do + FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) + File.open(File.join(@file_cache_path, 'whiz', 'bang'), 'w') { |f| f.print("borkborkbork") } + Chef::FileCache.load('whiz/bang').should == 'borkborkbork' + end - it "should raise a Chef::Exceptions::FileNotFound if the file doesn't exist" do - File.stub!(:exists?).and_return(false) - lambda { Chef::FileCache.load('whiz/bang') }.should raise_error(Chef::Exceptions::FileNotFound) + it "should raise a Chef::Exceptions::FileNotFound if the file doesn't exist" do + lambda { Chef::FileCache.load('whiz/bang') }.should raise_error(Chef::Exceptions::FileNotFound) + end end -end -describe Chef::FileCache, "delete method" do - before(:each) do - Chef::Config[:file_cache_path] = "/tmp/foo" - Dir.stub!(:mkdir).and_return(true) - File.stub!(:directory?).and_return(true) - File.stub!(:exists?).and_return(true) - File.stub!(:unlink).and_return(true) - end + describe "when deleting cached files" do + before(:each) do + FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) + File.open(File.join(@file_cache_path, 'whiz', 'bang'), 'w') { |f| f.print("borkborkbork") } + end + + it "unlinks the file" do + Chef::FileCache.delete("whiz/bang") + File.should_not exist(File.join(@file_cache_path, 'whiz', 'bang')) + end - it "should unlink the full path to whiz/bang" do - File.should_receive(:unlink).with("/tmp/foo/whiz/bang").and_return(true) - Chef::FileCache.delete("whiz/bang") end -end + describe "when listing files in the cache" do + before(:each) do + FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) + FileUtils.touch(File.join(@file_cache_path, 'whiz', 'bang')) + FileUtils.mkdir_p(File.join(@file_cache_path, 'snappy')) + FileUtils.touch(File.join(@file_cache_path, 'snappy', 'patter')) + end -describe Chef::FileCache, "list method" do - before(:each) do - Chef::Config[:file_cache_path] = "/tmp/foo" - Dir.stub!(:[]).with(File.join(Chef::Config[:file_cache_path], '**', '*')).and_return(["/tmp/foo/whiz/bang", "/tmp/foo/snappy/patter"]) - Dir.stub!(:[]).with(Chef::Config[:file_cache_path]).and_return(["/tmp/foo"]) - File.stub!(:file?).and_return(true) - end + it "should return the relative paths" do + Chef::FileCache.list.sort.should == %w{snappy/patter whiz/bang} + end - it "should return the relative paths" do - Chef::FileCache.list.should eql([ "whiz/bang", "snappy/patter" ]) - end -end + it "searches for cached files by globbing" do + Chef::FileCache.find('snappy/**/*').should == %w{snappy/patter} + end -describe Chef::FileCache, "has_key? method" do - before(:each) do - Chef::Config[:file_cache_path] = "/tmp/foo" end - it "should check the full path to the file" do - File.should_receive(:exists?).with("/tmp/foo/whiz/bang") - Chef::FileCache.has_key?("whiz/bang") - end + describe "when checking for the existence of a file" do + before do + FileUtils.mkdir_p(File.join(@file_cache_path, 'whiz')) + end - it "should return true if the file exists" do - File.stub!(:exists?).and_return(true) - Chef::FileCache.has_key?("whiz/bang").should eql(true) - end + it "has a key if the corresponding cache file exists" do + FileUtils.touch(File.join(@file_cache_path, 'whiz', 'bang')) + Chef::FileCache.should have_key("whiz/bang") + end - it "should return false if the file does not exist" do - File.stub!(:exists?).and_return(false) - Chef::FileCache.has_key?("whiz/bang").should eql(false) + it "doesn't have a key if the corresponding cache file doesn't exist" do + Chef::FileCache.should_not have_key("whiz/bang") + end end end - diff --git a/chef/spec/unit/node_spec.rb b/chef/spec/unit/node_spec.rb index f5746738f0..c626420f67 100644 --- a/chef/spec/unit/node_spec.rb +++ b/chef/spec/unit/node_spec.rb @@ -184,34 +184,41 @@ describe Chef::Node do end describe "consuming json" do - it "should add any json attributes to the node" do - @node.consume_attributes "one" => "two", "three" => "four" - @node.one.should eql("two") - @node.three.should eql("four") - end - it "should allow you to set recipes from the json attributes" do - @node.consume_attributes "recipes" => [ "one", "two", "three" ] - @node.recipes.should == [ "one", "two", "three" ] + it "consumes the run list portion of a collection of attributes and returns the remainder" do + attrs = {"run_list" => [ "role[base]", "recipe[chef::server]" ], "foo" => "bar"} + @node.consume_run_list(attrs).should == {"foo" => "bar"} + @node.run_list.should == [ "role[base]", "recipe[chef::server]" ] end - it "should overwrite the run list if you set recipes twice" do - @node.consume_attributes "recipes" => [ "one", "two" ] - @node.consume_attributes "recipes" => [ "three" ] + it "should overwrites the run list with the run list it consumes" do + @node.consume_run_list "recipes" => [ "one", "two" ] + @node.consume_run_list "recipes" => [ "three" ] @node.recipes.should == [ "three" ] end - it "should allow you to set a run_list from the json attributes" do - @node.consume_attributes "run_list" => [ "role[base]", "recipe[chef::server]" ] - @node.run_list.should == [ "role[base]", "recipe[chef::server]" ] - end - it "should not add duplicate recipes from the json attributes" do @node.recipes << "one" - @node.consume_attributes "recipes" => [ "one", "two", "three" ] + @node.consume_run_list "recipes" => [ "one", "two", "three" ] @node.recipes.should == [ "one", "two", "three" ] end + it "doesn't change the run list if no run_list is specified in the json" do + @node.run_list << "role[database]" + @node.consume_run_list "foo" => "bar" + @node.run_list.should == ["role[database]"] + end + + it "raises an exception if you provide both recipe and run_list attributes, since this is ambiguous" do + lambda { @node.consume_run_list "recipes" => "stuff", "run_list" => "other_stuff" }.should raise_error(Chef::Exceptions::AmbiguousRunlistSpecification) + end + + it "should add json attributes to the node" do + @node.consume_attributes "one" => "two", "three" => "four" + @node.one.should eql("two") + @node.three.should eql("four") + end + it "should set the tags attribute to an empty array if it is not already defined" do @node.consume_attributes({}) @node.tags.should eql([]) @@ -238,9 +245,52 @@ describe Chef::Node do @node.one.to_hash.should == {"two" => {"three" => "forty-two"}} end - it "raises an exception if you provide both recipe and run_list attributes, since this is ambiguous" do - lambda { @node.consume_attributes "recipes" => "stuff", "run_list" => "other_stuff" }.should raise_error(Chef::Exceptions::AmbiguousRunlistSpecification) + end + + describe "preparing for a chef client run" do + before do + @ohai_data = {:platform => 'foobuntu', :platform_version => '23.42'} + end + +# @json_attribs = node.consume_run_list(json_attribs) +# +# node.automatic_attrs = ohai.data +# +# platform, version = Chef::Platform.find_platform_and_version(node) +# Chef::Log.debug("Platform is #{platform} version #{version}") +# @node.automatic_attrs[:platform] = platform +# @node.automatic_attrs[:platform_version] = version +# # We clear defaults and overrides, so that any deleted attributes between runs are +# # still gone. +# @node.default_attrs = Mash.new +# @node.override_attrs = Mash.new + + it "clears the default and override attributes" do + @node.default_attrs["foo"] = "bar" + @node.override_attrs["baz"] = "qux" + @node.prepare_for_run(@ohai_data, {}) + @node.default_attrs.should be_empty + @node.override_attrs.should be_empty + end + + it "sets its platform according to platform detection" do + @node.prepare_for_run(@ohai_data, {}) + @node.automatic_attrs[:platform].should == 'foobuntu' + @node.automatic_attrs[:platform_version].should == '23.42' + end + + it "consumes the run list from provided json attributes" do + @node.prepare_for_run(@ohai_data, {"run_list" => ['recipe[unicorn]']}) + @node.run_list.should == ['recipe[unicorn]'] + end + + it "saves non-runlist json attrs for later" do + @node.run_list.stub!(:expand).and_return([[], {}, {}]) + @node.prepare_for_run(@ohai_data, {"foo" => "bar"}) + @node.expand! + @node.normal_attrs.should == {"foo" => "bar", "tags" => []} end + end # TODO: timh, cw: 2010-5-19: Node.recipe? deprecated. See node.rb diff --git a/features/chef-client/attribute_settings.feature b/features/chef-client/attribute_settings.feature index 566c57ea24..4427f719b2 100644 --- a/features/chef-client/attribute_settings.feature +++ b/features/chef-client/attribute_settings.feature @@ -116,4 +116,15 @@ Feature: Set default, normal, and override attributes Then the run should exit '0' And a file named 'attribute_setting.txt' should contain 'snakes' - + @chef1286 + Scenario: Attributes are provided as JSON via the command line + Given a validated node + And it includes the recipe 'attribute_settings_normal' + When I run the chef-client with json attributes + Then the run should exit '0' + Then a file named 'attribute_setting.txt' should contain 'from_json_file' + When the node is retrieved from the API + Then the inflated responses key 'attribute_priority_was' should match 'from_json_file' + + + diff --git a/features/data/json_attribs/attribute_settings.json b/features/data/json_attribs/attribute_settings.json new file mode 100644 index 0000000000..effc4941e8 --- /dev/null +++ b/features/data/json_attribs/attribute_settings.json @@ -0,0 +1,3 @@ +{ + "attribute_priority_was": "from_json_file" +}
\ No newline at end of file diff --git a/features/steps/run_client_steps.rb b/features/steps/run_client_steps.rb index c1de01e913..41b23e10e6 100644 --- a/features/steps/run_client_steps.rb +++ b/features/steps/run_client_steps.rb @@ -16,6 +16,8 @@ # limitations under the License. # +CHEF_CLIENT = File.join(CHEF_PROJECT_ROOT, "chef", "bin", "chef-client") + ### # When ### @@ -60,6 +62,13 @@ When /^I run the chef\-client at log level '(.+)'$/ do |log_level| When "I run the chef-client" end +When 'I run the chef-client with json attributes' do + @log_level = :debug + @chef_args = "-j #{File.join(FEATURES_DATA, 'json_attribs', 'attribute_settings.json')}" + When "I run the chef-client" +end + + When /^I run the chef\-client with config file '(.+)'$/ do |config_file| @config_file = config_file When "I run the chef-client" diff --git a/features/support/env.rb b/features/support/env.rb index a50847afe9..8a985066aa 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -22,7 +22,8 @@ require 'spec/expectations' CHEF_PROJECT_ROOT = File.expand_path(File.dirname(__FILE__) + '/../../') KNIFE_CONFIG = CHEF_PROJECT_ROOT + '/features/data/config/knife.rb' KNIFE_CMD = File.expand_path(File.join(CHEF_PROJECT_ROOT, "chef", "bin", "knife")) -INTEGRATION_COOKBOOKS = File.join(CHEF_PROJECT_ROOT, "features", "data", "cookbooks") +FEATURES_DATA = File.join(CHEF_PROJECT_ROOT, "features", "data") +INTEGRATION_COOKBOOKS = File.join(FEATURES_DATA, "cookbooks") $:.unshift(CHEF_PROJECT_ROOT + '/chef/lib') $:.unshift(CHEF_PROJECT_ROOT + '/chef-server-api/lib') |