spec/unit/node_spec.rb in microwave-1.0.4 vs spec/unit/node_spec.rb in microwave-11.400.2
- old
+ new
@@ -18,15 +18,16 @@
require 'spec_helper'
require 'ostruct'
describe Chef::Node do
- before(:each) do
- Chef::Config.node_path(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes")))
- @node = Chef::Node.new()
- end
+ let(:node) { Chef::Node.new() }
+ let(:platform_introspector) { node }
+
+ it_behaves_like "a platform introspector"
+
it "creates a node and assigns it a name" do
node = Chef::Node.build('solo-node')
node.name.should == 'solo-node'
end
@@ -37,41 +38,38 @@
describe "when the node does not exist on the server" do
before do
response = OpenStruct.new(:code => '404')
exception = Net::HTTPServerException.new("404 not found", response)
Chef::Node.stub!(:load).and_raise(exception)
- @node.name("created-node")
+ node.name("created-node")
end
it "creates a new node for find_or_create" do
- Chef::Node.stub!(:new).and_return(@node)
- @node.should_receive(:create).and_return(@node)
+ Chef::Node.stub!(:new).and_return(node)
+ node.should_receive(:create).and_return(node)
node = Chef::Node.find_or_create("created-node")
node.name.should == 'created-node'
- node.should equal(@node)
+ node.should equal(node)
end
end
describe "when the node exists on the server" do
before do
- @node.name('existing-node')
- Chef::Node.stub!(:load).and_return(@node)
+ node.name('existing-node')
+ Chef::Node.stub!(:load).and_return(node)
end
it "loads the node via the REST API for find_or_create" do
- Chef::Node.find_or_create('existing-node').should equal(@node)
+ Chef::Node.find_or_create('existing-node').should equal(node)
end
end
describe "run_state" do
- it "should have a template_cache hash" do
- @node.run_state[:template_cache].should be_a_kind_of(Hash)
+ it "is an empty hash" do
+ node.run_state.should respond_to(:keys)
+ node.run_state.should be_empty
end
-
- it "should have a seen_recipes hash" do
- @node.run_state[:seen_recipes].should be_a_kind_of(Hash)
- end
end
describe "initialize" do
it "should default to the '_default' chef_environment" do
n = Chef::Node.new
@@ -79,184 +77,229 @@
end
end
describe "name" do
it "should allow you to set a name with name(something)" do
- lambda { @node.name("latte") }.should_not raise_error
+ lambda { node.name("latte") }.should_not raise_error
end
it "should return the name with name()" do
- @node.name("latte")
- @node.name.should eql("latte")
+ node.name("latte")
+ node.name.should eql("latte")
end
it "should always have a string for name" do
- lambda { @node.name(Hash.new) }.should raise_error(ArgumentError)
+ lambda { node.name(Hash.new) }.should raise_error(ArgumentError)
end
it "cannot be blank" do
- lambda { @node.name("")}.should raise_error(Chef::Exceptions::ValidationFailed)
+ lambda { node.name("")}.should raise_error(Chef::Exceptions::ValidationFailed)
end
it "should not accept name doesn't match /^[\-[:alnum:]_:.]+$/" do
- lambda { @node.name("space in it")}.should raise_error(Chef::Exceptions::ValidationFailed)
+ lambda { node.name("space in it")}.should raise_error(Chef::Exceptions::ValidationFailed)
end
end
describe "chef_environment" do
it "should set an environment with chef_environment(something)" do
- lambda { @node.chef_environment("latte") }.should_not raise_error
+ lambda { node.chef_environment("latte") }.should_not raise_error
end
it "should return the chef_environment with chef_environment()" do
- @node.chef_environment("latte")
- @node.chef_environment.should == "latte"
+ node.chef_environment("latte")
+ node.chef_environment.should == "latte"
end
it "should disallow non-strings" do
- lambda { @node.chef_environment(Hash.new) }.should raise_error(ArgumentError)
- lambda { @node.chef_environment(42) }.should raise_error(ArgumentError)
+ lambda { node.chef_environment(Hash.new) }.should raise_error(ArgumentError)
+ lambda { node.chef_environment(42) }.should raise_error(ArgumentError)
end
it "cannot be blank" do
- lambda { @node.chef_environment("")}.should raise_error(Chef::Exceptions::ValidationFailed)
+ lambda { node.chef_environment("")}.should raise_error(Chef::Exceptions::ValidationFailed)
end
end
describe "attributes" do
- it "should be loaded from the node's cookbooks" do
- @cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
- @node.cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(@cookbook_repo))
- @node.load_attributes
- @node.ldap_server.should eql("ops1prod")
- @node.ldap_basedn.should eql("dc=hjksolutions,dc=com")
- @node.ldap_replication_password.should eql("forsure")
- @node.smokey.should eql("robinson")
- end
-
it "should have attributes" do
- @node.attribute.should be_a_kind_of(Hash)
+ node.attribute.should be_a_kind_of(Hash)
end
it "should allow attributes to be accessed by name or symbol directly on node[]" do
- @node.attribute["locust"] = "something"
- @node[:locust].should eql("something")
- @node["locust"].should eql("something")
+ node.default["locust"] = "something"
+ node[:locust].should eql("something")
+ node["locust"].should eql("something")
end
it "should return nil if it cannot find an attribute with node[]" do
- @node["secret"].should eql(nil)
+ node["secret"].should eql(nil)
end
- it "should allow you to set an attribute via node[]=" do
- @node["secret"] = "shush"
- @node["secret"].should eql("shush")
+ it "does not allow you to set an attribute via node[]=" do
+ lambda { node["secret"] = "shush" }.should raise_error(Chef::Exceptions::ImmutableAttributeModification)
end
it "should allow you to query whether an attribute exists with attribute?" do
- @node.attribute["locust"] = "something"
- @node.attribute?("locust").should eql(true)
- @node.attribute?("no dice").should eql(false)
+ node.default["locust"] = "something"
+ node.attribute?("locust").should eql(true)
+ node.attribute?("no dice").should eql(false)
end
it "should let you go deep with attribute?" do
- @node.set["battles"]["people"]["wonkey"] = true
- @node["battles"]["people"].attribute?("wonkey").should == true
- @node["battles"]["people"].attribute?("snozzberry").should == false
+ node.set["battles"]["people"]["wonkey"] = true
+ node["battles"]["people"].attribute?("wonkey").should == true
+ node["battles"]["people"].attribute?("snozzberry").should == false
end
- it "should allow you to set an attribute via method_missing" do
- @node.sunshine "is bright"
- @node.attribute[:sunshine].should eql("is bright")
+ it "does not allow you to set an attribute via method_missing" do
+ lambda { node.sunshine = "is bright"}.should raise_error(Chef::Exceptions::ImmutableAttributeModification)
end
it "should allow you get get an attribute via method_missing" do
- @node.sunshine "is bright"
- @node.sunshine.should eql("is bright")
+ node.default.sunshine = "is bright"
+ node.sunshine.should eql("is bright")
end
describe "normal attributes" do
it "should allow you to set an attribute with set, without pre-declaring a hash" do
- @node.set[:snoopy][:is_a_puppy] = true
- @node[:snoopy][:is_a_puppy].should == true
+ node.set[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
end
it "should allow you to set an attribute with set_unless" do
- @node.set_unless[:snoopy][:is_a_puppy] = false
- @node[:snoopy][:is_a_puppy].should == false
+ node.set_unless[:snoopy][:is_a_puppy] = false
+ node[:snoopy][:is_a_puppy].should == false
end
it "should not allow you to set an attribute with set_unless if it already exists" do
- @node.set[:snoopy][:is_a_puppy] = true
- @node.set_unless[:snoopy][:is_a_puppy] = false
- @node[:snoopy][:is_a_puppy].should == true
+ node.set[:snoopy][:is_a_puppy] = true
+ node.set_unless[:snoopy][:is_a_puppy] = false
+ node[:snoopy][:is_a_puppy].should == true
end
+ it "should allow you to set a value after a set_unless" do
+ # this tests for set_unless_present state bleeding between statements CHEF-3806
+ node.set_unless[:snoopy][:is_a_puppy] = false
+ node.set[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
+ end
+
+ it "should let you set a value after a 'dangling' set_unless" do
+ # this tests for set_unless_present state bleeding between statements CHEF-3806
+ node.set[:snoopy][:is_a_puppy] = "what"
+ node.set_unless[:snoopy][:is_a_puppy]
+ node.set[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
+ end
+
it "auto-vivifies attributes created via method syntax" do
- @node.set.fuu.bahrr.baz = "qux"
- @node.fuu.bahrr.baz.should == "qux"
+ node.set.fuu.bahrr.baz = "qux"
+ node.fuu.bahrr.baz.should == "qux"
end
end
describe "default attributes" do
it "should be set with default, without pre-declaring a hash" do
- @node.default[:snoopy][:is_a_puppy] = true
- @node[:snoopy][:is_a_puppy].should == true
+ node.default[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
end
it "should allow you to set with default_unless without pre-declaring a hash" do
- @node.default_unless[:snoopy][:is_a_puppy] = false
- @node[:snoopy][:is_a_puppy].should == false
+ node.default_unless[:snoopy][:is_a_puppy] = false
+ node[:snoopy][:is_a_puppy].should == false
end
it "should not allow you to set an attribute with default_unless if it already exists" do
- @node.default[:snoopy][:is_a_puppy] = true
- @node.default_unless[:snoopy][:is_a_puppy] = false
- @node[:snoopy][:is_a_puppy].should == true
+ node.default[:snoopy][:is_a_puppy] = true
+ node.default_unless[:snoopy][:is_a_puppy] = false
+ node[:snoopy][:is_a_puppy].should == true
end
+ it "should allow you to set a value after a default_unless" do
+ # this tests for set_unless_present state bleeding between statements CHEF-3806
+ node.default_unless[:snoopy][:is_a_puppy] = false
+ node.default[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
+ end
+
+ it "should allow you to set a value after a 'dangling' default_unless" do
+ # this tests for set_unless_present state bleeding between statements CHEF-3806
+ node.default[:snoopy][:is_a_puppy] = "what"
+ node.default_unless[:snoopy][:is_a_puppy]
+ node.default[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
+ end
+
it "auto-vivifies attributes created via method syntax" do
- @node.default.fuu.bahrr.baz = "qux"
- @node.fuu.bahrr.baz.should == "qux"
+ 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
it "should be set with override, without pre-declaring a hash" do
- @node.override[:snoopy][:is_a_puppy] = true
- @node[:snoopy][:is_a_puppy].should == true
+ node.override[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
end
it "should allow you to set with override_unless without pre-declaring a hash" do
- @node.override_unless[:snoopy][:is_a_puppy] = false
- @node[:snoopy][:is_a_puppy].should == false
+ node.override_unless[:snoopy][:is_a_puppy] = false
+ node[:snoopy][:is_a_puppy].should == false
end
it "should not allow you to set an attribute with override_unless if it already exists" do
- @node.override[:snoopy][:is_a_puppy] = true
- @node.override_unless[:snoopy][:is_a_puppy] = false
- @node[:snoopy][:is_a_puppy].should == true
+ node.override[:snoopy][:is_a_puppy] = true
+ node.override_unless[:snoopy][:is_a_puppy] = false
+ node[:snoopy][:is_a_puppy].should == true
end
+ it "should allow you to set a value after an override_unless" do
+ # this tests for set_unless_present state bleeding between statements CHEF-3806
+ node.override_unless[:snoopy][:is_a_puppy] = false
+ node.override[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
+ end
+
+ it "should allow you to set a value after a 'dangling' override_unless" do
+ # this tests for set_unless_present state bleeding between statements CHEF-3806
+ node.override_unless[:snoopy][:is_a_puppy] = "what"
+ node.override_unless[:snoopy][:is_a_puppy]
+ node.override[:snoopy][:is_a_puppy] = true
+ node[:snoopy][:is_a_puppy].should == true
+ end
+
it "auto-vivifies attributes created via method syntax" do
- @node.override.fuu.bahrr.baz = "qux"
- @node.fuu.bahrr.baz.should == "qux"
+ node.override.fuu.bahrr.baz = "qux"
+ node.fuu.bahrr.baz.should == "qux"
end
+ it "sets force_overrides via override!" do
+ node.override![:foo] = "wet bar"
+ node.override[:foo] = "bar"
+ node[:foo].should == "wet bar"
+ end
+
end
it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do
- lambda { @node.sunshine }.should raise_error(ArgumentError)
+ lambda { node.sunshine }.should raise_error(NoMethodError)
end
it "should allow you to iterate over attributes with each_attribute" do
- @node.sunshine "is bright"
- @node.canada "is a nice place"
+ node.default.sunshine = "is bright"
+ node.default.canada = "is a nice place"
seen_attributes = Hash.new
- @node.each_attribute do |a,v|
+ node.each_attribute do |a,v|
seen_attributes[a] = v
end
seen_attributes.should have_key("sunshine")
seen_attributes.should have_key("canada")
seen_attributes["sunshine"].should == "is bright"
@@ -270,231 +313,319 @@
@ohai_data = {:platform => 'foo', :platform_version => 'bar'}
end
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]" ]
+ node.consume_run_list(attrs).should == {"foo" => "bar"}
+ node.run_list.should == [ "role[base]", "recipe[chef::server]" ]
end
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.run_list.should == [ "three" ]
+ node.consume_run_list "recipes" => [ "one", "two" ]
+ node.consume_run_list "recipes" => [ "three" ]
+ node.run_list.should == [ "three" ]
end
it "should not add duplicate recipes from the json attributes" do
- @node.run_list << "one"
- @node.consume_run_list "recipes" => [ "one", "two", "three" ]
- @node.run_list.should == [ "one", "two", "three" ]
+ node.run_list << "one"
+ node.consume_run_list "recipes" => [ "one", "two", "three" ]
+ node.run_list.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]"]
+ 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)
+ 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_external_attrs(@ohai_data, {"one" => "two", "three" => "four"})
- @node.one.should eql("two")
- @node.three.should eql("four")
+ node.consume_external_attrs(@ohai_data, {"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_external_attrs(@ohai_data, {})
- @node.tags.should eql([])
+ node.consume_external_attrs(@ohai_data, {})
+ node.tags.should eql([])
end
it "should not set the tags attribute to an empty array if it is already defined" do
- @node[:tags] = [ "radiohead" ]
- @node.consume_external_attrs(@ohai_data, {})
- @node.tags.should eql([ "radiohead" ])
+ node.normal[:tags] = [ "radiohead" ]
+ node.consume_external_attrs(@ohai_data, {})
+ node.tags.should eql([ "radiohead" ])
end
it "deep merges attributes instead of overwriting them" do
- @node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "four"}})
- @node.one.to_hash.should == {"two" => {"three" => "four"}}
- @node.consume_external_attrs(@ohai_data, "one" => {"abc" => "123"})
- @node.consume_external_attrs(@ohai_data, "one" => {"two" => {"foo" => "bar"}})
- @node.one.to_hash.should == {"two" => {"three" => "four", "foo" => "bar"}, "abc" => "123"}
+ node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "four"}})
+ node.one.to_hash.should == {"two" => {"three" => "four"}}
+ node.consume_external_attrs(@ohai_data, "one" => {"abc" => "123"})
+ node.consume_external_attrs(@ohai_data, "one" => {"two" => {"foo" => "bar"}})
+ node.one.to_hash.should == {"two" => {"three" => "four", "foo" => "bar"}, "abc" => "123"}
end
it "gives attributes from JSON priority when deep merging" do
- @node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "four"}})
- @node.one.to_hash.should == {"two" => {"three" => "four"}}
- @node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "forty-two"}})
- @node.one.to_hash.should == {"two" => {"three" => "forty-two"}}
+ node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "four"}})
+ node.one.to_hash.should == {"two" => {"three" => "four"}}
+ node.consume_external_attrs(@ohai_data, "one" => {"two" => {"three" => "forty-two"}})
+ node.one.to_hash.should == {"two" => {"three" => "forty-two"}}
end
end
describe "preparing for a chef client run" do
before do
@ohai_data = {:platform => 'foobuntu', :platform_version => '23.42'}
end
it "sets its platform according to platform detection" do
- @node.consume_external_attrs(@ohai_data, {})
- @node.automatic_attrs[:platform].should == 'foobuntu'
- @node.automatic_attrs[:platform_version].should == '23.42'
+ node.consume_external_attrs(@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.consume_external_attrs(@ohai_data, {"run_list" => ['recipe[unicorn]']})
- @node.run_list.should == ['recipe[unicorn]']
+ node.consume_external_attrs(@ohai_data, {"run_list" => ['recipe[unicorn]']})
+ node.run_list.should == ['recipe[unicorn]']
end
it "saves non-runlist json attrs for later" do
expansion = Chef::RunList::RunListExpansion.new('_default', [])
- @node.run_list.stub!(:expand).and_return(expansion)
- @node.consume_external_attrs(@ohai_data, {"foo" => "bar"})
- @node.expand!
- @node.normal_attrs.should == {"foo" => "bar", "tags" => []}
+ node.run_list.stub!(:expand).and_return(expansion)
+ node.consume_external_attrs(@ohai_data, {"foo" => "bar"})
+ node.expand!
+ node.normal_attrs.should == {"foo" => "bar", "tags" => []}
end
end
describe "when expanding its run list and merging attributes" do
before do
- @expansion = Chef::RunList::RunListExpansion.new("_default", [])
- @node.run_list.stub!(:expand).and_return(@expansion)
+ @environment = Chef::Environment.new.tap do |e|
+ e.name('rspec_env')
+ e.default_attributes("env default key" => "env default value")
+ e.override_attributes("env override key" => "env override value")
+ end
+ Chef::Environment.should_receive(:load).with("rspec_env").and_return(@environment)
+ @expansion = Chef::RunList::RunListExpansion.new("rspec_env", [])
+ node.chef_environment("rspec_env")
+ node.run_list.stub!(:expand).and_return(@expansion)
end
it "sets the 'recipes' automatic attribute to the recipes in the expanded run_list" do
@expansion.recipes << 'recipe[chef::client]' << 'recipe[nginx::default]'
- @node.expand!
- @node.automatic_attrs[:recipes].should == ['recipe[chef::client]', 'recipe[nginx::default]']
+ node.expand!
+ node.automatic_attrs[:recipes].should == ['recipe[chef::client]', 'recipe[nginx::default]']
end
it "sets the 'roles' automatic attribute to the expanded role list" do
@expansion.instance_variable_set(:@applied_roles, {'arf' => nil, 'countersnark' => nil})
- @node.expand!
- @node.automatic_attrs[:roles].sort.should == ['arf', 'countersnark']
+ node.expand!
+ node.automatic_attrs[:roles].sort.should == ['arf', 'countersnark']
end
+ it "applies default attributes from the environment as environment defaults" do
+ node.expand!
+ node.attributes.env_default["env default key"].should == "env default value"
+ end
+
+ it "applies override attributes from the environment as env overrides" do
+ node.expand!
+ node.attributes.env_override["env override key"].should == "env override value"
+ end
+
+ it "applies default attributes from roles as role defaults" do
+ @expansion.default_attrs["role default key"] = "role default value"
+ node.expand!
+ node.attributes.role_default["role default key"].should == "role default value"
+ end
+
+ it "applies override attributes from roles as role overrides" do
+ @expansion.override_attrs["role override key"] = "role override value"
+ node.expand!
+ node.attributes.role_override["role override key"].should == "role override value"
+ end
end
- # TODO: timh, cw: 2010-5-19: Node.recipe? deprecated. See node.rb
- # describe "recipes" do
- # it "should have a RunList of recipes that should be applied" do
- # @node.recipes.should be_a_kind_of(Chef::RunList)
- # end
- #
- # it "should allow you to query whether or not it has a recipe applied with recipe?" do
- # @node.recipes << "sunrise"
- # @node.recipe?("sunrise").should eql(true)
- # @node.recipe?("not at home").should eql(false)
- # end
- #
- # it "should allow you to query whether or not a recipe has been applied, even if it was included" do
- # @node.run_state[:seen_recipes]["snakes"] = true
- # @node.recipe?("snakes").should eql(true)
- # end
- #
- # it "should return false if a recipe has not been seen" do
- # @node.recipe?("snakes").should eql(false)
- # end
- #
- # it "should allow you to set recipes with arguments" do
- # @node.recipes "one", "two"
- # @node.recipe?("one").should eql(true)
- # @node.recipe?("two").should eql(true)
- # end
- # end
+ describe "when querying for recipes in the run list" do
+ context "when a recipe is in the top level run list" do
+ before do
+ node.run_list << "recipe[nginx::module]"
+ end
+ it "finds the recipe" do
+ node.recipe?("nginx::module").should be_true
+ end
+
+ it "does not find a recipe not in the run list" do
+ node.recipe?("nginx::other_module").should be_false
+ end
+ end
+ context "when a recipe is in the expanded run list only" do
+ before do
+ node.run_list << "role[base]"
+ node.automatic_attrs[:recipes] = [ "nginx::module" ]
+ end
+
+ it "finds a recipe in the expanded run list" do
+ node.recipe?("nginx::module").should be_true
+ end
+
+ it "does not find a recipe that's not in the run list" do
+ node.recipe?("nginx::other_module").should be_false
+ end
+ end
+ end
+
+ describe "when clearing computed state at the beginning of a run" do
+ before do
+ node.default[:foo] = "default"
+ node.normal[:foo] = "normal"
+ node.override[:foo] = "override"
+ node.reset_defaults_and_overrides
+ end
+
+ it "removes default attributes" do
+ node.default.should be_empty
+ end
+
+ it "removes override attributes" do
+ node.override.should be_empty
+ end
+
+ it "leaves normal level attributes untouched" do
+ node[:foo].should == "normal"
+ end
+
+ end
+
+ describe "when merging environment attributes" do
+ before do
+ node.chef_environment = "rspec"
+ @expansion = Chef::RunList::RunListExpansion.new("rspec", [])
+ @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"}
+ Chef::Environment.stub!(:load).and_return(@environment)
+ node.apply_expansion_attributes(@expansion)
+ end
+
+ it "does not nuke role-only default attrs" do
+ node[:d_role].should == "role only"
+ end
+
+ it "does not nuke role-only override attrs" do
+ node[:o_role].should == "role only"
+ end
+
+ it "does not nuke env-only default attrs" do
+ node[:o_env].should == "env only"
+ end
+
+ it "does not nuke role-only override attrs" do
+ node[:o_env].should == "env only"
+ end
+
+ it "gives role defaults precedence over env defaults" do
+ node[:default].should == "from role"
+ end
+
+ it "gives env overrides precedence over role overrides" do
+ node[:override].should == "from env"
+ end
+ end
+
+ describe "when evaluating attributes files" do
+ before do
+ @cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
+ @cookbook_loader = Chef::CookbookLoader.new(@cookbook_repo)
+ @cookbook_loader.load_cookbooks
+
+ @cookbook_collection = Chef::CookbookCollection.new(@cookbook_loader.cookbooks_by_name)
+
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(node, @cookbook_collection, @events)
+
+ node.include_attribute("openldap::default")
+ node.include_attribute("openldap::smokey")
+ end
+
+ it "sets attributes from the files" do
+ node.ldap_server.should eql("ops1prod")
+ node.ldap_basedn.should eql("dc=hjksolutions,dc=com")
+ node.ldap_replication_password.should eql("forsure")
+ node.smokey.should eql("robinson")
+ end
+
+ it "gives a sensible error when attempting to load a missing attributes file" do
+ lambda { node.include_attribute("nope-this::doesnt-exist") }.should raise_error(Chef::Exceptions::CookbookNotFound)
+ end
+ end
+
describe "roles" do
it "should allow you to query whether or not it has a recipe applied with role?" do
- @node.run_list << "role[sunrise]"
- @node.role?("sunrise").should eql(true)
- @node.role?("not at home").should eql(false)
+ node.run_list << "role[sunrise]"
+ node.role?("sunrise").should eql(true)
+ node.role?("not at home").should eql(false)
end
it "should allow you to set roles with arguments" do
- @node.run_list << "role[one]"
- @node.run_list << "role[two]"
- @node.role?("one").should eql(true)
- @node.role?("two").should eql(true)
+ node.run_list << "role[one]"
+ node.run_list << "role[two]"
+ node.role?("one").should eql(true)
+ node.role?("two").should eql(true)
end
end
describe "run_list" do
it "should have a Chef::RunList of recipes and roles that should be applied" do
- @node.run_list.should be_a_kind_of(Chef::RunList)
+ node.run_list.should be_a_kind_of(Chef::RunList)
end
it "should allow you to query the run list with arguments" do
- @node.run_list "recipe[baz]"
- @node.run_list?("recipe[baz]").should eql(true)
+ node.run_list "recipe[baz]"
+ node.run_list?("recipe[baz]").should eql(true)
end
it "should allow you to set the run list with arguments" do
- @node.run_list "recipe[baz]", "role[foo]"
- @node.run_list?("recipe[baz]").should eql(true)
- @node.run_list?("role[foo]").should eql(true)
+ node.run_list "recipe[baz]", "role[foo]"
+ node.run_list?("recipe[baz]").should eql(true)
+ node.run_list?("role[foo]").should eql(true)
end
end
describe "from file" do
it "should load a node from a ruby file" do
- @node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb")))
- @node.name.should eql("test.example.com-short")
- @node.sunshine.should eql("in")
- @node.something.should eql("else")
- @node.recipes.should == ["operations-master", "operations-monitoring"]
+ node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb")))
+ node.name.should eql("test.example.com-short")
+ node.sunshine.should eql("in")
+ node.something.should eql("else")
+ node.run_list.should == ["operations-master", "operations-monitoring"]
end
it "should raise an exception if the file cannot be found or read" do
- lambda { @node.from_file("/tmp/monkeydiving") }.should raise_error(IOError)
+ lambda { node.from_file("/tmp/monkeydiving") }.should raise_error(IOError)
end
end
- describe "find_file" do
- it "should load a node from a file by fqdn" do
- @node.find_file("test.example.com")
- @node.name.should == "test.example.com"
- @node.chef_environment.should == "dev"
- end
-
- it "should load a node from a file by hostname" do
- File.stub!(:exists?).and_return(true)
- File.should_receive(:exists?).with(File.join(Chef::Config[:node_path], "test.example.com.rb")).and_return(false)
- @node.find_file("test.example.com")
- @node.name.should == "test.example.com-short"
- end
-
- it "should load a node from the default file" do
- File.stub!(:exists?).and_return(true)
- File.should_receive(:exists?).with(File.join(Chef::Config[:node_path], "test.example.com.rb")).and_return(false)
- File.should_receive(:exists?).with(File.join(Chef::Config[:node_path], "test.rb")).and_return(false)
- @node.find_file("test.example.com")
- @node.name.should == "test.example.com-default"
- end
-
- it "should raise an ArgumentError if it cannot find any node file at all" do
- File.stub!(:exists?).and_return(true)
- File.should_receive(:exists?).with(File.join(Chef::Config[:node_path], "test.example.com.rb")).and_return(false)
- File.should_receive(:exists?).with(File.join(Chef::Config[:node_path], "test.rb")).and_return(false)
- File.should_receive(:exists?).with(File.join(Chef::Config[:node_path], "default.rb")).and_return(false)
- lambda { @node.find_file("test.example.com") }.should raise_error(ArgumentError)
- end
- end
-
describe "update_from!" do
before(:each) do
- @node.name("orig")
- @node.chef_environment("dev")
- @node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } }
- @node.override_attrs = { "one" => { "two" => "three", "four" => "six" } }
- @node.normal_attrs = { "one" => { "two" => "seven" } }
- @node.run_list << "role[marxist]"
- @node.run_list << "role[leninist]"
- @node.run_list << "recipe[stalinist]"
+ node.name("orig")
+ node.chef_environment("dev")
+ node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } }
+ node.override_attrs = { "one" => { "two" => "three", "four" => "six" } }
+ node.normal_attrs = { "one" => { "two" => "seven" } }
+ node.run_list << "role[marxist]"
+ node.run_list << "role[leninist]"
+ node.run_list << "recipe[stalinist]"
@example = Chef::Node.new()
@example.name("newname")
@example.chef_environment("prod")
@example.default_attrs = { "alpha" => { "bravo" => "charlie", "delta" => "echo" } }
@@ -504,35 +635,35 @@
@example.run_list << "role[drama]"
@example.run_list << "recipe[mystery]"
end
it "allows update of everything except name" do
- @node.update_from!(@example)
- @node.name.should == "orig"
- @node.chef_environment.should == @example.chef_environment
- @node.default_attrs.should == @example.default_attrs
- @node.override_attrs.should == @example.override_attrs
- @node.normal_attrs.should == @example.normal_attrs
- @node.run_list.should == @example.run_list
+ node.update_from!(@example)
+ node.name.should == "orig"
+ node.chef_environment.should == @example.chef_environment
+ node.default_attrs.should == @example.default_attrs
+ node.override_attrs.should == @example.override_attrs
+ node.normal_attrs.should == @example.normal_attrs
+ node.run_list.should == @example.run_list
end
it "should not update the name of the node" do
- @node.should_not_receive(:name).with(@example.name)
- @node.update_from!(@example)
+ node.should_not_receive(:name).with(@example.name)
+ node.update_from!(@example)
end
end
describe "to_hash" do
it "should serialize itself as a hash" do
- @node.chef_environment("dev")
- @node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } }
- @node.override_attrs = { "one" => { "two" => "three", "four" => "six" } }
- @node.normal_attrs = { "one" => { "two" => "seven" } }
- @node.run_list << "role[marxist]"
- @node.run_list << "role[leninist]"
- @node.run_list << "recipe[stalinist]"
- h = @node.to_hash
+ node.chef_environment("dev")
+ node.default_attrs = { "one" => { "two" => "three", "four" => "five", "eight" => "nine" } }
+ node.override_attrs = { "one" => { "two" => "three", "four" => "six" } }
+ node.normal_attrs = { "one" => { "two" => "seven" } }
+ node.run_list << "role[marxist]"
+ node.run_list << "role[leninist]"
+ node.run_list << "recipe[stalinist]"
+ h = node.to_hash
h["one"]["two"].should == "three"
h["one"]["four"].should == "six"
h["one"]["eight"].should == "nine"
h["role"].should be_include("marxist")
h["role"].should be_include("leninist")
@@ -541,50 +672,67 @@
h["run_list"].should be_include("recipe[stalinist]")
h["chef_environment"].should == "dev"
end
end
- describe "json" do
+ describe "converting to or from json" do
it "should serialize itself as json", :json => true do
- @node.find_file("test.example.com")
- json = Chef::JSONCompat.to_json(@node)
+ node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
+ json = Chef::JSONCompat.to_json(node)
json.should =~ /json_class/
json.should =~ /name/
json.should =~ /chef_environment/
json.should =~ /normal/
json.should =~ /default/
json.should =~ /override/
json.should =~ /run_list/
end
- it 'should serialze valid json with a run list', :json => true do
+ it 'should serialize valid json with a run list', :json => true do
#This test came about because activesupport mucks with Chef json serialization
#Test should pass with and without Activesupport
- @node.run_list << {"type" => "role", "name" => 'Cthulu'}
- @node.run_list << {"type" => "role", "name" => 'Hastur'}
- json = Chef::JSONCompat.to_json(@node)
+ node.run_list << {"type" => "role", "name" => 'Cthulu'}
+ node.run_list << {"type" => "role", "name" => 'Hastur'}
+ json = Chef::JSONCompat.to_json(node)
json.should =~ /\"run_list\":\[\"role\[Cthulu\]\",\"role\[Hastur\]\"\]/
end
+ it "merges the override components into a combined override object" do
+ node.attributes.role_override["role override"] = "role override"
+ node.attributes.env_override["env override"] = "env override"
+ node_for_json = node.for_json
+ node_for_json["override"]["role override"].should == "role override"
+ node_for_json["override"]["env override"].should == "env override"
+ end
+
+ it "merges the default components into a combined default object" do
+ node.attributes.role_default["role default"] = "role default"
+ node.attributes.env_default["env default"] = "env default"
+ node_for_json = node.for_json
+ node_for_json["default"]["role default"].should == "role default"
+ node_for_json["default"]["env default"].should == "env default"
+ end
+
+
it "should deserialize itself from json", :json => true do
- @node.find_file("test.example.com")
- json = Chef::JSONCompat.to_json(@node)
+ node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
+ json = Chef::JSONCompat.to_json(node)
serialized_node = Chef::JSONCompat.from_json(json)
serialized_node.should be_a_kind_of(Chef::Node)
- serialized_node.name.should eql(@node.name)
- serialized_node.chef_environment.should eql(@node.chef_environment)
- @node.each_attribute do |k,v|
+ serialized_node.name.should eql(node.name)
+ serialized_node.chef_environment.should eql(node.chef_environment)
+ node.each_attribute do |k,v|
serialized_node[k].should eql(v)
end
- serialized_node.run_list.should == @node.run_list
+ serialized_node.run_list.should == node.run_list
end
end
describe "to_s" do
it "should turn into a string like node[name]" do
- @node.name("airplane")
- @node.to_s.should eql("node[airplane]")
+ node.name("airplane")
+ node.to_s.should eql("node[airplane]")
end
end
describe "api model" do
before(:each) do
@@ -619,126 +767,50 @@
end
describe "destroy" do
it "should destroy a node" do
@rest.should_receive(:delete_rest).with("nodes/monkey").and_return("foo")
- @node.name("monkey")
- @node.destroy
+ node.name("monkey")
+ node.destroy
end
end
describe "save" do
it "should update a node if it already exists" do
- @node.name("monkey")
- @rest.should_receive(:put_rest).with("nodes/monkey", @node).and_return("foo")
- @node.save
+ node.name("monkey")
+ @rest.should_receive(:put_rest).with("nodes/monkey", node).and_return("foo")
+ node.save
end
it "should not try and create if it can update" do
- @node.name("monkey")
- @rest.should_receive(:put_rest).with("nodes/monkey", @node).and_return("foo")
+ node.name("monkey")
+ @rest.should_receive(:put_rest).with("nodes/monkey", node).and_return("foo")
@rest.should_not_receive(:post_rest)
- @node.save
+ node.save
end
it "should create if it cannot update" do
- @node.name("monkey")
+ node.name("monkey")
exception = mock("404 error", :code => "404")
@rest.should_receive(:put_rest).and_raise(Net::HTTPServerException.new("foo", exception))
- @rest.should_receive(:post_rest).with("nodes", @node)
- @node.save
+ @rest.should_receive(:post_rest).with("nodes", node)
+ node.save
end
describe "when whyrun mode is enabled" do
before do
Chef::Config[:why_run] = true
end
after do
Chef::Config[:why_run] = false
end
it "should not save" do
- @node.name("monkey")
+ node.name("monkey")
@rest.should_not_receive(:put_rest)
@rest.should_not_receive(:post_rest)
- @node.save
+ node.save
end
end
end
- end
-
- describe "acting as a CouchDB-backed model" do
- before(:each) do
- @couchdb = Chef::CouchDB.new
- @mock_couch = mock('couch mock')
- end
-
- describe "list" do
- before(:each) do
- @mock_couch.stub!(:list).and_return(
- { "rows" => [ { "value" => "a", "key" => "avenue" } ] }
- )
- Chef::CouchDB.stub!(:new).and_return(@mock_couch)
- end
-
- it "should retrieve a list of nodes from CouchDB" do
- Chef::Node.cdb_list.should eql(["avenue"])
- end
-
- it "should return just the ids if inflate is false" do
- Chef::Node.cdb_list(false).should eql(["avenue"])
- end
-
- it "should return the full objects if inflate is true" do
- Chef::Node.cdb_list(true).should eql(["a"])
- end
- end
-
- describe "when loading a given node" do
- it "should load a node from couchdb by name" do
- @couchdb.should_receive(:load).with("node", "coffee").and_return(true)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
- Chef::Node.cdb_load("coffee")
- end
- end
-
- describe "when destroying a Node" do
- it "should delete this node from couchdb" do
- @couchdb.should_receive(:delete).with("node", "bob", 1).and_return(true)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
- node = Chef::Node.new
- node.name "bob"
- node.couchdb_rev = 1
- node.cdb_destroy
- end
- end
-
- describe "when saving a Node" do
- before(:each) do
- @couchdb.stub!(:store).and_return({ "rev" => 33 })
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
- @node = Chef::Node.new
- @node.name "bob"
- @node.couchdb_rev = 1
- end
-
- it "should save the node to couchdb" do
- @couchdb.should_receive(:store).with("node", "bob", @node).and_return({ "rev" => 33 })
- @node.cdb_save
- end
-
- it "should store the new couchdb_rev" do
- @node.cdb_save
- @node.couchdb_rev.should eql(33)
- end
- end
-
- describe "create_design_document" do
- it "should create our design document" do
- @couchdb.should_receive(:create_design_document).with("nodes", Chef::Node::DESIGN_DOCUMENT)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
- Chef::Node.create_design_document
- end
- end
-
end
end