diff options
-rw-r--r-- | lib/puppet/application/resource.rb | 56 | ||||
-rw-r--r-- | lib/puppet/indirector/resource/ral.rb | 48 | ||||
-rw-r--r-- | lib/puppet/indirector/resource/rest.rb | 5 | ||||
-rw-r--r-- | lib/puppet/indirector/rest.rb | 2 | ||||
-rw-r--r-- | lib/puppet/network/rest_authconfig.rb | 1 | ||||
-rw-r--r-- | lib/puppet/resource.rb | 17 | ||||
-rw-r--r-- | lib/puppet/resource/catalog.rb | 3 | ||||
-rw-r--r-- | lib/puppet/type.rb | 6 | ||||
-rw-r--r-- | lib/puppet/util/settings.rb | 1 | ||||
-rwxr-xr-x | spec/unit/application/resource.rb | 89 | ||||
-rw-r--r-- | spec/unit/indirector/resource/ral.rb | 129 | ||||
-rwxr-xr-x | spec/unit/indirector/resource/rest.rb | 11 | ||||
-rwxr-xr-x | spec/unit/indirector/rest.rb | 15 | ||||
-rwxr-xr-x | spec/unit/network/rest_authconfig.rb | 1 | ||||
-rwxr-xr-x | spec/unit/resource.rb | 19 |
15 files changed, 311 insertions, 92 deletions
diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb index f914a1b53..0046fc1d8 100644 --- a/lib/puppet/application/resource.rb +++ b/lib/puppet/application/resource.rb @@ -71,52 +71,24 @@ Puppet::Application.new(:resource) do trans.to_manifest } - text = if @host - client = Puppet::Network::Client.resource.new(:Server => @host, :Port => Puppet[:puppetport]) - unless client.read_cert - raise "client.read_cert failed" - end - begin - # They asked for a single resource. - if name - transbucket = [client.describe(type, name)] - else - # Else, list the whole thing out. - transbucket = client.instances(type) - end - rescue Puppet::Network::XMLRPCClientError => exc - raise "client.list(#{type}) failed: #{exc.message}" - end - transbucket.sort { |a,b| a.name <=> b.name }.collect(&format) + if @host + Puppet::Resource.indirection.terminus_class = :rest + port = Puppet[:puppetport] + key = ["https://#{host}:#{port}", "production", "resources", type, name].join('/') else - if name - obj = typeobj.instances.find { |o| o.name == name } || typeobj.new(:name => name, :check => properties) - vals = obj.retrieve - - unless params.empty? - params.each do |param, value| - obj[param] = value - end - catalog = Puppet::Resource::Catalog.new - catalog.add_resource obj - begin - catalog.apply - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - end + key = [type, name].join('/') + end - end - [format.call(obj.to_trans(true))] + text = if name + if params.empty? + [ Puppet::Resource.find( key ) ] else - typeobj.instances.collect do |obj| - next if ARGV.length > 0 and ! ARGV.include? obj.name - trans = obj.to_trans(true) - format.call(trans) - end + request = Puppet::Indirector::Request.new(:resource, :save, key) # Yuck. + [ Puppet::Resource.new( type, name, params ).save( request ) ] end - end.compact.join("\n") + else + Puppet::Resource.search( key, {} ) + end.map(&format).join("\n") if options[:edit] file = "/tmp/x2puppet-#{Process.pid}.pp" diff --git a/lib/puppet/indirector/resource/ral.rb b/lib/puppet/indirector/resource/ral.rb new file mode 100644 index 000000000..f2c3f847d --- /dev/null +++ b/lib/puppet/indirector/resource/ral.rb @@ -0,0 +1,48 @@ +class Puppet::Resource::Ral < Puppet::Indirector::Code + def find( request ) + # find by name + res = type(request).instances.find { |o| o.name == resource_name(request) } + res ||= type(request).new(:name => resource_name(request), :check => type(request).properties.collect { |s| s.name }) + + return res.to_resource + end + + def search( request ) + conditions = request.options.dup + conditions[:name] = resource_name(request) if resource_name(request) + + type(request).instances.map do |res| + res.to_resource + end.find_all do |res| + conditions.all? {|property, value| res.to_resource[property].to_s == value.to_s} + end.sort do |a,b| + a.title <=> b.title + end + end + + def save( request ) + # In RAL-land, to "save" means to actually try to change machine state + res = request.instance + ral_res = res.to_ral + + catalog = Puppet::Resource::Catalog.new + catalog.add_resource ral_res + catalog.apply + + return ral_res.to_resource + end + + private + + def type_name( request ) + request.key.split('/')[0] + end + + def resource_name( request ) + request.key.split('/')[1] + end + + def type( request ) + Puppet::Type.type(type_name(request)) or raise Puppet::Error "Could not find type #{type}" + end +end diff --git a/lib/puppet/indirector/resource/rest.rb b/lib/puppet/indirector/resource/rest.rb new file mode 100644 index 000000000..7848ae65e --- /dev/null +++ b/lib/puppet/indirector/resource/rest.rb @@ -0,0 +1,5 @@ +require 'puppet/indirector/status' +require 'puppet/indirector/rest' + +class Puppet::Resource::Rest < Puppet::Indirector::REST +end diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index a89e98606..4fd385919 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -67,7 +67,7 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus def find(request) return nil unless result = deserialize(network(request).get(indirection2uri(request), headers)) - result.name = request.key + result.name = request.key if result.respond_to?(:name=) result end diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb index 01ed412cd..7c0ef9cf3 100644 --- a/lib/puppet/network/rest_authconfig.rb +++ b/lib/puppet/network/rest_authconfig.rb @@ -16,6 +16,7 @@ module Puppet { :acl => "/certificate/", :method => :find, :authenticated => false }, { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, { :acl => "/status", :method => [:find], :authenticated => true }, + { :acl => "/resource", :method => [:find, :save, :search], :authenticated => true }, ] def self.main diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index e733f3ee0..bdd11fcc5 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -1,6 +1,6 @@ require 'puppet' require 'puppet/util/tagging' -require 'puppet/resource/reference' +#require 'puppet/resource/reference' require 'puppet/util/pson' # The simplest resource class. Eventually it will function as the @@ -12,6 +12,10 @@ class Puppet::Resource attr_accessor :file, :line, :catalog, :exported, :virtual attr_writer :type, :title + require 'puppet/indirector' + extend Puppet::Indirector + indirects :resource, :terminus_class => :ral + ATTRIBUTES = [:file, :line, :exported] def self.from_pson(pson) @@ -225,6 +229,17 @@ class Puppet::Resource return result end + def name + # this is potential namespace conflict + # between the notion of an "indirector name" + # and a "resource name" + [ type, title ].join('/') + end + + def to_resource + self + end + private # Produce a canonical method name. diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb index c5ae8f52d..6e064a9a1 100644 --- a/lib/puppet/resource/catalog.rb +++ b/lib/puppet/resource/catalog.rb @@ -1,3 +1,4 @@ +require 'puppet/resource' require 'puppet/node' require 'puppet/indirector' require 'puppet/simple_graph' @@ -79,7 +80,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph # If the name and title differ, set up an alias - if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.name != resource.title + if resource.respond_to?(:name) and resource.respond_to?(:title) and resource.respond_to?(:isomorphic?) and resource.name != resource.title self.alias(resource, resource.name) if resource.isomorphic? end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 1df84f2df..2fb4abca8 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -2025,6 +2025,12 @@ class Type return trans end + def to_resource + # this 'type instance' versus 'resource' distinction seems artificial + # I'd like to see it collapsed someday ~JW + self.to_trans.to_resource + end + %w{exported virtual}.each do |m| define_method(m+"?") do self.send(m) diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index 08d42d913..ad1b947b3 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -1,6 +1,5 @@ require 'puppet' require 'sync' -require 'puppet/transportable' require 'getoptlong' require 'puppet/external/event-loop' diff --git a/spec/unit/application/resource.rb b/spec/unit/application/resource.rb index 98b4485ed..9d47ba56e 100755 --- a/spec/unit/application/resource.rb +++ b/spec/unit/application/resource.rb @@ -179,75 +179,76 @@ describe "resource" do before :each do @resource.stubs(:puts) @resource.host = 'host' - @client = stub_everything 'client' - @client.stubs(:read_cert).returns(true) - @client.stubs(:instances).returns([]) - Puppet::Network::Client.resource.stubs(:new).returns(@client) + + Puppet::Resource.stubs(:find ).never + Puppet::Resource.stubs(:search).never + Puppet::Resource.stubs(:save ).never end - it "should connect to it" do - Puppet::Network::Client.resource.expects(:new).with { |h| h[:Server] == 'host' }.returns(@client) + it "should search for resources" do + Puppet::Resource.expects(:search).with('https://host:8139/production/resources/type/', {}).returns([]) @resource.main end - it "should raise an error if there are no certs" do - @client.stubs(:read_cert).returns(nil) - - lambda { @resource.main }.should raise_error + it "should describe the given resource" do + push_args('type','name') + x = stub_everything 'resource' + Puppet::Resource.expects(:find).with('https://host:8139/production/resources/type/name').returns(x) + @resource.main + pop_args end - it "should retrieve all the instances if there is no name" do - @client.expects(:instances).returns([]) + it "should add given parameters to the object" do + push_args('type','name','param=temp') - @resource.main - end + res = stub "resource" + res.expects(:save).with{|x| x.uri == 'https://host:8139/production/resources/type/name'}.returns(res) + res.expects(:collect) + res.expects(:to_manifest) + Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) - it "should describe the given resource" do - push_args('type','name') - @client.expects(:describe).returns(stub_everything) @resource.main pop_args end + end describe "without a host" do before :each do @resource.stubs(:puts) @resource.host = nil + + Puppet::Resource.stubs(:find ).never + Puppet::Resource.stubs(:search).never + Puppet::Resource.stubs(:save ).never end - it "should retrieve all the instances if there is no name" do - @type.expects(:instances).returns([]) + it "should search for resources" do + Puppet::Resource.expects(:search).with('type/', {}).returns([]) + @resource.main + end + it "should describe the given resource" do + push_args('type','name') + x = stub_everything 'resource' + Puppet::Resource.expects(:find).with('type/name').returns(x) @resource.main + pop_args end - describe 'but with a given name' do - before :each do - push_args('type','name') - @type.stubs(:new).returns(:bob) - end - - after :each do - pop_args - end - - it "should retrieve a specific instance if it exists" do - pending - end - - it "should create a stub instance if it doesn't exist" do - pending - end - - it "should add given parameters to the object" do - push_args('type','name','param=temp') - pending - @object.expects(:[]=).with('param','temp') - @resource.main - pop_args - end + it "should add given parameters to the object" do + push_args('type','name','param=temp') + + res = stub "resource" + res.expects(:save).with{|x| x.uri == nil}.returns(res) + res.expects(:collect) + res.expects(:to_manifest) + Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) + + @resource.main + pop_args end + end end end diff --git a/spec/unit/indirector/resource/ral.rb b/spec/unit/indirector/resource/ral.rb new file mode 100644 index 000000000..f74bf3d60 --- /dev/null +++ b/spec/unit/indirector/resource/ral.rb @@ -0,0 +1,129 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "Puppet::Resource::Ral" do + describe "find" do + before do + @request = stub 'request', :key => "user/root" + end + + it "should find an existing instance" do + my_resource = stub "my user resource" + + wrong_instance = stub "wrong user", :name => "bob" + my_instance = stub "my user", :name => "root", :to_resource => my_resource + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ wrong_instance, my_instance, wrong_instance ]) + Puppet::Resource::Ral.new.find(@request).should == my_resource + end + + it "if there is no instance, it should create one" do + wrong_instance = stub "wrong user", :name => "bob" + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ wrong_instance, wrong_instance ]) + result = Puppet::Resource::Ral.new.find(@request) + result.should be_is_a Puppet::Resource + result.title.should == "root" + end + end + + describe "search" do + before do + @request = stub 'request', :key => "user/", :options => {} + end + + it "should convert ral resources into regular resources" do + my_resource = stub "my user resource" + my_instance = stub "my user", :name => "root", :to_resource => my_resource + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ my_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [my_resource] + end + + it "should filter results by name if there's a name in the key" do + my_resource = stub "my user resource" + my_resource.stubs(:to_resource).returns(my_resource) + my_resource.stubs(:[]).with(:name).returns("root") + + wrong_resource = stub "wrong resource" + wrong_resource.stubs(:to_resource).returns(wrong_resource) + wrong_resource.stubs(:[]).with(:name).returns("bad") + + my_instance = stub "my user", :to_resource => my_resource + wrong_instance = stub "wrong user", :to_resource => wrong_resource + + @request = stub 'request', :key => "user/root", :options => {} + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [my_resource] + end + + it "should filter results by query parameters" do + wrong_resource = stub "my user resource" + wrong_resource.stubs(:to_resource).returns(wrong_resource) + wrong_resource.stubs(:[]).with(:name).returns("root") + + my_resource = stub "wrong resource" + my_resource.stubs(:to_resource).returns(my_resource) + my_resource.stubs(:[]).with(:name).returns("bob") + + my_instance = stub "my user", :to_resource => my_resource + wrong_instance = stub "wrong user", :to_resource => wrong_resource + + @request = stub 'request', :key => "user/", :options => {:name => "bob"} + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [my_resource] + end + + it "should return sorted results" do + a_resource = stub "alice resource" + a_resource.stubs(:to_resource).returns(a_resource) + a_resource.stubs(:title).returns("alice") + + b_resource = stub "bob resource" + b_resource.stubs(:to_resource).returns(b_resource) + b_resource.stubs(:title).returns("bob") + + a_instance = stub "alice user", :to_resource => a_resource + b_instance = stub "bob user", :to_resource => b_resource + + @request = stub 'request', :key => "user/", :options => {} + + require 'puppet/type/user' + Puppet::Type::User.expects(:instances).returns([ b_instance, a_instance ]) + Puppet::Resource::Ral.new.search(@request).should == [a_resource, b_resource] + end + end + + describe "save" do + before do + @rebuilt_res = stub 'rebuilt instance' + @ral_res = stub 'ral resource', :to_resource => @rebuilt_res + @instance = stub 'instance', :to_ral => @ral_res + @request = stub 'request', :key => "user/", :instance => @instance + @catalog = stub 'catalog' + + Puppet::Resource::Catalog.stubs(:new).returns(@catalog) + @catalog.stubs(:apply) + @catalog.stubs(:add_resource) + end + + it "should apply a new catalog with a ral object in it" do + Puppet::Resource::Catalog.expects(:new).returns(@catalog) + @catalog.expects(:add_resource).with(@ral_res) + @catalog.expects(:apply) + Puppet::Resource::Ral.new.save(@request) + end + + it "should return a regular resource that used to be the ral resource" do + Puppet::Resource::Ral.new.save(@request).should == @rebuilt_res + end + end +end diff --git a/spec/unit/indirector/resource/rest.rb b/spec/unit/indirector/resource/rest.rb new file mode 100755 index 000000000..d5f2a9d46 --- /dev/null +++ b/spec/unit/indirector/resource/rest.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +require 'puppet/indirector/resource/rest' + +describe Puppet::Resource::Rest do + it "should be a sublcass of Puppet::Indirector::REST" do + Puppet::Resource::Rest.superclass.should equal(Puppet::Indirector::REST) + end +end diff --git a/spec/unit/indirector/rest.rb b/spec/unit/indirector/rest.rb index d12e3c642..1cb34c4f4 100755 --- a/spec/unit/indirector/rest.rb +++ b/spec/unit/indirector/rest.rb @@ -199,15 +199,26 @@ describe Puppet::Indirector::REST do @searcher.find(@request) end - it "should deserialize and return the http response" do + it "should deserialize and return the http response, setting name" do @connection.expects(:get).returns @response - instance = stub 'object', :name= => nil + instance = stub 'object' + instance.expects(:name=) @searcher.expects(:deserialize).with(@response).returns instance @searcher.find(@request).should == instance end + it "should deserialize and return the http response, and not require name=" do + @connection.expects(:get).returns @response + + instance = stub 'object' + @searcher.expects(:deserialize).with(@response).returns instance + + @searcher.find(@request).should == instance + end + + it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:get).with { |path, args| path == "/my/uri" }.returns(@response) diff --git a/spec/unit/network/rest_authconfig.rb b/spec/unit/network/rest_authconfig.rb index 407fc43f4..fe17d5626 100755 --- a/spec/unit/network/rest_authconfig.rb +++ b/spec/unit/network/rest_authconfig.rb @@ -17,6 +17,7 @@ describe Puppet::Network::RestAuthConfig do { :acl => "/certificate/", :method => :find, :authenticated => false }, { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, { :acl => "/status", :method => [:find], :authenticated => true }, + { :acl => "/resource", :method => [:find, :save, :search], :authenticated => true }, ] before :each do diff --git a/spec/unit/resource.rb b/spec/unit/resource.rb index b26f4f923..2b1d49d50 100755 --- a/spec/unit/resource.rb +++ b/spec/unit/resource.rb @@ -492,4 +492,23 @@ describe Puppet::Resource do resource['foo'].should == %w{one} end end + + describe "it should implement to_resource" do + resource = Puppet::Resource.new("file", "/my/file") + resource.to_resource.should == resource + end + + describe "because it is an indirector model" do + it "should include Puppet::Indirector" do + Puppet::Resource.should be_is_a(Puppet::Indirector) + end + + it "should have a default terminus" do + Puppet::Resource.indirection.terminus_class.should == :ral + end + + it "should have a name" do + Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" + end + end end |