diff --git a/lib/puppet/face/facts.rb b/lib/puppet/face/facts.rb index b35026d47..ae9c10ee3 100644 --- a/lib/puppet/face/facts.rb +++ b/lib/puppet/face/facts.rb @@ -1,37 +1,37 @@ require 'puppet/indirector/face' require 'puppet/node/facts' Puppet::Indirector::Face.define(:facts, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "Retrieve and store facts." description <<-'EOT' This subcommand manages facts, which are collections of normalized system information used by Puppet. It can read facts directly from the local system (with the default `facter` terminus). EOT find = get_action(:find) find.summary "Retrieve a node's facts." - find.arguments "" + find.arguments "[]" find.returns <<-'EOT' A hash containing some metadata and (under the "values" key) the set of facts for the requested node. When used from the Ruby API: A Puppet::Node::Facts object. RENDERING ISSUES: Facts cannot currently be rendered as a string; use yaml or json. EOT find.notes <<-'EOT' When using the `facter` terminus, the host argument is ignored. EOT find.examples <<-'EOT' Get facts from the local system: - $ puppet facts find x + $ puppet facts find EOT deactivate_action(:destroy) deactivate_action(:search) end diff --git a/lib/puppet/indirector/face.rb b/lib/puppet/indirector/face.rb index 10fa27ef0..ffc280808 100644 --- a/lib/puppet/indirector/face.rb +++ b/lib/puppet/indirector/face.rb @@ -1,139 +1,148 @@ require 'puppet/face' class Puppet::Indirector::Face < Puppet::Face option "--terminus TERMINUS" do summary "The indirector terminus to use." description <<-EOT Indirector faces expose indirected subsystems of Puppet. These subsystems are each able to retrieve and alter a specific type of data (with the familiar actions of `find`, `search`, `save`, and `destroy`) from an arbitrary number of pluggable backends. In Puppet parlance, these backends are called terminuses. Almost all indirected subsystems have a `rest` terminus that interacts with the puppet master's data. Most of them have additional terminuses for various local data models, which are in turn used by the indirected subsystem on the puppet master whenever it receives a remote request. The terminus for an action is often determined by context, but occasionally needs to be set explicitly. See the "Notes" section of this face's manpage for more details. EOT before_action do |action, args, options| set_terminus(options[:terminus]) end after_action do |action, args, options| indirection.reset_terminus_class end end def self.indirections Puppet::Indirector::Indirection.instances.collect { |t| t.to_s }.sort end def self.terminus_classes(indirection) Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect { |t| t.to_s }.sort end def call_indirection_method(method, key, options) begin result = indirection.__send__(method, key, options) rescue => detail message = "Could not call '#{method}' on '#{indirection_name}': #{detail}" Puppet.log_exception(detail, message) raise RuntimeError, message, detail.backtrace end return result end option "--extra HASH" do summary "Extra arguments to pass to the indirection request" description <<-EOT A terminus can take additional arguments to refine the operation, which are passed as an arbitrary hash to the back-end. Anything passed as the extra value is just send direct to the back-end. EOT default_to do Hash.new end end action :destroy do summary "Delete an object." arguments "" when_invoked {|key, options| call_indirection_method :destroy, key, options[:extra] } end action :find do summary "Retrieve an object by name." - arguments "" - when_invoked {|key, options| call_indirection_method :find, key, options[:extra] } + arguments "[]" + when_invoked do |*args| + # Default the key to Puppet[:certname] if none is supplied + if args.length == 1 + key = Puppet[:certname] + options = args.last + else + key, options = *args + end + call_indirection_method :find, key, options[:extra] + end end action :save do summary "API only: create or overwrite an object." arguments "" description <<-EOT API only: create or overwrite an object. As the Faces framework does not currently accept data from STDIN, save actions cannot currently be invoked from the command line. EOT when_invoked {|key, options| call_indirection_method :save, key, options[:extra] } end action :search do summary "Search for an object or retrieve multiple objects." arguments "" when_invoked {|key, options| call_indirection_method :search, key, options[:extra] } end # Print the configuration for the current terminus class action :info do summary "Print the default terminus class for this face." description <<-EOT Prints the default terminus class for this subcommand. Note that different run modes may have different default termini; when in doubt, specify the run mode with the '--run_mode' option. EOT when_invoked do |options| if t = indirection.terminus_class "Run mode '#{Puppet.run_mode.name}': #{t}" else "No default terminus class for run mode '#{Puppet.run_mode.name}'" end end end attr_accessor :from def indirection_name @indirection_name || name.to_sym end # Here's your opportunity to override the indirection name. By default it # will be the same name as the face. def set_indirection_name(name) @indirection_name = name end # Return an indirection associated with a face, if one exists; # One usually does. def indirection unless @indirection @indirection = Puppet::Indirector::Indirection.instance(indirection_name) @indirection or raise "Could not find terminus for #{indirection_name}" end @indirection end def set_terminus(from) begin indirection.terminus_class = from rescue => detail msg = "Could not set '#{indirection.name}' terminus to '#{from}' (#{detail}); valid terminus types are #{self.class.terminus_classes(indirection.name).join(", ") }" raise detail, msg, detail.backtrace end end end diff --git a/spec/unit/application/facts_spec.rb b/spec/unit/application/facts_spec.rb index a81b0f751..4739389b0 100755 --- a/spec/unit/application/facts_spec.rb +++ b/spec/unit/application/facts_spec.rb @@ -1,31 +1,23 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/application/facts' describe Puppet::Application::Facts do before :each do subject.command_line.stubs(:subcommand_name).returns 'facts' end - it "should fail if no key is given to find" do - subject.command_line.stubs(:args).returns %w{find} - expect { - expect { subject.run }.to exit_with(1) - }.to have_printed(/Error: puppet facts find takes 1 argument, but you gave 0/) - @logs.first.to_s.should =~ /puppet facts find takes 1 argument, but you gave 0/ - end - it "should return facts if a key is given to find" do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Node::Facts.indirection.expects(:find).returns(Puppet::Node::Facts.new('whatever', {})) subject.command_line.stubs(:args).returns %w{find whatever --render-as yaml} expect { expect { subject.run }.to exit_with(0) }.to have_printed(/object:Puppet::Node::Facts/) @logs.should be_empty end end diff --git a/spec/unit/face/facts_spec.rb b/spec/unit/face/facts_spec.rb index bfaeed8c3..853b8ff93 100755 --- a/spec/unit/face/facts_spec.rb +++ b/spec/unit/face/facts_spec.rb @@ -1,13 +1,9 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/face' describe Puppet::Face[:facts, '0.0.1'] do describe "#find" do it { should be_action :find } - - it "should fail without a key" do - expect { subject.find }.to raise_error ArgumentError, /wrong number of arguments/ - end end end diff --git a/spec/unit/indirector/face_spec.rb b/spec/unit/indirector/face_spec.rb index b4c601f72..575a5cc99 100755 --- a/spec/unit/indirector/face_spec.rb +++ b/spec/unit/indirector/face_spec.rb @@ -1,70 +1,75 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/indirector/face' describe Puppet::Indirector::Face do subject do instance = Puppet::Indirector::Face.new(:test, '0.0.1') indirection = stub('indirection', :name => :stub_indirection, :reset_terminus_class => nil) instance.stubs(:indirection).returns indirection instance end it { should be_option :extra } it "should be able to return a list of indirections" do Puppet::Indirector::Face.indirections.should be_include("catalog") end it "should return the sorted to_s list of terminus classes" do Puppet::Indirector::Terminus.expects(:terminus_classes).returns([ :yaml, :compiler, :rest ]) Puppet::Indirector::Face.terminus_classes(:catalog).should == [ 'compiler', 'rest', 'yaml' ] end describe "as an instance" do it "should be able to determine its indirection" do # Loading actions here can get, um, complicated Puppet::Face.stubs(:load_actions) Puppet::Indirector::Face.new(:catalog, '0.0.1').indirection.should equal(Puppet::Resource::Catalog.indirection) end end [:find, :search, :save, :destroy].each do |method| it "should define a '#{method}' action" do Puppet::Indirector::Face.should be_action(method) end it "should call the indirection method with options when the '#{method}' action is invoked" do subject.indirection.expects(method).with(:test, {}) subject.send(method, :test) end it "should forward passed options" do subject.indirection.expects(method).with(:test, {'one'=>'1'}) subject.send(method, :test, :extra => {'one'=>'1'}) end end + it "should default key to certname for find action" do + subject.indirection.expects(:find).with(Puppet[:certname], {'one'=>'1'}) + subject.send(:find, :extra => {'one'=>'1'}) + end + it "should be able to override its indirection name" do subject.set_indirection_name :foo subject.indirection_name.should == :foo end it "should be able to set its terminus class" do subject.indirection.expects(:terminus_class=).with(:myterm) subject.set_terminus(:myterm) end it "should define a class-level 'info' action" do Puppet::Indirector::Face.should be_action(:info) end end