diff options
author | Serdar Sutay <serdar@opscode.com> | 2014-11-08 16:13:28 -0800 |
---|---|---|
committer | Serdar Sutay <serdar@opscode.com> | 2014-11-08 16:13:28 -0800 |
commit | 60739dd573795624679101eedcd431998befa9b4 (patch) | |
tree | 2b394f236762c4263ba72164ac822a7407a762a9 | |
parent | 5be5e42ba2359e0cdadc345fad10b8d398731675 (diff) | |
parent | c7c51e3ab60871837caa81c3c4c39fd3febe83cf (diff) | |
download | chef-60739dd573795624679101eedcd431998befa9b4.tar.gz |
Merge pull request #2386 from opscode/sersut/12-attr-port
Merge pull request #2097 from opscode/lcg/chef-12-attr
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | RELEASE_NOTES.md | 8 | ||||
-rw-r--r-- | lib/chef/node.rb | 16 | ||||
-rw-r--r-- | lib/chef/node/attribute.rb | 107 | ||||
-rw-r--r-- | lib/chef/node/attribute_collections.rb | 112 | ||||
-rw-r--r-- | spec/unit/node/attribute_spec.rb | 6 | ||||
-rw-r--r-- | spec/unit/node_spec.rb | 334 |
7 files changed, 540 insertions, 44 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index a388733423..aed9f16163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -146,6 +146,7 @@ ### Chef Contributions +* Added RFC-023 Chef 12 Attribute Changes (https://github.com/opscode/chef-rfc/blob/master/rfc023-chef-12-attributes-changes.md) * Added os/platform_family options to provides syntax on the Chef::Resource DSL * Added provides methods to the Chef::Provider DSL * Added supported?(resource, action) class method to all Providers for late-evaluation if a provider can handle a diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3541460625..924f189543 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -21,6 +21,14 @@ block to the `on_create` method; it is now configured by calling either # End-User Changes +## Chef 12 Attribute Changes + +The Chef 12 Attribute RFC 23 (https://github.com/opscode/chef-rfc/blob/master/rfc023-chef-12-attributes-changes.md) has been merged into +Chef. This adds the ability to remove precedence levels (or all levels) of attributes in recipes code, or to +force setting an attribute precedence level. The major backwards incompatible change to call out in this RFC is that +`node.force_default!` and `node.force_override!` have changed from accessors to setters, and any cookbook code that used these functions +(extremely uncommon) simply needs to drop the exclamation point off of the method in order to use the accessor. + ## Knife Prefers `config.rb` to `knife.rb`. Knife will now look for `config.rb` in preference to `knife.rb` for its diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 5f788af4d4..dbb7852586 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -42,6 +42,8 @@ class Chef extend Forwardable def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key? + def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override + def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override! attr_accessor :recipe_list, :run_state, :override_runlist @@ -146,13 +148,6 @@ class Chef attributes.default end - # Set a force default attribute. Intermediate mashes will be created by - # auto-vivify if necessary. - def default! - attributes.set_unless_value_present = false - attributes.default! - end - # Set a default attribute of this node, auto-vivifying any mashes that are # missing, but if the final value already exists, don't set it def default_unless @@ -167,13 +162,6 @@ class Chef attributes.override end - # Set a force override attribute. Intermediate mashes will be created by - # auto-vivify if needed. - def override! - attributes.set_unless_value_present = false - attributes.override! - end - # Set an override attribute of this node, auto-vivifying any mashes that # are missing, but if the final value already exists, don't set it def override_unless diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index 66569cf0e1..3eb6449046 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -58,7 +58,6 @@ class Chef :@force_default ] - OVERRIDE_COMPONENTS = [ :@override, :@role_override, @@ -146,7 +145,6 @@ class Chef METHOD_DEFN end - # return the cookbook level default attribute component attr_reader :default @@ -159,11 +157,6 @@ class Chef # return the force_default level attribute component attr_reader :force_default - # default! is the "advertised" method for force_default, but is - # implemented as an alias because instance variables can't (easily) have - # +!+ characters. - alias :default! :force_default - # return the "normal" level attribute component attr_reader :normal @@ -179,11 +172,6 @@ class Chef # return the force override level attribute component attr_reader :force_override - # +override!+ is the "advertised" method for +force_override+ but is - # implemented as an alias because instance variables can't easily have - # +!+ characters. - alias :override! :force_override - # return the automatic level attribute component attr_reader :automatic @@ -311,6 +299,100 @@ class Chef @automatic = VividMash.new(self, new_data) end + # + # Deleting attributes + # + + # clears attributes from all precedence levels + def rm(*args) + # just easier to compute our retval, rather than collect+merge sub-retvals + ret = args.inject(merged_attributes) do |attr, arg| + if attr.nil? || !attr.respond_to?(:[]) + nil + else + begin + attr[arg] + rescue TypeError + raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)" + end + end + end + rm_default(*args) + rm_normal(*args) + rm_override(*args) + ret + end + + # does <level>['foo']['bar'].delete('baz') + def remove_from_precedence_level(level, *args, key) + multimash = level.element(*args) + multimash.nil? ? nil : multimash.delete(key) + end + + private :remove_from_precedence_level + + # clears attributes from all default precedence levels + # + # equivalent to: force_default!['foo']['bar'].delete('baz') + def rm_default(*args) + reset + remove_from_precedence_level(force_default!(autovivify: false), *args) + end + + # clears attributes from normal precedence + # + # equivalent to: normal!['foo']['bar'].delete('baz') + def rm_normal(*args) + reset + remove_from_precedence_level(normal!(autovivify: false), *args) + end + + # clears attributes from all override precedence levels + # + # equivalent to: force_override!['foo']['bar'].delete('baz') + def rm_override(*args) + reset + remove_from_precedence_level(force_override!(autovivify: false), *args) + end + + # + # Replacing attributes without merging + # + + # sets default attributes without merging + def default!(opts={}) + reset + MultiMash.new(self, @default, [], opts) + end + + # sets normal attributes without merging + def normal!(opts={}) + reset + MultiMash.new(self, @normal, [], opts) + end + + # sets override attributes without merging + def override!(opts={}) + reset + MultiMash.new(self, @override, [], opts) + end + + # clears from all default precedence levels and then sets force_default + def force_default!(opts={}) + reset + MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts) + end + + # clears from all override precedence levels and then sets force_override + def force_override!(opts={}) + reset + MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts) + end + + # + # Accessing merged attributes + # + def merged_attributes @merged_attributes ||= begin components = [merge_defaults, @normal, merge_overrides, @automatic] @@ -391,7 +473,6 @@ class Chef end end - end end diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb index f09b02b106..c8bc618762 100644 --- a/lib/chef/node/attribute_collections.rb +++ b/lib/chef/node/attribute_collections.rb @@ -209,5 +209,117 @@ class Chef end + # == MultiMash + # This is a Hash-like object that contains multiple VividMashes in it. Its + # purpose is so that the user can descend into the mash and delete a subtree + # from all of the Mash objects (used to delete all values in a subtree from + # default, force_default, role_default and env_default at the same time). The + # assignment operator strictly does assignment (does no merging) and works + # by deleting the subtree and then assigning to the last mash which passed in + # the initializer. + # + # A lot of the complexity of this class comes from the fact that at any key + # value some or all of the mashes may walk off their ends and become nil or + # true or something. The schema may change so that one precidence leve may + # be 'true' object and another may be a VividMash. It is also possible that + # one or many of them may transition from VividMashes to Hashes or Arrays. + # + # It also supports the case where you may be deleting a key using node.rm + # in which case if intermediate keys all walk off into nil then you don't want + # to be autovivifying keys as you go. On the other hand you may be using + # node.force_default! in which case you'll wind up with a []= operator at the + # end and you want autovivification, so we conditionally have to support either + # operation. + # + # @todo: can we have an autovivify class that decorates a class that doesn't + # autovivify or something so that the code is less awful? + # + class MultiMash + attr_reader :root + attr_reader :mashes + attr_reader :opts + attr_reader :primary_mash + + # Initialize with an array of mashes. For the delete return value to work + # properly the mashes must come from the same attribute level (i.e. all + # override or all default, but not a mix of both). + def initialize(root, primary_mash, mashes, opts={}) + @root = root + @primary_mash = primary_mash + @mashes = mashes + @opts = opts + @opts[:autovivify] = true if @opts[:autovivify].nil? + end + + def [](key) + # handle the secondary mashes + new_mashes = [] + mashes.each do |mash| + new_mash = safe_evalute_key(mash, key) + # secondary mashes never autovivify so once they fall into nil, we just stop tracking them + new_mashes.push(new_mash) unless new_mash.nil? + end + + new_primary_mash = safe_evalute_key(primary_mash, key) + + if new_primary_mash.nil? && @opts[:autovivify] + primary_mash[key] = VividMash.new(root) + new_primary_mash = primary_mash[key] + end + + MultiMash.new(root, new_primary_mash, new_mashes, opts) + end + + def []=(key, value) + if primary_mash.nil? + # This theoretically should never happen since node#force_default! setter methods will autovivify and + # node#rm methods do not end in #[]= operators. + raise TypeError, "No autovivification was specified initially on a method chain ending in assignment" + end + ret = delete(key) + primary_mash[key] = value + ret + end + + # mash.element('foo', 'bar') is the same as mash['foo']['bar'] + def element(key = nil, *subkeys) + return self if key.nil? + submash = self[key] + subkeys.empty? ? submash : submash.element(*subkeys) + end + + def delete(key) + # the return value is a deep merge which is correct semantics when + # merging between attributes on the same level (this would be incorrect + # if passed both override and default attributes which would need hash_only + # merging). + ret = mashes.inject(Mash.new) do |merged, mash| + Chef::Mixin::DeepMerge.merge(merged, mash) + end + ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash) + mashes.each do |mash| + mash.delete(key) if mash.respond_to?(:delete) + end + primary_mash.delete(key) if primary_mash.respond_to?(:delete) + ret[key] + end + + private + + def safe_evalute_key(mash, key) + if mash.respond_to?(:[]) + if mash.respond_to?(:has_key?) + if mash.has_key?(key) + return mash[key] if mash[key].respond_to?(:[]) + end + elsif !mash[key].nil? + return mash[key] if mash[key].respond_to?(:[]) + end + end + return nil + end + + end + end end diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb index 5325117d6c..3b3a81f3a6 100644 --- a/spec/unit/node/attribute_spec.rb +++ b/spec/unit/node/attribute_spec.rb @@ -285,7 +285,7 @@ describe Chef::Node::Attribute do end it "prefers 'forced default' over any other default" do - @attributes.default!["default"] = "force default" + @attributes.force_default["default"] = "force default" @attributes.role_default["default"] = "role default" @attributes.env_default["default"] = "environment default" @attributes["default"].should == "force default" @@ -307,7 +307,7 @@ describe Chef::Node::Attribute do end it "prefers 'forced overrides' over role or cookbook overrides" do - @attributes.override!["override"] = "force override" + @attributes.force_override["override"] = "force override" @attributes.env_override["override"] = "environment override" @attributes.role_override["override"] = "role override" @attributes["override"].should == "force override" @@ -939,7 +939,6 @@ describe Chef::Node::Attribute do end - describe "values" do before do @attributes = Chef::Node::Attribute.new( @@ -1185,4 +1184,3 @@ describe Chef::Node::Attribute do end end - diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index da7a67aec1..695da8d671 100644 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -247,13 +247,6 @@ describe Chef::Node do node.default.fuu.bahrr.baz = "qux" node.fuu.bahrr.baz.should == "qux" end - - it "accesses force defaults via default!" do - node.default![:foo] = "wet bar" - node.default[:foo] = "bar" - node[:foo].should == "wet bar" - end - end describe "override attributes" do @@ -292,13 +285,330 @@ describe Chef::Node do node.override.fuu.bahrr.baz = "qux" node.fuu.bahrr.baz.should == "qux" end + end + + describe "globally deleting attributes" do + context "with hash values" do + before do + node.role_default["mysql"]["server"]["port"] = 1234 + node.normal["mysql"]["server"]["port"] = 2345 + node.override["mysql"]["server"]["port"] = 3456 + end + + it "deletes all the values and returns the value with the highest precidence" do + expect( node.rm("mysql", "server", "port") ).to eql(3456) + expect( node["mysql"]["server"]["port"] ).to be_nil + expect( node["mysql"]["server"] ).to eql({}) + end + + it "deletes nested things correctly" do + node.default["mysql"]["client"]["client_setting"] = "foo" + expect( node.rm("mysql", "server") ).to eql( {"port" => 3456} ) + expect( node["mysql"] ).to eql( { "client" => { "client_setting" => "foo" } } ) + end + + it "returns nil if the node attribute does not exist" do + expect( node.rm("no", "such", "thing") ).to be_nil + end - it "sets force_overrides via override!" do - node.override![:foo] = "wet bar" - node.override[:foo] = "bar" - node[:foo].should == "wet bar" + it "can delete the entire tree" do + expect( node.rm("mysql") ).to eql({"server"=>{"port"=>3456}}) + end end + context "when trying to delete through a thing that isn't an array-like or hash-like object" do + before do + node.default["mysql"] = true + end + + it "returns nil when you're two levels deeper" do + expect( node.rm("mysql", "server", "port") ).to eql(nil) + end + + it "returns nil when you're one level deeper" do + expect( node.rm("mysql", "server") ).to eql(nil) + end + + it "correctly deletes at the top level" do + expect( node.rm("mysql") ).to eql(true) + end + end + + context "with array indexes" do + before do + node.role_default["mysql"]["server"][0]["port"] = 1234 + node.normal["mysql"]["server"][0]["port"] = 2345 + node.override["mysql"]["server"][0]["port"] = 3456 + node.override["mysql"]["server"][1]["port"] = 3456 + end + + it "deletes the array element" do + expect( node.rm("mysql", "server", 0, "port") ).to eql(3456) + expect( node["mysql"]["server"][0]["port"] ).to be_nil + expect( node["mysql"]["server"][1]["port"] ).to eql(3456) + end + end + + context "with real arrays" do + before do + node.role_default["mysql"]["server"] = [ { + "port" => 1234, + } ] + node.normal["mysql"]["server"] = [ { + "port" => 2345, + } ] + node.override["mysql"]["server"] = [ { + "port" => 3456, + } ] + end + + it "deletes the array element" do + expect( node.rm("mysql", "server", 0, "port") ).to eql(3456) + expect( node["mysql"]["server"][0]["port"] ).to be_nil + end + + it "does not have a horrible error message when mistaking arrays for hashes" do + expect { node.rm("mysql", "server", "port") }.to raise_error(TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)") + end + end + end + + describe "granular deleting attributes" do + context "when only defaults exist" do + before do + node.role_default["mysql"]["server"]["port"] = 1234 + node.default["mysql"]["server"]["port"] = 2345 + node.force_default["mysql"]["server"]["port"] = 3456 + end + + it "returns the deleted values" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + end + + it "returns nil for the combined attribues" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + expect( node["mysql"]["server"]["port"] ).to eql(nil) + end + + it "returns an empty hash for the default attrs" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + # this auto-vivifies, should it? + expect( node.default_attrs["mysql"]["server"]["port"] ).to eql({}) + end + + it "returns an empty hash after the last key is deleted" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + expect( node["mysql"]["server"] ).to eql({}) + end + end + + context "when trying to delete through a thing that isn't an array-like or hash-like object" do + before do + node.default["mysql"] = true + end + + it "returns nil when you're two levels deeper" do + expect( node.rm_default("mysql", "server", "port") ).to eql(nil) + end + + it "returns nil when you're one level deeper" do + expect( node.rm_default("mysql", "server") ).to eql(nil) + end + + it "correctly deletes at the top level" do + expect( node.rm_default("mysql") ).to eql(true) + end + end + + context "when a higher precedence exists" do + before do + node.role_default["mysql"]["server"]["port"] = 1234 + node.default["mysql"]["server"]["port"] = 2345 + node.force_default["mysql"]["server"]["port"] = 3456 + + node.override["mysql"]["server"]["port"] = 9999 + end + + it "returns the deleted values" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + end + + it "returns the higher precedence values after the delete" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + expect( node["mysql"]["server"]["port"] ).to eql(9999) + end + + it "returns an empty has for the default attrs" do + expect( node.rm_default("mysql", "server", "port") ).to eql(3456) + # this auto-vivifies, should it? + expect( node.default_attrs["mysql"]["server"]["port"] ).to eql({}) + end + end + + context "when a lower precedence exists" do + before do + node.default["mysql"]["server"]["port"] = 2345 + node.override["mysql"]["server"]["port"] = 9999 + node.role_override["mysql"]["server"]["port"] = 9876 + node.force_override["mysql"]["server"]["port"] = 6669 + end + + it "returns the deleted values" do + expect( node.rm_override("mysql", "server", "port") ).to eql(6669) + end + + it "returns the lower precedence levels after the delete" do + expect( node.rm_override("mysql", "server", "port") ).to eql(6669) + expect( node["mysql"]["server"]["port"] ).to eql(2345) + end + + it "returns an empty has for the override attrs" do + expect( node.rm_override("mysql", "server", "port") ).to eql(6669) + # this auto-vivifies, should it? + expect( node.override_attrs["mysql"]["server"]["port"] ).to eql({}) + end + end + + it "rm_default returns nil on deleting non-existent values" do + expect( node.rm_default("no", "such", "thing") ).to be_nil + end + + it "rm_normal returns nil on deleting non-existent values" do + expect( node.rm_normal("no", "such", "thing") ).to be_nil + end + + it "rm_override returns nil on deleting non-existent values" do + expect( node.rm_override("no", "such", "thing") ).to be_nil + end + end + + describe "granular replacing attributes" do + it "removes everything at the level of the last key" do + node.default["mysql"]["server"]["port"] = 2345 + + node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" } + + expect( node["mysql"]["server"] ).to eql({ "data_dir" => "/my_raid_volume/lib/mysql" }) + end + + it "replaces a value at the cookbook sub-level of the atributes only" do + node.default["mysql"]["server"]["port"] = 2345 + node.default["mysql"]["server"]["service_name"] = "fancypants-sql" + node.role_default["mysql"]["server"]["port"] = 1234 + node.force_default["mysql"]["server"]["port"] = 3456 + + node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" } + + expect( node["mysql"]["server"]["port"] ).to eql(3456) + expect( node["mysql"]["server"]["service_name"] ).to be_nil + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + expect( node["mysql"]["server"] ).to eql({ "port" => 3456, "data_dir" => "/my_raid_volume/lib/mysql" }) + end + + it "higher precedence values aren't removed" do + node.role_default["mysql"]["server"]["port"] = 1234 + node.default["mysql"]["server"]["port"] = 2345 + node.force_default["mysql"]["server"]["port"] = 3456 + node.override["mysql"]["server"]["service_name"] = "fancypants-sql" + + node.default!["mysql"]["server"] = { "data_dir" => "/my_raid_volume/lib/mysql" } + + expect( node["mysql"]["server"]["port"] ).to eql(3456) + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + expect( node["mysql"]["server"] ).to eql({ "service_name" => "fancypants-sql", "port" => 3456, "data_dir" => "/my_raid_volume/lib/mysql" }) + end + end + + describe "granular force replacing attributes" do + it "removes everything at the level of the last key" do + node.force_default["mysql"]["server"]["port"] = 2345 + + node.force_default!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + + expect( node["mysql"]["server"] ).to eql({ + "data_dir" => "/my_raid_volume/lib/mysql", + }) + end + + it "removes all values from the precedence level when setting" do + node.role_default["mysql"]["server"]["port"] = 1234 + node.default["mysql"]["server"]["port"] = 2345 + node.force_default["mysql"]["server"]["port"] = 3456 + + node.force_default!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + + expect( node["mysql"]["server"]["port"] ).to be_nil + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + expect( node["mysql"]["server"] ).to eql({ + "data_dir" => "/my_raid_volume/lib/mysql", + }) + end + + it "higher precedence levels are not removed" do + node.role_default["mysql"]["server"]["port"] = 1234 + node.default["mysql"]["server"]["port"] = 2345 + node.force_default["mysql"]["server"]["port"] = 3456 + node.override["mysql"]["server"]["service_name"] = "fancypants-sql" + + node.force_default!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + + expect( node["mysql"]["server"]["port"] ).to be_nil + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + expect( node["mysql"]["server"] ).to eql({ + "service_name" => "fancypants-sql", + "data_dir" => "/my_raid_volume/lib/mysql", + }) + end + + it "will autovivify" do + node.force_default!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + end + + it "lower precedence levels aren't removed" do + node.role_override["mysql"]["server"]["port"] = 1234 + node.override["mysql"]["server"]["port"] = 2345 + node.force_override["mysql"]["server"]["port"] = 3456 + node.default["mysql"]["server"]["service_name"] = "fancypants-sql" + + node.force_override!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + + expect( node["mysql"]["server"]["port"] ).to be_nil + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + expect( node["mysql"]["server"] ).to eql({ + "service_name" => "fancypants-sql", + "data_dir" => "/my_raid_volume/lib/mysql", + }) + end + + it "when overwriting a non-hash/array" do + node.override["mysql"] = false + node.force_override["mysql"] = true + node.force_override!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + expect( node["mysql"]["server"]["data_dir"] ).to eql("/my_raid_volume/lib/mysql") + end + + it "when overwriting an array with a hash" do + node.force_override["mysql"][0] = true + node.force_override!["mysql"]["server"] = { + "data_dir" => "/my_raid_volume/lib/mysql", + } + expect( node["mysql"]["server"] ).to eql({ + "data_dir" => "/my_raid_volume/lib/mysql", + }) + end end it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do @@ -536,7 +846,6 @@ describe Chef::Node do @expansion.default_attrs.replace({:default => "from role", :d_role => "role only"}) @expansion.override_attrs.replace({:override => "from role", :o_role => "role only"}) - @environment = Chef::Environment.new @environment.default_attributes = {:default => "from env", :d_env => "env only" } @environment.override_attributes = {:override => "from env", :o_env => "env only"} @@ -753,7 +1062,6 @@ describe Chef::Node do node_for_json["default"]["env default"].should == "env default" end - it "should deserialize itself from json", :json => true do node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA)) json = Chef::JSONCompat.to_json(node) |