[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, upstream, updated. 0.25.5-639-g8f94f35

test branch puppet-dev at googlegroups.com
Wed Jul 14 10:32:07 UTC 2010


The following commit has been merged in the upstream branch:
commit 6d2a10b40c9f77ea5101abe6e568ed5a798c04f3
Author: Luke Kanies <luke at reductivelabs.com>
Date:   Fri Jan 8 00:49:36 2010 -0800

    Adding simplistic pure ruby interface
    
    This is a simplistic DSL - you can create
    resource types (defined resources), classes,
    and nodes, and they can call functions and
    create resources.  Nothing else, at this point.
    
    Signed-off-by: Luke Kanies <luke at reductivelabs.com>

diff --git a/ext/pure_ruby_dsl/dsl_test.rb b/ext/pure_ruby_dsl/dsl_test.rb
new file mode 100644
index 0000000..6eff298
--- /dev/null
+++ b/ext/pure_ruby_dsl/dsl_test.rb
@@ -0,0 +1,7 @@
+hostclass "foobar" do
+    notify "this is a test", "loglevel" => "warning"
+end
+
+node "default" do
+    acquire "foobar"
+end
diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb
new file mode 100644
index 0000000..93cf0fb
--- /dev/null
+++ b/lib/puppet/dsl.rb
@@ -0,0 +1,11 @@
+require 'puppet'
+
+module Puppet::DSL
+end
+
+require 'puppet/dsl/resource_type_api'
+require 'puppet/dsl/resource_helper'
+
+class Object
+    include Puppet::DSL::ResourceTypeAPI
+end
diff --git a/lib/puppet/dsl/resource_helper.rb b/lib/puppet/dsl/resource_helper.rb
new file mode 100644
index 0000000..aaf8ab5
--- /dev/null
+++ b/lib/puppet/dsl/resource_helper.rb
@@ -0,0 +1,62 @@
+# This module adds functionality to a resource to make it
+# capable of evaluating the DSL resource type block and also
+# hooking into the scope system.
+require 'puppet/resource/type_collection_helper'
+
+module Puppet::DSL::ResourceHelper
+    include Puppet::Resource::TypeCollectionHelper
+
+    FUNCTION_MAP = {:acquire => :include}
+
+    # Try to convert a missing method into a resource type or a function.
+    def method_missing(name, *args)
+        return create_resource(name, args[0], args[1]) if valid_type?(name)
+
+        name = map_function(name)
+
+        return call_function(name, args) if Puppet::Parser::Functions.function(name)
+
+        super
+    end
+
+    def set_instance_variables
+        eachparam do |param|
+            instance_variable_set("@#{param.name}", param.value)
+        end
+    end
+
+    def create_resource(type, names, arguments = nil)
+        names = [names] unless names.is_a?(Array)
+
+        arguments ||= {}
+        raise ArgumentError, "Resource arguments must be provided as a hash" unless arguments.is_a?(Hash)
+
+        names.collect do |name|
+            resource = Puppet::Parser::Resource.new(:type => type, :title => name, :scope => scope)
+            arguments.each do |param, value|
+                resource[param] = value
+            end
+
+            scope.compiler.add_resource(scope, resource)
+            resource
+        end
+    end
+
+    def call_function(name, args)
+        return false unless method = Puppet::Parser::Functions.function(name)
+        scope.send(method, *args)
+    end
+
+    def valid_type?(name)
+        return true if [:class, :node].include?(name)
+        return true if Puppet::Type.type(name)
+        return true if known_resource_types.definition(name)
+        return false
+    end
+
+    private
+
+    def map_function(name)
+        return FUNCTION_MAP[name] || name
+    end
+end
diff --git a/lib/puppet/dsl/resource_type_api.rb b/lib/puppet/dsl/resource_type_api.rb
new file mode 100644
index 0000000..014d1a3
--- /dev/null
+++ b/lib/puppet/dsl/resource_type_api.rb
@@ -0,0 +1,40 @@
+require 'puppet/resource/type'
+
+module Puppet::DSL::ResourceTypeAPI
+    def resource_type(name, *args, &block)
+        result = mk_resource_type(:definition, name, Hash.new, block)
+        result.set_arguments(munge_type_arguments(args))
+        result
+    end
+
+    def hostclass(name, options = {}, &block)
+        mk_resource_type(:hostclass, name, options, block)
+    end
+
+    def node(name, options = {}, &block)
+        mk_resource_type(:node, name, options, block)
+    end
+
+    private
+
+    def mk_resource_type(type, name, options, code)
+        klass = Puppet::Resource::Type.new(type, name, options)
+
+        klass.ruby_code = code if code
+
+        Puppet::Node::Environment.new.known_resource_types.add klass
+
+        klass
+    end
+
+    def munge_type_arguments(args)
+        args.inject([]) do |result, item|
+            if item.is_a?(Hash)
+                item.each { |p, v| result << [p, v] }
+            else
+                result << item
+            end
+            result
+        end
+    end
+end
diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb
index 1b78bf5..fa5fd5c 100644
--- a/lib/puppet/resource/type.rb
+++ b/lib/puppet/resource/type.rb
@@ -3,6 +3,7 @@ require 'puppet/util/warnings'
 require 'puppet/util/errors'
 require 'puppet/util/inline_docs'
 require 'puppet/parser/ast/leaf'
+require 'puppet/dsl'
 
 class Puppet::Resource::Type
     include Puppet::Util::InlineDocs
@@ -11,7 +12,7 @@ class Puppet::Resource::Type
 
     RESOURCE_SUPERTYPES = [:hostclass, :node, :definition]
 
-    attr_accessor :file, :line, :doc, :code, :parent, :code_collection
+    attr_accessor :file, :line, :doc, :code, :ruby_code, :parent, :resource_type_collection
     attr_reader :type, :namespace, :arguments, :behaves_like
 
     # Are we a child of the passed class?  Do a recursive search up our
@@ -35,8 +36,9 @@ class Puppet::Resource::Type
 
         set_resource_parameters(resource, scope)
 
-        return nil unless c = self.code
-        return c.safeevaluate(scope)
+        code.safeevaluate(scope) if code
+
+        evaluate_ruby_code(resource, scope) if ruby_code
     end
 
     def initialize(type, name, options = {})
@@ -137,7 +139,7 @@ class Puppet::Resource::Type
     def parent_type
         return nil unless parent
 
-        unless @parent_type ||= code_collection.send(type, parent)
+        unless @parent_type ||= resource_type_collection.send(type, parent)
             fail Puppet::ParseError, "Could not find parent resource type '#{parent}'"
         end
 
@@ -217,6 +219,14 @@ class Puppet::Resource::Type
         return parent_scope(resource.scope, klass)
     end
 
+    def evaluate_ruby_code(resource, scope)
+        resource.extend(Puppet::DSL::ResourceHelper)
+
+        resource.set_instance_variables
+
+        resource.instance_eval(&ruby_code)
+    end
+
     # Split an fq name into a namespace and name
     def namesplit(fullname)
         ary = fullname.split("::")
diff --git a/lib/puppet/resource/type_collection.rb b/lib/puppet/resource/type_collection.rb
index e2ca562..513c1c6 100644
--- a/lib/puppet/resource/type_collection.rb
+++ b/lib/puppet/resource/type_collection.rb
@@ -31,7 +31,7 @@ class Puppet::Resource::TypeCollection
         end
         method = "add_#{instance.type}"
         send(method, instance)
-        instance.code_collection = self
+        instance.resource_type_collection = self
         instance
     end
 
diff --git a/spec/unit/dsl/resource_helper.rb b/spec/unit/dsl/resource_helper.rb
new file mode 100755
index 0000000..11dad40
--- /dev/null
+++ b/spec/unit/dsl/resource_helper.rb
@@ -0,0 +1,151 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/dsl/resource_helper'
+
+class DSLResourceHelperTester
+    include Puppet::DSL::ResourceHelper
+end
+
+describe Puppet::DSL::ResourceHelper do
+    before do
+        @resource = Puppet::Parser::Resource.new(:type => :mytype, :title => "myresource", :scope => mock("scope"), :source => mock("source"))
+        class << @resource
+            include Puppet::DSL::ResourceHelper
+        end
+    end
+
+    it "should include the resource type collection helper" do
+        Puppet::DSL::ResourceHelper.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper)
+    end
+
+    it "should be able to set all of its parameters as instance variables" do
+        @resource["foo"] = "myval"
+        @resource.set_instance_variables
+        @resource.instance_variable_get("@foo").should == "myval"
+    end
+
+    describe "when calling a function" do
+        it "should return false if the function does not exist" do
+            Puppet::Parser::Functions.expects(:function).with("myfunc").returns nil
+            @resource.call_function("myfunc", "foo").should be_false
+        end
+
+        it "should use the scope the call the provided function with the provided arguments and return the results" do
+            scope = stub 'scope'
+            @resource.stubs(:scope).returns scope
+            Puppet::Parser::Functions.expects(:function).with("myfunc").returns "myfunc_method"
+
+            scope.expects(:myfunc_method).with("one", "two")
+            @resource.call_function("myfunc", ["one", "two"])
+        end
+    end
+
+    describe "when determining if a provided name is a valid type" do
+        it "should be valid if it's :class" do
+            @resource.should be_valid_type(:class)
+        end
+
+        it "should be valid if it's :node" do
+            @resource.should be_valid_type(:node)
+        end
+
+        it "should be valid if it's a builtin type" do
+            Puppet::Type.expects(:type).with(:mytype).returns "whatever"
+            @resource.should be_valid_type(:mytype)
+        end
+
+        it "should be valid if it's a defined resource type in the environment's known resource types" do
+            collection = stub 'collection'
+            @resource.stubs(:known_resource_types).returns collection
+            collection.expects(:definition).with(:mytype).returns "whatever"
+            @resource.should be_valid_type(:mytype)
+        end
+
+        it "should not be valid unless it's a node, class, builtin type, or defined resource" do
+            collection = stub 'collection'
+            @resource.stubs(:known_resource_types).returns collection
+            collection.expects(:definition).returns nil
+            Puppet::Type.expects(:type).returns nil
+            @resource.should_not be_valid_type(:mytype)
+        end
+    end
+
+    describe "when creating a resource" do
+        before do
+            @resource.scope.stubs(:source).returns stub("source")
+            @resource.scope.stubs(:compiler).returns stub("compiler", :add_resource => nil)
+            @created_resource = Puppet::Parser::Resource.new(:title => "eh", :type => 'yay', :scope => @resource.scope)
+        end
+
+        it "should create and return a resource of the type specified" do
+            Puppet::Parser::Resource.expects(:new).with { |args| args[:type] == "mytype" }.returns @created_resource
+            @resource.create_resource("mytype", "myname", {:foo => "bar"}).should == [@created_resource]
+        end
+
+        it "should use the name from the first element of the provided argument array" do
+            Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "myname" }.returns @created_resource
+            @resource.create_resource("mytype", "myname", {:foo => "bar"})
+        end
+
+        it "should create multiple resources if the first element of the argument array is an array" do
+            second_resource = Puppet::Parser::Resource.new(:title => "eh", :type => 'yay', :scope => @resource.scope)
+            Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "first" }.returns @created_resource
+            Puppet::Parser::Resource.expects(:new).with { |args| args[:title] == "second" }.returns @created_resource
+            @resource.create_resource("mytype", ["first", "second"], {:foo => "bar"})
+        end
+
+        it "should provide its scope as the scope" do
+            Puppet::Parser::Resource.expects(:new).with { |args| args[:scope] == @resource.scope }.returns @created_resource
+            @resource.create_resource("mytype", "myname", {:foo => "bar"})
+        end
+
+        it "should set each provided argument as a parameter on the created resource" do
+            result = @resource.create_resource("mytype", "myname", {"foo" => "bar", "biz" => "baz"}).shift
+            result["foo"].should == "bar"
+            result["biz"].should == "baz"
+        end
+
+        it "should add the resource to the scope's copmiler" do
+            Puppet::Parser::Resource.expects(:new).returns @created_resource
+            @resource.scope.compiler.expects(:add_resource).with(@resource.scope, @created_resource)
+            @resource.create_resource("mytype", "myname", {:foo => "bar"})
+        end
+
+        it "should fail if the resource parameters are not a hash" do
+            lambda { @resource.create_resource("mytype", "myname", %w{foo bar}) }.should raise_error(ArgumentError)
+        end
+    end
+
+    describe "when an unknown method is called" do
+        it "should create a resource if the method name is a valid type" do
+            @resource.expects(:valid_type?).with(:mytype).returns true
+            @resource.expects(:create_resource).with(:mytype, "myname", {:foo => "bar"}).returns true
+
+            @resource.mytype("myname", :foo => "bar")
+        end
+
+        it "should call any function whose name matches the undefined method if the name is not a valid type" do
+            @resource.expects(:valid_type?).with(:myfunc).returns false
+            @resource.expects(:create_resource).never
+
+            Puppet::Parser::Functions.expects(:function).with(:myfunc).returns true
+
+            @resource.expects(:call_function).with(:myfunc, %w{foo bar})
+
+            @resource.myfunc("foo", "bar")
+        end
+
+        it "should raise a method missing error if the method is neither a type nor a function" do
+            @resource.expects(:valid_type?).with(:myfunc).returns false
+            @resource.expects(:create_resource).never
+
+            Puppet::Parser::Functions.expects(:function).with(:myfunc).returns false
+
+            @resource.expects(:call_function).never
+
+            lambda { @resource.myfunc("foo", "bar") }.should raise_error(NoMethodError)
+        end
+    end
+end
diff --git a/spec/unit/dsl/resource_type_api.rb b/spec/unit/dsl/resource_type_api.rb
new file mode 100755
index 0000000..8d63bf2
--- /dev/null
+++ b/spec/unit/dsl/resource_type_api.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/dsl/resource_type_api'
+
+class DSLAPITester
+    include Puppet::DSL::ResourceTypeAPI
+end
+
+describe Puppet::DSL::ResourceTypeAPI do
+    before do
+        @api = DSLAPITester.new
+    end
+
+    [:definition, :node, :hostclass].each do |type|
+        method = type == :definition ? "resource_type" : type
+        it "should be able to create a #{type}" do
+            newtype = Puppet::Resource::Type.new(:hostclass, "foo")
+            Puppet::Resource::Type.expects(:new).with { |t, n, args| t == type }.returns newtype
+            @api.send(method, "myname")
+        end
+
+        it "should use the provided name when creating a #{type}" do
+            type = Puppet::Resource::Type.new(:hostclass, "foo")
+            Puppet::Resource::Type.expects(:new).with { |t, n, args| n == "myname" }.returns type
+            @api.send(method, "myname")
+        end
+
+        unless type == :definition
+            it "should pass in any provided options" do
+                type = Puppet::Resource::Type.new(:hostclass, "foo")
+                Puppet::Resource::Type.expects(:new).with { |t, n, args| args == {:myarg => :myvalue} }.returns type
+                @api.send(method, "myname", :myarg => :myvalue)
+            end
+        end
+
+        it "should set any provided block as the type's ruby code"
+
+        it "should add the type to the current environment's known resource types"
+    end
+
+    describe "when creating a definition" do
+        it "should use the provided options to define valid arguments for the resource type"
+    end
+end
diff --git a/spec/unit/resource/type.rb b/spec/unit/resource/type.rb
index d1ee024..bb37217 100755
--- a/spec/unit/resource/type.rb
+++ b/spec/unit/resource/type.rb
@@ -9,7 +9,7 @@ describe Puppet::Resource::Type do
         Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo"
     end
 
-    [:code, :doc, :line, :file, :code_collection].each do |attr|
+    [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr|
         it "should have a '#{attr}' attribute" do
             type = Puppet::Resource::Type.new(:hostclass, "foo")
             type.send(attr.to_s + "=", "yay")
@@ -94,19 +94,19 @@ describe Puppet::Resource::Type do
         end
 
         it "should return the name converted to a string when the name is not a regex" do
-            pending "Need to define ResourceTypeCollection behaviour first"
+            pending "Need to define LoadedCode behaviour first"
             name = Puppet::Parser::AST::HostName.new(:value => "foo")
             Puppet::Resource::Type.new(:node, name).name.should == "foo"
         end
 
         it "should return the name converted to a string when the name is a regex" do
-            pending "Need to define ResourceTypeCollection behaviour first"
+            pending "Need to define LoadedCode behaviour first"
             name = Puppet::Parser::AST::HostName.new(:value => /regex/)
             Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s
         end
 
         it "should mark any created scopes as a node scope" do
-            pending "Need to define ResourceTypeCollection behaviour first"
+            pending "Need to define LoadedCode behaviour first"
             name = Puppet::Parser::AST::HostName.new(:value => /regex/)
             Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s
         end
@@ -330,6 +330,8 @@ describe Puppet::Resource::Type do
             @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))
             @scope = Puppet::Parser::Scope.new :compiler => @compiler
             @resource = Puppet::Parser::Resource.new(:foo, "yay", :scope => @scope)
+            @known_resource_types = stub 'known_resource_types'
+            @resource.stubs(:known_resource_types).returns @known_resource_types
             @type = Puppet::Resource::Type.new(:hostclass, "foo")
             @type.stubs(:set_resource_parameters)
         end
@@ -359,7 +361,7 @@ describe Puppet::Resource::Type do
             @compiler.class_scope(@type).should be_nil
         end
 
-        it "should evaluate the code if any is provided" do
+        it "should evaluate the AST code if any is provided" do
             code = stub 'code'
             @type.stubs(:code).returns code
             @type.stubs(:subscope).returns stub("subscope", :compiler => @compiler)
@@ -368,6 +370,33 @@ describe Puppet::Resource::Type do
             @type.evaluate_code(@resource)
         end
 
+        describe "and ruby code is provided" do
+            before do
+                @type.stubs(:ruby_code).returns(proc { "foo" })
+            end
+
+            it "should instance evaluate the ruby code on the resource" do
+                evaluated = false
+                @type.stubs(:ruby_code).returns(proc { evaluated = true })
+
+                @type.evaluate_code(@resource)
+
+                evaluated.should be_true
+            end
+
+            it "should include the DSL Resource Helper module in the provided resource" do
+                @type.evaluate_code(@resource)
+
+                @resource.metaclass.ancestors.should be_include(Puppet::DSL::ResourceHelper)
+            end
+
+            it "should convert the resource's parameters to instance variables" do
+                @resource.expects(:set_instance_variables)
+
+                @type.evaluate_code(@resource)
+            end
+        end
+
         it "should noop if there is no code" do
             @type.expects(:code).returns nil
 
diff --git a/spec/unit/resource/type_collection.rb b/spec/unit/resource/type_collection.rb
index a2a213f..ea78afc 100644
--- a/spec/unit/resource/type_collection.rb
+++ b/spec/unit/resource/type_collection.rb
@@ -40,7 +40,7 @@ describe Puppet::Resource::TypeCollection do
         @code.add(node)
         @code.node("foo").should equal(node)
 
-        node.code_collection.should equal(@code)
+        node.resource_type_collection.should equal(@code)
     end
 
     it "should store node resource types as nodes" do

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list