diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index 4bd4d1de6..5b0a98615 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -1,143 +1,120 @@ require 'puppet/indirector' # A class for managing nodes, including their facts and environment. class Puppet::Node require 'puppet/node/facts' require 'puppet/node/inventory' require 'puppet/node/environment' # Set up indirection, so that nodes can be looked for in # the node sources. extend Puppet::Indirector # Adds the environment getter and setter, with some instance/string conversion include Puppet::Node::Environment::Helper # Use the node source as the indirection terminus. indirects :node, :terminus_setting => :node_terminus, :doc => "Where to find node information. A node is composed of its name, its facts, and its environment." attr_accessor :name, :classes, :source, :ipaddress, :parameters attr_reader :time - def self.from_pson(pson) - raise ArgumentError, "No name provided in pson data" unless name = pson['name'] - - node = new(name) - node.classes = pson['classes'] - node.parameters = pson['parameters'] - node.environment = pson['environment'] - node - end - - def to_pson(*args) - result = { - 'document_type' => "Puppet::Node", - 'data' => {} - } - result['data']['name'] = name - result['data']['classes'] = classes unless classes.empty? - result['data']['parameters'] = parameters unless parameters.empty? - result['data']['environment'] = environment.name - - result.to_pson(*args) - end - def environment return super if @environment if env = parameters["environment"] self.environment = env return super end # Else, return the default Puppet::Node::Environment.new end def initialize(name, options = {}) raise ArgumentError, "Node names cannot be nil" unless name @name = name if classes = options[:classes] if classes.is_a?(String) @classes = [classes] else @classes = classes end else @classes = [] end @parameters = options[:parameters] || {} if env = options[:environment] self.environment = env end @time = Time.now end # Merge the node facts with parameters from the node source. def fact_merge if facts = Puppet::Node::Facts.indirection.find(name) merge(facts.values) end rescue => detail error = Puppet::Error.new("Could not retrieve facts for #{name}: #{detail}") error.set_backtrace(detail.backtrace) raise error end # Merge any random parameters into our parameter list. def merge(params) params.each do |name, value| @parameters[name] = value unless @parameters.include?(name) end @parameters["environment"] ||= self.environment.name.to_s if self.environment end # Calculate the list of names we might use for looking # up our node. This is only used for AST nodes. def names return [name] if Puppet.settings[:strict_hostname_checking] names = [] names += split_name(name) if name.include?(".") # First, get the fqdn unless fqdn = parameters["fqdn"] if parameters["hostname"] and parameters["domain"] fqdn = parameters["hostname"] + "." + parameters["domain"] else Puppet.warning "Host is missing hostname and/or domain: #{name}" end end # Now that we (might) have the fqdn, add each piece to the name # list to search, in order of longest to shortest. names += split_name(fqdn) if fqdn # And make sure the node name is first, since that's the most # likely usage. # The name is usually the Certificate CN, but it can be # set to the 'facter' hostname instead. if Puppet[:node_name] == 'cert' names.unshift name else names.unshift parameters["hostname"] end names.uniq end def split_name(name) list = name.split(".") tmp = [] list.each_with_index do |short, i| tmp << list[0..i].join(".") end tmp.reverse end end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index e8f826dca..169a9cdcf 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1,266 +1,203 @@ #!/usr/bin/env rspec require 'spec_helper' require 'matchers/json' describe Puppet::Node do describe "when managing its environment" do it "should use any set environment" do Puppet::Node.new("foo", :environment => "bar").environment.name.should == :bar end it "should support providing an actual environment instance" do Puppet::Node.new("foo", :environment => Puppet::Node::Environment.new(:bar)).environment.name.should == :bar end it "should determine its environment from its parameters if no environment is set" do Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.name.should == :bar end it "should use the default environment if no environment is provided" do Puppet::Node.new("foo").environment.name.should == Puppet::Node::Environment.new.name end it "should always return an environment instance rather than a string" do Puppet::Node.new("foo").environment.should be_instance_of(Puppet::Node::Environment) end it "should allow the environment to be set after initialization" do node = Puppet::Node.new("foo") node.environment = :bar node.environment.name.should == :bar end it "should allow its environment to be set by parameters after initialization" do node = Puppet::Node.new("foo") node.parameters["environment"] = :bar node.environment.name.should == :bar end end - - describe "when converting to json" do - before do - @node = Puppet::Node.new("mynode") - end - - it "should provide its name" do - @node.should set_json_attribute('name').to("mynode") - end - - it "should include the classes if set" do - @node.classes = %w{a b c} - @node.should set_json_attribute("classes").to(%w{a b c}) - end - - it "should not include the classes if there are none" do - @node.should_not set_json_attribute('classes') - end - - it "should include parameters if set" do - @node.parameters = {"a" => "b", "c" => "d"} - @node.should set_json_attribute('parameters').to({"a" => "b", "c" => "d"}) - end - - it "should not include the parameters if there are none" do - @node.should_not set_json_attribute('parameters') - end - - it "should include the environment" do - @node.environment = "production" - @node.should set_json_attribute('environment').to('production') - end - end - - describe "when converting from json" do - before do - @node = Puppet::Node.new("mynode") - @format = Puppet::Network::FormatHandler.format('pson') - end - - def from_json(json) - @format.intern(Puppet::Node, json) - end - - it "should set its name" do - Puppet::Node.should read_json_attribute('name').from(@node.to_pson).as("mynode") - end - - it "should include the classes if set" do - @node.classes = %w{a b c} - Puppet::Node.should read_json_attribute('classes').from(@node.to_pson).as(%w{a b c}) - end - - it "should include parameters if set" do - @node.parameters = {"a" => "b", "c" => "d"} - Puppet::Node.should read_json_attribute('parameters').from(@node.to_pson).as({"a" => "b", "c" => "d"}) - end - - it "should include the environment" do - @node.environment = "production" - Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(Puppet::Node::Environment.new(:production)) - end - end end describe Puppet::Node, "when initializing" do before do @node = Puppet::Node.new("testnode") end it "should set the node name" do @node.name.should == "testnode" end it "should not allow nil node names" do proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError) end it "should default to an empty parameter hash" do @node.parameters.should == {} end it "should default to an empty class array" do @node.classes.should == [] end it "should note its creation time" do @node.time.should be_instance_of(Time) end it "should accept parameters passed in during initialization" do params = {"a" => "b"} @node = Puppet::Node.new("testing", :parameters => params) @node.parameters.should == params end it "should accept classes passed in during initialization" do classes = %w{one two} @node = Puppet::Node.new("testing", :classes => classes) @node.classes.should == classes end it "should always return classes as an array" do @node = Puppet::Node.new("testing", :classes => "myclass") @node.classes.should == ["myclass"] end end describe Puppet::Node, "when merging facts" do before do @node = Puppet::Node.new("testnode") Puppet::Node::Facts.indirection.stubs(:find).with(@node.name).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b")) end it "should fail intelligently if it cannot find facts" do Puppet::Node::Facts.indirection.expects(:find).with(@node.name).raises "foo" lambda { @node.fact_merge }.should raise_error(Puppet::Error) end it "should prefer parameters already set on the node over facts from the node" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["one"].should == "a" end it "should add passed parameters to the parameter list" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["two"].should == "b" end it "should accept arbitrary parameters to merge into its parameters" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.merge "two" => "three" @node.parameters["two"].should == "three" end it "should add the environment to the list of parameters" do Puppet.settings.stubs(:value).with(:environments).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "two" => "three" @node.parameters["environment"].should == "one" end it "should not set the environment if it is already set in the parameters" do Puppet.settings.stubs(:value).with(:environments).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "environment" => "two" @node.parameters["environment"].should == "two" end end describe Puppet::Node, "when indirecting" do it "should default to the 'plain' node terminus" do Puppet::Node.indirection.terminus_class.should == :plain end it "should not have a cache class defined" do Puppet::Node.indirection.cache_class.should be_nil end after do Puppet::Util::Cacher.expire end end describe Puppet::Node, "when generating the list of names to search through" do before do @node = Puppet::Node.new("foo.domain.com", :parameters => {"hostname" => "yay", "domain" => "domain.com"}) end it "should return an array of names" do @node.names.should be_instance_of(Array) end describe "and the node name is fully qualified" do it "should contain an entry for each part of the node name" do @node.names.should be_include("foo.domain.com") @node.names.should be_include("foo.domain") @node.names.should be_include("foo") end end it "should include the node's fqdn" do @node.names.should be_include("yay.domain.com") end it "should combine and include the node's hostname and domain if no fqdn is available" do @node.names.should be_include("yay.domain.com") end it "should contain an entry for each name available by stripping a segment of the fqdn" do @node.parameters["fqdn"] = "foo.deep.sub.domain.com" @node.names.should be_include("foo.deep.sub.domain") @node.names.should be_include("foo.deep.sub") end describe "and :node_name is set to 'cert'" do before do Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false Puppet.settings.stubs(:value).with(:node_name).returns "cert" end it "should use the passed-in key as the first value" do @node.names[0].should == "foo.domain.com" end describe "and strict hostname checking is enabled" do it "should only use the passed-in key" do Puppet.settings.expects(:value).with(:strict_hostname_checking).returns true @node.names.should == ["foo.domain.com"] end end end describe "and :node_name is set to 'facter'" do before do Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false Puppet.settings.stubs(:value).with(:node_name).returns "facter" end it "should use the node's 'hostname' fact as the first value" do @node.names[0].should == "yay" end end end