Also adding JSON support.

This is so that we can remotely retrieve information
about resource types and classes, such as what arguments
are required.

Signed-off-by: Luke Kanies <[email protected]>
---
 ext/auth.conf                                 |  100 +++++++++++++++++++++++++
 ext/puppet-test                               |   24 ++++++
 lib/puppet/indirector/resource_type.rb        |    5 +
 lib/puppet/indirector/resource_type/parser.rb |   27 +++++++
 lib/puppet/indirector/resource_type/rest.rb   |    7 ++
 lib/puppet/resource/type.rb                   |   34 +++++++++
 spec/unit/indirector/resource_type/parser.rb  |   75 ++++++++++++++++++
 spec/unit/indirector/resource_type/rest.rb    |   15 ++++
 spec/unit/resource/type.rb                    |   41 ++++++++++
 9 files changed, 328 insertions(+), 0 deletions(-)
 create mode 100644 ext/auth.conf
 create mode 100644 lib/puppet/indirector/resource_type.rb
 create mode 100644 lib/puppet/indirector/resource_type/parser.rb
 create mode 100644 lib/puppet/indirector/resource_type/rest.rb
 create mode 100755 spec/unit/indirector/resource_type/parser.rb
 create mode 100755 spec/unit/indirector/resource_type/rest.rb

diff --git a/ext/auth.conf b/ext/auth.conf
new file mode 100644
index 0000000..6b0bd17
--- /dev/null
+++ b/ext/auth.conf
@@ -0,0 +1,100 @@
+# This is an example auth.conf file, it mimics the puppetmasterd defaults
+#
+# The ACL are checked in order of appearance in this file.
+#
+# Supported syntax:
+# This file supports two different syntax depending on how
+# you want to express the ACL.
+#
+# Path syntax (the one used below):
+# ---------------------------------
+# path /path/to/resource
+# [environment envlist]
+# [method methodlist]
+# [auth[enthicated] {yes|no|on|off|any}]
+# allow [host|ip|*]
+# deny [host|ip]
+#
+# The path is matched as a prefix. That is /file match at
+# the same time /file_metadat and /file_content.
+#
+# Regex syntax:
+# -------------
+# This one is differenciated from the path one by a '~'
+#
+# path ~ regex
+# [environment envlist]
+# [method methodlist]
+# [auth[enthicated] {yes|no|on|off|any}]
+# allow [host|ip|*]
+# deny [host|ip]
+#
+# The regex syntax is the same as ruby ones.
+#
+# Ex:
+# path ~ .pp$
+# will match every resource ending in .pp (manifests files for instance)
+#
+# path ~ ^/path/to/resource
+# is essentially equivalent to path /path/to/resource
+#
+# environment:: restrict an ACL to a specific set of environments
+# method:: restrict an ACL to a specific set of methods
+# auth:: restrict an ACL to an authenticated or unauthenticated request
+# the default when unspecified is to restrict the ACL to authenticated requests
+# (ie exactly as if auth yes was present).
+#
+
+### Authenticated ACL - those applies only when the client
+### has a valid certificate and is thus authenticated
+
+# allow nodes to retrieve their own catalog (ie their configuration)
+#path ~ ^/catalog/([^/]+)$
+#method find
+#allow *
+#allow $1
+
+# allow all nodes to access the certificates services
+#path /certificate_revocation_list/ca
+#method find
+#allow *
+
+# allow all nodes to access plugins
+#path /file_metadata/plugins
+#allow *
+
+# allow all nodes to store their reports
+#path /report
+#method save
+#allow *
+
+# inconditionnally allow access to all files services
+# which means in practice that fileserver.conf will
+# still be used
+#path /file
+#allow *
+
+### Unauthenticated ACL, for clients for which the current master doesn't
+### have a valid certificate
+
+# allow access to the master CA
+#path /certificate/ca
+#auth no
+#method find
+#allow *
+
+#path /certificate/
+#auth no
+#method find
+#allow *
+
+#path /certificate_request
+#auth no
+#method find, save
+#allow *
+
+# this one is not stricly necessary, but it has the merit
+# to show the default policy which is deny everything else
+#path /
+#auth any
+#allow *
diff --git a/ext/puppet-test b/ext/puppet-test
index 5333307..61397e7 100755
--- a/ext/puppet-test
+++ b/ext/puppet-test
@@ -229,6 +229,30 @@ Suite.new :local_catalog, "Local catalog handling" do
     end
 end
 
+Suite.new :resource_type, "Managing resource types" do
+    newtest :find, "Find a type" do
+        Puppet::Resource::Type.terminus_class = :parser
+        ARGV.each do |name|
+            json = Puppet::Resource::Type.find(name).to_pson
+            data = PSON.parse(json)
+            p Puppet::Resource::Type.from_pson(data)
+        end
+    end
+
+    newtest :search_types, "Find all types" do
+        Puppet::Resource::Type.terminus_class = :rest
+        result = Puppet::Resource::Type.search("*")
+        result.each { |r| p r }
+    end
+
+    newtest :restful_type, "Find a type and return it via REST" do
+        Puppet::Resource::Type.terminus_class = :rest
+        ARGV.each do |name|
+            p Puppet::Resource::Type.find(name)
+        end
+    end
+end
+
 Suite.new :remote_catalog, "Remote catalog handling" do
     def prepare
         $args[:cache] = false
diff --git a/lib/puppet/indirector/resource_type.rb 
b/lib/puppet/indirector/resource_type.rb
new file mode 100644
index 0000000..0564dc2
--- /dev/null
+++ b/lib/puppet/indirector/resource_type.rb
@@ -0,0 +1,5 @@
+require 'puppet/resource/type'
+
+# A stub class, so our constants work.
+class Puppet::Indirector::ResourceType # :nodoc:
+end
diff --git a/lib/puppet/indirector/resource_type/parser.rb 
b/lib/puppet/indirector/resource_type/parser.rb
new file mode 100644
index 0000000..81ec1bf
--- /dev/null
+++ b/lib/puppet/indirector/resource_type/parser.rb
@@ -0,0 +1,27 @@
+require 'puppet/resource/type'
+require 'puppet/indirector/code'
+require 'puppet/indirector/resource_type'
+
+class Puppet::Indirector::ResourceType::Parser < Puppet::Indirector::Code
+    desc "Return the data-form of a resource type."
+
+    def find(request)
+        krt = request.environment.known_resource_types
+
+        # This is a bit ugly.
+        [:hostclass, :definition, :node].each do |type|
+            if r = krt.send(type, request.key)
+                return r
+            end
+        end
+        nil
+    end
+
+    def search(request)
+        raise ArgumentError, "Only '*' is acceptable as a search request" 
unless request.key == "*"
+        krt = request.environment.known_resource_types
+        result = [krt.hostclasses.values, krt.definitions.values, 
krt.nodes.values].flatten
+        return nil if result.empty?
+        result
+    end
+end
diff --git a/lib/puppet/indirector/resource_type/rest.rb 
b/lib/puppet/indirector/resource_type/rest.rb
new file mode 100644
index 0000000..66d3320
--- /dev/null
+++ b/lib/puppet/indirector/resource_type/rest.rb
@@ -0,0 +1,7 @@
+require 'puppet/resource/type'
+require 'puppet/indirector/rest'
+require 'puppet/indirector/resource_type'
+
+class Puppet::Indirector::ResourceType::Rest < Puppet::Indirector::REST
+    desc "Retrieve resource types via a REST HTTP interface."
+end
diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb
index 402df06..3657f89 100644
--- a/lib/puppet/resource/type.rb
+++ b/lib/puppet/resource/type.rb
@@ -6,6 +6,7 @@ require 'puppet/parser/ast/leaf'
 require 'puppet/dsl'
 
 class Puppet::Resource::Type
+    Puppet::ResourceType = self
     include Puppet::Util::InlineDocs
     include Puppet::Util::Warnings
     include Puppet::Util::Errors
@@ -19,6 +20,38 @@ class Puppet::Resource::Type
         define_method("#{t}?") { self.type == t }
     end
 
+    require 'puppet/indirector'
+    extend Puppet::Indirector
+    indirects :resource_type, :terminus_class => :parser
+
+    def self.from_pson(data)
+        name = data.delete('name') or raise ArgumentError, "Resource Type 
names must be specified"
+        type = data.delete('type') || "definition"
+
+        data = data.inject({}) { |result, ary| result[ary[0].intern] = ary[1]; 
result }
+
+        new(type, name, data)
+    end
+
+    def to_pson_data_hash
+        data = [:code, :doc, :line, :file, :parent].inject({}) do |hash, param|
+            next hash unless value = self.send(param)
+            hash[param.to_s] = value
+            hash
+        end
+
+        data['arguments'] = arguments.dup
+
+        data['name'] = name
+        data['type'] = type
+
+        data
+    end
+
+    def to_pson(*args)
+        to_pson_data_hash.to_pson(*args)
+    end
+
     # Are we a child of the passed class?  Do a recursive search up our
     # parentage tree to figure it out.
     def child_of?(klass)
@@ -260,3 +293,4 @@ class Puppet::Resource::Type
         end
     end
 end
+
diff --git a/spec/unit/indirector/resource_type/parser.rb 
b/spec/unit/indirector/resource_type/parser.rb
new file mode 100755
index 0000000..331ca1c
--- /dev/null
+++ b/spec/unit/indirector/resource_type/parser.rb
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/resource_type/parser'
+
+describe Puppet::Indirector::ResourceType::Parser do
+    before do
+        @terminus = Puppet::Indirector::ResourceType::Parser.new
+        @request = Puppet::Indirector::Request.new(:resource_type, :find, 
"foo")
+        @krt = Puppet::Resource::TypeCollection.new(@request.environment)
+        @request.environment.stubs(:known_resource_types).returns @krt
+    end
+
+    it "should be registered with the resource_type indirection" do
+        Puppet::Indirector::Terminus.terminus_class(:resource_type, 
:parser).should equal(Puppet::Indirector::ResourceType::Parser)
+    end
+
+    describe "when finding" do
+        it "should use the request's environment's list of known resource 
types" do
+            
@request.environment.known_resource_types.expects(:hostclass).returns nil
+
+            @terminus.find(@request)
+        end
+
+        it "should return any found type" do
+            type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo"))
+
+            @terminus.find(@request).should == type
+        end
+
+        it "should return nil if no type can be found" do
+            @terminus.find(@request).should be_nil
+        end
+
+        it "should prefer definitions to nodes" do
+            type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo"))
+            node = @krt.add(Puppet::Resource::Type.new(:node, "foo"))
+
+            @terminus.find(@request).should == type
+        end
+    end
+
+    describe "when searching" do
+        before do
+            @request.key = "*"
+        end
+
+        it "should use the request's environment's list of known resource 
types" do
+            
@request.environment.known_resource_types.expects(:hostclasses).returns({})
+
+            @terminus.search(@request)
+        end
+
+        it "should fail if anyther other than '*' was provided as the search 
key" do
+            @request.key = "foo*"
+            lambda { @terminus.search(@request) }.should 
raise_error(ArgumentError)
+        end
+
+        it "should return all known types" do
+            type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo"))
+            node = @krt.add(Puppet::Resource::Type.new(:node, "bar"))
+            define = @krt.add(Puppet::Resource::Type.new(:definition, "baz"))
+
+            result = @terminus.search(@request)
+            result.should be_include(type)
+            result.should be_include(node)
+            result.should be_include(define)
+        end
+
+        it "should return nil if no types can be found" do
+            @terminus.search(@request).should be_nil
+        end
+    end
+end
diff --git a/spec/unit/indirector/resource_type/rest.rb 
b/spec/unit/indirector/resource_type/rest.rb
new file mode 100755
index 0000000..28196d5
--- /dev/null
+++ b/spec/unit/indirector/resource_type/rest.rb
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/indirector/resource_type/rest'
+
+describe Puppet::Indirector::ResourceType::Rest do
+    it "should be registered with the resource_type indirection" do
+        Puppet::Indirector::Terminus.terminus_class(:resource_type, 
:rest).should equal(Puppet::Indirector::ResourceType::Rest)
+    end
+
+    it "should be a subclass of Puppet::Indirector::Rest" do
+        Puppet::Indirector::ResourceType::Rest.superclass.should == 
Puppet::Indirector::REST
+    end
+end
diff --git a/spec/unit/resource/type.rb b/spec/unit/resource/type.rb
index bbf2f73..8c024d7 100755
--- a/spec/unit/resource/type.rb
+++ b/spec/unit/resource/type.rb
@@ -23,6 +23,47 @@ describe Puppet::Resource::Type do
         end
     end
 
+    it "should indirect 'resource_type'" do
+        Puppet::Resource::Type.indirection.name.should == :resource_type
+    end
+
+    it "should default to 'parser' for its terminus class" do
+        Puppet::Resource::Type.indirection.terminus_class.should == :parser
+    end
+
+    describe "when converting to json" do
+        before do
+            @type = Puppet::Resource::Type.new(:hostclass, "foo")
+        end
+
+        def from_json(json)
+            Puppet::Resource::Type.from_pson(json)
+        end
+
+        def double_convert
+            Puppet::Resource::Type.from_pson(PSON.parse(@type.to_pson))
+        end
+
+        it "should include the name and type" do
+            double_convert.name.should == @type.name
+            double_convert.type.should == @type.type
+        end
+
+        it "should include any arguments" do
+            @type.set_arguments("one" => nil, "two" => "foo")
+
+            double_convert.arguments.should == {"one" => nil, "two" => "foo"}
+        end
+
+        it "should include any extra attributes" do
+            @type.file = "/my/file"
+            @type.line = 50
+
+            double_convert.file.should == "/my/file"
+            double_convert.line.should == 50
+        end
+    end
+
     describe "when a node"  do
         it "should allow a regex as its name" do
             lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not 
raise_error
-- 
1.6.1

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to