diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 508e21e8d..f991f539d 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -13,8 +13,7 @@ class Puppet::Resource extend Puppet::Util::Pson include Enumerable attr_accessor :file, :line, :catalog, :exported, :virtual, :validate_parameters, :strict - attr_reader :title, :namespaces - attr_writer :relative_type + attr_reader :namespaces require 'puppet/indirector' extend Puppet::Indirector @@ -166,6 +165,8 @@ class Puppet::Resource extract_parameters(params) end + resolve_type_and_title() + tag(self.type) tag(self.title) if valid_tag?(self.title) @@ -185,23 +186,38 @@ class Puppet::Resource end def title=(value) - if klass = resource_type and klass.respond_to?(:canonicalize_ref) - value = klass.canonicalize_ref(value) + @unresolved_title = value + @title = nil + end + + def old_title + if type == "Class" and value == "" + @title = :main + return end + + if klass = resource_type + p klass + if type == "Class" + value = munge_type_name(resource_type.name) + end + + if klass.respond_to?(:canonicalize_ref) + value = klass.canonicalize_ref(value) + end + elsif type == "Class" + value = munge_type_name(value) + end + @title = value end - # Canonize the type so we know it's always consistent. - def relative_type - munge_type_name(@relative_type) - end - def resource_type - case relative_type.to_s.downcase - when "class"; find_hostclass - when "node"; find_node + case type + when "Class"; find_hostclass(title) + when "Node"; find_node(title) else - find_builtin_resource_type || find_defined_resource_type + find_resource_type(type) end end @@ -306,18 +322,26 @@ class Puppet::Resource self end - def type - munge_type_name(if r = resource_type - resource_type.name - else - relative_type - end) + # We have to lazy-evaluate this. + def title=(value) + @title = nil + @unresolved_title = value end - # Only allow people to set the relative type, - # so we force it to be looked up each time. + # We have to lazy-evaluate this. def type=(value) - @relative_type = value + @type = nil + @unresolved_type = value || "Class" + end + + def title + resolve_type_and_title unless @title + @title + end + + def type + resolve_type_and_title unless @type + @type end def valid_parameter?(name) @@ -330,21 +354,25 @@ class Puppet::Resource private - def find_node - known_resource_types.node(title) + def find_node(name) + known_resource_types.node(name) end - def find_hostclass + def find_hostclass(title) name = title == :main ? "" : title known_resource_types.find_hostclass(namespaces, name) end - def find_builtin_resource_type - Puppet::Type.type(relative_type.to_s.downcase.to_sym) + def find_resource_type(type) + find_builtin_resource_type(type) || find_defined_resource_type(type) end - def find_defined_resource_type - known_resource_types.find_definition(namespaces, relative_type.to_s.downcase) + def find_builtin_resource_type(type) + Puppet::Type.type(type.to_s.downcase.to_sym) + end + + def find_defined_resource_type(type) + known_resource_types.find_definition(namespaces, type.to_s.downcase) end # Produce a canonical method name. @@ -381,8 +409,6 @@ class Puppet::Resource return bucket end - private - def extract_parameters(params) params.each do |param, value| validate_parameter(param) if strict? @@ -399,12 +425,72 @@ class Puppet::Resource end def munge_type_name(value) - return :main if value == "" + return :main if value == :main + return "Class" if value == "" or value.nil? or value.to_s.downcase == "component" - if value.nil? or value.to_s.downcase == "component" - "Class" + value.to_s.split("::").collect { |s| s.capitalize }.join("::") + end + + # This is an annoyingly complicated method for resolving qualified + # types as necessary, and putting them in type or title attributes. + def resolve_type_and_title + if @unresolved_type + @type = resolve_type + @unresolved_type = nil + end + if @unresolved_title + @title = resolve_title + @unresolved_title = nil + end + end + + def resolve_type + type = munge_type_name(@unresolved_type) + + case type + when "Class", "Node"; + return type else - value.to_s.split("::").collect { |s| s.capitalize }.join("::") + # Otherwise, some kind of builtin or defined resource type + return munge_type_name(if r = find_resource_type(type) + r.name + else + type + end) + end + end + + # This method only works if resolve_type was called first + def resolve_title + case @type + when "Node"; return @unresolved_title + when "Class"; + resolve_title_for_class(@unresolved_title) + else + resolve_title_for_resource(@unresolved_title) + end + end + + def resolve_title_for_class(title) + if title == "" or title == :main + return :main + end + + if klass = find_hostclass(title) + result = klass.name + + if klass.respond_to?(:canonicalize_ref) + result = klass.canonicalize_ref(result) + end + end + return munge_type_name(result || title) + end + + def resolve_title_for_resource(title) + if type = find_resource_type(@type) and type.respond_to?(:canonicalize_ref) + return type.canonicalize_ref(title) + else + return title end end end diff --git a/spec/unit/resource.rb b/spec/unit/resource.rb index 5b82c2936..0eacd0b90 100755 --- a/spec/unit/resource.rb +++ b/spec/unit/resource.rb @@ -48,7 +48,7 @@ describe Puppet::Resource do it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do ref = Puppet::Resource.new(:component, "foo") ref.type.should == "Class" - ref.title.should == "foo" + ref.title.should == "Foo" end it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do @@ -60,7 +60,7 @@ describe Puppet::Resource do it "should set the type to 'Class' if it is nil and the title contains no square brackets" do ref = Puppet::Resource.new(nil, "yay") ref.type.should == "Class" - ref.title.should == "yay" + ref.title.should == "Yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do @@ -113,108 +113,159 @@ describe Puppet::Resource do end it "should support specifying namespaces" do - Puppet::Resource.new("file", "/my/file", :namespaces => [:foo]).namespaces.should == [:foo] + Puppet::Resource.new("file", "/my/file", :namespaces => ["foo"]).namespaces.should == ["foo"] end it "should convert namespaces to an array if not specified as one" do - Puppet::Resource.new("file", "/my/file", :namespaces => :foo).namespaces.should == [:foo] + Puppet::Resource.new("file", "/my/file", :namespaces => "foo").namespaces.should == ["foo"] end it "should default to a single amespace of an empty string" do Puppet::Resource.new("file", "/my/file").namespaces.should == [""] end - it "should be able to look up its resource type when the type is a builtin resource" do - Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) - end + describe "and munging its type and title" do + describe "when modeling a builtin resource" do + it "should be able to find the resource type" do + Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) + end - it "should be able to look up its resource type via its environment when the type is a defined resource type" do - resource = Puppet::Resource.new("foobar", "/my/file") - type = Puppet::Resource::Type.new(:definition, "foobar") - resource.environment.known_resource_types.add type + it "should set its type to the capitalized type name" do + Puppet::Resource.new("file", "/my/file").type.should == "File" + end + end - resource.resource_type.should equal(type) - end + describe "when modeling a defined resource" do + describe "that exists" do + before do + @type = Puppet::Resource::Type.new(:definition, "foo::bar") + Puppet::Node::Environment.new.known_resource_types.add @type + end - it "should be able to look up its resource type via its environment when the type is a node" do - resource = Puppet::Resource.new("node", "foobar") - node = Puppet::Resource::Type.new(:node, "foobar") - resource.environment.known_resource_types.add node + it "should set its type to the capitalized type name" do + Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" + end - resource.resource_type.should equal(node) - end + it "should be able to find the resource type" do + Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) + end - it "should be able to look up its resource type via its environment when the type is a class" do - resource = Puppet::Resource.new("class", "foobar") - klass = Puppet::Resource::Type.new(:hostclass, "foobar") - resource.environment.known_resource_types.add klass + it "should set its title to the provided title" do + Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" + end - resource.resource_type.should equal(klass) - end + describe "and the resource is unqualified and models a qualified resource type" do + it "should set its type to the fully qualified resource type" do + Puppet::Resource.new("bar", "/my/file", :namespaces => %w{foo}).type.should == "Foo::Bar" + end - it "should use its namespaces when looking up defined resource types" do - resource = Puppet::Resource.new("bar", "/my/file", :namespaces => ["foo"]) - type = Puppet::Resource::Type.new(:definition, "foo::bar") - resource.environment.known_resource_types.add type + it "should be able to find the resource type" do + Puppet::Resource.new("bar", "/my/file", :namespaces => %w{foo}).resource_type.should equal(@type) + end + end + end - resource.resource_type.should equal(type) - end + describe "that does not exist" do + it "should set its resource type to the capitalized resource type name" do + Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" + end + end + end - it "should use its namespaces to set its type name when looking up defined resource types" do - type = Puppet::Resource::Type.new(:definition, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add type - resource = Puppet::Resource.new("bar", "/my/file", :namespaces => ["foo"]) - resource.type.should == "Foo::Bar" - end + describe "when modeling a node" do + # Life's easier with nodes, because they can't be qualified. + it "should set its type to 'Node' and its title to the provided title" do + node = Puppet::Resource.new("node", "foo") + node.type.should == "Node" + node.title.should == "foo" + end + end - it "should look up its resource type when set manually" do - type = Puppet::Resource::Type.new(:definition, "foo::bar") - Puppet::Node::Environment.new.known_resource_types.add type - resource = Puppet::Resource.new("foo", "/my/file", :namespaces => ["foo"]) - resource.type = "bar" - resource.type.should == "Foo::Bar" - end + describe "when modeling a class" do + it "should set its type to 'Class'" do + Puppet::Resource.new("class", "foo").type.should == "Class" + end - it "should use its namespaces when looking up host classes" do - resource = Puppet::Resource.new("class", "bar", :namespaces => ["foo"]) - type = Puppet::Resource::Type.new(:hostclass, "foo::bar") - resource.environment.known_resource_types.add type + describe "that exists" do + before do + @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") + Puppet::Node::Environment.new.known_resource_types.add @type + end - resource.resource_type.should equal(type) - end + it "should set its title to the capitalized, fully qualified resource type" do + Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" + end - it "should consider a class whose name is an empty string to be the main class" do - type = Puppet::Resource::Type.new(:hostclass, "") - Puppet::Node::Environment.new.known_resource_types.add type + it "should be able to find the resource type" do + Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) + end - resource = Puppet::Resource.new("class", "").type.should == :main + describe "and the resource is unqualified and models a qualified class" do + it "should set its title to the fully qualified resource type" do + Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).title.should == "Foo::Bar" + end + + it "should be able to find the resource type" do + Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).resource_type.should equal(@type) + end + + it "should set its type to 'Class'" do + Puppet::Resource.new("class", "bar", :namespaces => %w{foo}).type.should == "Class" + end + end + end + + describe "that does not exist" do + it "should set its type to 'Class' and its title to the capitalized provided name" do + klass = Puppet::Resource.new("class", "foo::bar") + klass.type.should == "Class" + klass.title.should == "Foo::Bar" + end + end + + describe "and its name is set to the empty string" do + it "should set its title to :main" do + Puppet::Resource.new("class", "").title.should == :main + end + + describe "and a class exists whose name is the empty string" do # this was a bit tough to track down + it "should set its title to :main" do + @type = Puppet::Resource::Type.new(:hostclass, "") + Puppet::Node::Environment.new.known_resource_types.add @type + + Puppet::Resource.new("class", "").title.should == :main + end + end + end + + describe "and its name is set to :main" do + it "should set its title to :main" do + Puppet::Resource.new("class", :main).title.should == :main + end + + describe "and a class exists whose name is the empty string" do # this was a bit tough to track down + it "should set its title to :main" do + @type = Puppet::Resource::Type.new(:hostclass, "") + Puppet::Node::Environment.new.known_resource_types.add @type + + Puppet::Resource.new("class", :main).title.should == :main + end + end + end + end end it "should return nil when looking up resource types that don't exist" do Puppet::Resource.new("foobar", "bar").resource_type.should be_nil end - it "should fail when an invalid parameter is used and parameter validation is enabled" do - type = Puppet::Resource::Type.new(:definition, "foobar") - Puppet::Node::Environment.new.known_resource_types.add type - resource = Puppet::Resource.new("foobar", "/my/file", :validate_parameters => true) - lambda { resource[:yay] = true }.should raise_error(ArgumentError) - end - - it "should not fail when an invalid parameter is used and parameter validation is disabled" do + it "should not fail when an invalid parameter is used and strict mode is disabled" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type resource = Puppet::Resource.new("foobar", "/my/file") resource[:yay] = true end - it "should not fail when a valid parameter is used and parameter validation is enabled" do - type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"yay" => nil}) - Puppet::Node::Environment.new.known_resource_types.add type - resource = Puppet::Resource.new("foobar", "/my/file", :validate_parameters => true) - resource[:yay] = true - end - it "should be considered equivalent to another resource if their type and title match and no parameters are set" do Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") end @@ -239,10 +290,7 @@ describe Puppet::Resource do Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") end - describe "when refering to a resource with name canonicalization" do - before do - end - + describe "when referring to a resource with name canonicalization" do it "should canonicalize its own name" do res = Puppet::Resource.new("file", "/path/") res.title.should == "/path" diff --git a/test/language/ast/resource_reference.rb b/test/language/ast/resource_reference.rb deleted file mode 100755 index 5abb0d6e5..000000000 --- a/test/language/ast/resource_reference.rb +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke A. Kanies on 2007-07-8. -# Copyright (c) 2007. All rights reserved. - -require File.dirname(__FILE__) + '/../../lib/puppettest' - -require 'puppettest' -require 'puppettest/parsertesting' - -class TestASTResourceReference < Test::Unit::TestCase - include PuppetTest - include PuppetTest::ParserTesting - AST = Puppet::Parser::AST - - def newref(type, title) - AST::ResourceReference.new(:type => type, :title => AST::String.new(:value => title)) - end - - def setup - super - @scope = mkscope - @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) - end - - # Related to #706, make sure resource references correctly translate to qualified types. - def test_scoped_references - @parser.newdefine "one" - @parser.newdefine "one::two" - @parser.newdefine "three" - twoscope = @scope.newscope(:namespace => "one") - assert(twoscope.find_definition("two"), "Could not find 'two' definition") - title = "title" - - # First try a qualified type - assert_equal("One::Two", newref("two", title).evaluate(twoscope).type, - "Defined type was not made fully qualified") - - # Then try a type that does not need to be qualified - assert_equal("One", newref("one", title).evaluate(twoscope).type, - "Unqualified defined type was not handled correctly") - - # Then an unqualified type from within the one namespace - assert_equal("Three", newref("three", title).evaluate(twoscope).type, - "Defined type was not made fully qualified") - - # Then a builtin type - assert_equal("File", newref("file", title).evaluate(twoscope).type, - "Builtin type was not handled correctly") - - # Now try a type that does not exist, which should throw an error. - assert_raise(Puppet::ParseError, "Did not fail on a missing type in a resource reference") do - newref("nosuchtype", title).evaluate(twoscope) - end - - # Now run the same tests, but with the classes - @parser.newclass "four" - @parser.newclass "one::five" - - # First try an unqualified type - assert_equal("four", newref("class", "four").evaluate(twoscope).title, - "Unqualified class was not found") - - # Then a qualified class - assert_equal("one::five", newref("class", "five").evaluate(twoscope).title, - "Class was not made fully qualified") - - # Then try a type that does not need to be qualified - assert_equal("four", newref("class", "four").evaluate(twoscope).title, - "Unqualified class was not handled correctly") - - # Now try a type that does not exist, which should throw an error. - assert_raise(Puppet::ParseError, "Did not fail on a missing type in a resource reference") do - newref("class", "nosuchclass").evaluate(twoscope) - end - end -end