[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