diff --git a/lib/puppet/face/certificate_request.rb b/lib/puppet/face/certificate_request.rb index aa29195fc..e4855e987 100644 --- a/lib/puppet/face/certificate_request.rb +++ b/lib/puppet/face/certificate_request.rb @@ -1,52 +1,54 @@ require 'puppet/indirector/face' Puppet::Indirector::Face.define(:certificate_request, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "Manage certificate requests." description <<-EOT This subcommand retrieves and submits certificate signing requests (CSRs). EOT deactivate_action(:destroy) find = get_action(:find) find.summary "Retrieve a single CSR." - find.arguments "" + find.arguments "[]" find.render_as = :s find.returns <<-EOT A single certificate request. When used from the Ruby API, returns a Puppet::SSL::CertificateRequest object. + + Defaults to the current nodes certname. EOT find.examples <<-EOT Retrieve a single CSR from the puppet master's CA: $ puppet certificate_request find somenode.puppetlabs.lan --terminus rest EOT search = get_action(:search) search.summary "Retrieve all outstanding CSRs." search.arguments "" search.render_as = :s search.returns <<-EOT A list of certificate requests. When used from the Ruby API, returns an array of Puppet::SSL::CertificateRequest objects. EOT search.short_description <<-EOT Retrieves all outstanding certificate signing requests. Due to a known bug, this action requires a dummy search key, the content of which is irrelevant. EOT search.notes <<-EOT Although this action always returns all CSRs, it requires a dummy search key; this is a known bug. EOT search.examples <<-EOT Retrieve all CSRs from the local CA (similar to 'puppet cert list'): $ puppet certificate_request search x --terminus ca EOT get_action(:save).summary "API only: submit a certificate signing request." get_action(:save).arguments "" end diff --git a/lib/puppet/face/certificate_revocation_list.rb b/lib/puppet/face/certificate_revocation_list.rb index a004a0db2..36f507839 100644 --- a/lib/puppet/face/certificate_revocation_list.rb +++ b/lib/puppet/face/certificate_revocation_list.rb @@ -1,57 +1,54 @@ require 'puppet/indirector/face' Puppet::Indirector::Face.define(:certificate_revocation_list, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "Manage the list of revoked certificates." description <<-EOT This subcommand is primarily for retrieving the certificate revocation list from the CA. EOT find = get_action(:find) find.summary "Retrieve the certificate revocation list." - find.arguments "" find.render_as = :s find.returns <<-EOT The certificate revocation list. When used from the Ruby API: returns an OpenSSL::X509::CRL object. EOT find.short_description <<-EOT - Retrieves the certificate revocation list. Due to a known bug, this action - requires a dummy argument, the content of which is irrelevant. + Retrieves the certificate revocation list. EOT find.notes <<-EOT - Although this action always returns the CRL from the specified terminus, it - requires a dummy argument; this is a known bug. + Although this action always returns the CRL from the specified terminus. EOT find.examples <<-EXAMPLES Retrieve a copy of the puppet master's CRL: - $ puppet certificate_revocation_list find crl --terminus rest + $ puppet certificate_revocation_list find --terminus rest EXAMPLES destroy = get_action(:destroy) destroy.summary "Delete the certificate revocation list." destroy.arguments "" destroy.returns "Nothing." destroy.description <<-EOT Deletes the certificate revocation list. This cannot be done over REST, but it is possible to delete the locally cached copy or the local CA's copy of the CRL. EOT destroy.short_description <<-EOT Deletes the certificate revocation list. This cannot be done over REST, but it is possible to delete the locally cached copy or the local CA's copy of the CRL. Due to a known bug, this action requires a dummy argument, the content of which is irrelevant. EOT destroy.notes <<-EOT Although this action always deletes the CRL from the specified terminus, it requires a dummy argument; this is a known bug. EOT deactivate_action(:search) deactivate_action(:save) end diff --git a/lib/puppet/face/facts.rb b/lib/puppet/face/facts.rb index b35026d47..54036e2ce 100644 --- a/lib/puppet/face/facts.rb +++ b/lib/puppet/face/facts.rb @@ -1,37 +1,38 @@ 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 + find.default = true deactivate_action(:destroy) deactivate_action(:search) end diff --git a/lib/puppet/face/resource_type.rb b/lib/puppet/face/resource_type.rb index 723609a80..ef74cb4d0 100644 --- a/lib/puppet/face/resource_type.rb +++ b/lib/puppet/face/resource_type.rb @@ -1,79 +1,84 @@ require 'puppet/indirector/face' Puppet::Indirector::Face.define(:resource_type, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "View classes, defined resource types, and nodes from all manifests." description <<-'EOT' This subcommand reads information about the resource collections (classes, nodes, and defined types) available in Puppet's site manifest and modules. It will eventually be extended to examine native resource types. EOT notes <<-'EOT' The `find` and `search` actions return similar hashes of resource collection info. These hashes will include the following four keys: * `file` (a string) * `name` (a string) * `type` (, , or ) * `line` (an integer) They may optionally include the following keys: * `parent` () * `arguments` (a hash of parameters and default values) * `doc` (a string) EOT deactivate_action(:save) deactivate_action(:destroy) find = get_action(:find) find.summary "Retrieve info about a resource collection." find.arguments "" find.returns <<-'EOT' A hash of info about the requested resource collection. When used from the Ruby API: returns a Puppet::Resource::Type object. RENDERING ISSUES: yaml and string output for this indirection are currently unusable; use json instead. EOT find.notes <<-'EOT' If two resource collections share the same name (e.g. you have both a node and a class named "default"), `find` will only return one of them. This can be worked around by using `search` instead. EOT find.examples <<-'EOT' Retrieve info about a specific locally-defined class: $ puppet resource_type find ntp::disabled Retrieve info from the puppet master about a specific class: $ puppet resource_type find ntp --terminus rest EOT + # For this face we don't want to default to the certname like other indirector + # based faces. Instead we want the user to always supply a argument. + find.when_invoked = Proc.new do |key, options| + call_indirection_method :find, key, options[:extra] + end search = get_action(:search) search.summary "Search for collections matching a regular expression." search.arguments "" search.returns <<-'EOT' An array of hashes of resource collection info. When used from the Ruby API: returns an array of Puppet::Resource::Type objects. RENDERING ISSUES: yaml and string output for this indirection are currently unusable; use json instead. EOT search.examples <<-'EOT' Retrieve all classes, nodes, and defined types: $ puppet resource_type search '.*' Search for classes related to Nagios: $ puppet resource_type search nagios EOT end diff --git a/lib/puppet/face/status.rb b/lib/puppet/face/status.rb index 3003dc484..932585308 100644 --- a/lib/puppet/face/status.rb +++ b/lib/puppet/face/status.rb @@ -1,52 +1,47 @@ require 'puppet/indirector/face' Puppet::Indirector::Face.define(:status, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "View puppet server status." deactivate_action(:destroy) deactivate_action(:save) deactivate_action(:search) find = get_action(:find) find.default = true find.summary "Check status of puppet master server." - find.arguments "" find.returns <<-'EOT' A "true" response or a low-level connection error. When used from the Ruby API: returns a Puppet::Status object. EOT find.description <<-'EOT' Checks whether a Puppet server is properly receiving and processing HTTP requests. This action is only useful when used with '--terminus rest'; when invoked with the `local` terminus, `find` will always return true. Over REST, this action will query the configured puppet master by default. To query other servers, including puppet agent nodes started with the <--listen> option, you can set the global <--server> and <--masterport> options on the command line; note that agent nodes listen on port 8139. EOT find.short_description <<-EOT Checks whether a Puppet server is properly receiving and processing HTTP - requests. Due to a known bug, this action requires a dummy argument, the - content of which is irrelevant. This action is only useful when used with - '--terminus rest', and will always return true when invoked locally. + requests. This action is only useful when used with '--terminus rest', + and will always return true when invoked locally. EOT find.notes <<-'EOT' This action requires that the server's `auth.conf` file allow find access to the `status` REST terminus. Puppet agent does not use this facility, and it is turned off by default. See for more details. - - Although this action always returns an unnamed status object, it requires a - dummy argument. This is a known bug. EOT find.examples <<-'EOT' Check the status of the configured puppet master: - $ puppet status find x --terminus rest + $ puppet status find --terminus rest EOT 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