[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Max Martin max at puppetlabs.com
Tue May 10 08:10:59 UTC 2011


The following commit has been merged in the experimental branch:
commit e20e6185f7f26d02c7ea275f8adf43c088169129
Author: Max Martin <max at puppetlabs.com>
Date:   Tue Mar 22 18:36:01 2011 -0700

    (#5528) Add REST API for signing, revoking, retrieving, cleaning certs
    
    This commit introduces a new Indirector terminus, certificate_status,
    which allows for signing, revoking, listing, and cleaning
    SSL certificates over HTTP via REST. Documentation for these new
    features can be found in our REST API documentation on the docs site:
    
    http://docs.puppetlabs.com/guides/rest_api.html
    
    This documentation has not been updated as of the writing of this
    commit, but will be very soon. Puppet::SSL::Host is now fully integrated
    into the Indirector.
    
    Paired-with:Matt Robinson, Jacob Helwig, Jesse Wolfe, Richard Crowley,
    Luke Kanies

diff --git a/lib/puppet/indirector/certificate_status.rb b/lib/puppet/indirector/certificate_status.rb
new file mode 100644
index 0000000..47c3adc
--- /dev/null
+++ b/lib/puppet/indirector/certificate_status.rb
@@ -0,0 +1,4 @@
+require 'puppet/indirector'
+
+class Puppet::Indirector::CertificateStatus
+end
diff --git a/lib/puppet/indirector/certificate_status/file.rb b/lib/puppet/indirector/certificate_status/file.rb
new file mode 100644
index 0000000..9061d94
--- /dev/null
+++ b/lib/puppet/indirector/certificate_status/file.rb
@@ -0,0 +1,82 @@
+require 'puppet'
+require 'puppet/indirector/certificate_status'
+require 'puppet/ssl/certificate'
+require 'puppet/ssl/certificate_authority'
+require 'puppet/ssl/certificate_request'
+require 'puppet/ssl/host'
+require 'puppet/ssl/key'
+
+class Puppet::Indirector::CertificateStatus::File < Puppet::Indirector::Code
+  def ca
+    raise ArgumentError, "This process is not configured as a certificate authority" unless Puppet::SSL::CertificateAuthority.ca?
+    Puppet::SSL::CertificateAuthority.new
+  end
+
+  def destroy(request)
+    deleted = []
+    [
+      Puppet::SSL::Certificate,
+      Puppet::SSL::CertificateRequest,
+      Puppet::SSL::Key,
+    ].collect do |part|
+      if part.indirection.destroy(request.key)
+        deleted << "#{part}"
+      end
+    end
+
+    return "Nothing was deleted" if deleted.empty?
+    "Deleted for #{request.key}: #{deleted.join(", ")}"
+  end
+
+  def save(request)
+    if request.instance.desired_state == "signed"
+      certificate_request = Puppet::SSL::CertificateRequest.indirection.find(request.key)
+      raise Puppet::Error, "Cannot sign for host #{request.key} without a certificate request" unless certificate_request
+      ca.sign(request.key)
+    elsif request.instance.desired_state == "revoked"
+      certificate = Puppet::SSL::Certificate.indirection.find(request.key)
+      raise Puppet::Error, "Cannot revoke host #{request.key} because has it doesn't have a signed certificate" unless certificate
+      ca.revoke(request.key)
+    else
+      raise Puppet::Error, "State #{request.instance.desired_state} invalid; Must specify desired state of 'signed' or 'revoked' for host #{request.key}"
+    end
+
+  end
+
+  def search(request)
+    # Support historic interface wherein users provide classes to filter
+    # the search.  When used via the REST API, the arguments must be
+    # a Symbol or an Array containing Symbol objects.
+    klasses = case request.options[:for]
+    when Class
+      [request.options[:for]]
+    when nil
+      [
+        Puppet::SSL::Certificate,
+        Puppet::SSL::CertificateRequest,
+        Puppet::SSL::Key,
+      ]
+    else
+      [request.options[:for]].flatten.map do |klassname|
+        indirection.class.model(klassname.to_sym)
+      end
+    end
+
+    klasses.collect do |klass|
+      klass.indirection.search(request.key, request.options)
+    end.flatten.collect do |result|
+      result.name
+    end.uniq.collect &Puppet::SSL::Host.method(:new)
+  end
+
+  def find(request)
+    ssl_host = Puppet::SSL::Host.new(request.key)
+    public_key = Puppet::SSL::Certificate.indirection.find(request.key)
+
+    if ssl_host.certificate_request || public_key
+      ssl_host
+    else
+      nil
+    end
+  end
+end
diff --git a/lib/puppet/indirector/certificate_status/rest.rb b/lib/puppet/indirector/certificate_status/rest.rb
new file mode 100644
index 0000000..c53b663
--- /dev/null
+++ b/lib/puppet/indirector/certificate_status/rest.rb
@@ -0,0 +1,10 @@
+require 'puppet/ssl/host'
+require 'puppet/indirector/rest'
+require 'puppet/indirector/certificate_status'
+
+class Puppet::Indirector::CertificateStatus::Rest < Puppet::Indirector::REST
+  desc "Sign, revoke, search for, or clean certificates & certificate requests over HTTP."
+
+  use_server_setting(:ca_server)
+  use_port_setting(:ca_port)
+end
diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb
index dcb0e0a..5fe1439 100644
--- a/lib/puppet/network/http/api/v1.rb
+++ b/lib/puppet/network/http/api/v1.rb
@@ -61,6 +61,7 @@ module Puppet::Network::HTTP::API::V1
     # that leads to the fix being too long.
     return :singular if indirection == "facts"
     return :singular if indirection == "status"
+    return :singular if indirection == "certificate_status"
     return :plural if indirection == "inventory"
 
     result = (indirection =~ /s$|_search$/) ? :plural : :singular
diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb
index 7f71ced..b9215ef 100644
--- a/lib/puppet/ssl/host.rb
+++ b/lib/puppet/ssl/host.rb
@@ -1,3 +1,4 @@
+require 'puppet/indirector'
 require 'puppet/ssl'
 require 'puppet/ssl/key'
 require 'puppet/ssl/certificate'
@@ -15,11 +16,17 @@ class Puppet::SSL::Host
   CertificateRequest = Puppet::SSL::CertificateRequest
   CertificateRevocationList = Puppet::SSL::CertificateRevocationList
 
+  extend Puppet::Indirector
+  indirects :certificate_status, :terminus_class => :file
+
   attr_reader :name
   attr_accessor :ca
 
   attr_writer :key, :certificate, :certificate_request
 
+  # This accessor is used in instances for indirector requests to hold desired state
+  attr_accessor :desired_state
+
   class << self
     include Puppet::Util::Cacher
 
@@ -47,6 +54,13 @@ class Puppet::SSL::Host
     CertificateRequest.indirection.terminus_class = terminus
     CertificateRevocationList.indirection.terminus_class = terminus
 
+    host_map = {:ca => :file, :file => nil, :rest => :rest}
+    if term = host_map[terminus]
+      self.indirection.terminus_class = term
+    else
+      self.indirection.reset_terminus_class
+    end
+
     if cache
       # This is weird; we don't actually cache our keys, we
       # use what would otherwise be the cache as our normal
@@ -85,30 +99,34 @@ class Puppet::SSL::Host
 
   # Specify how we expect to interact with our certificate authority.
   def self.ca_location=(mode)
-    raise ArgumentError, "CA Mode can only be #{CA_MODES.collect { |m| m.to_s }.join(", ")}" unless CA_MODES.include?(mode)
+    modes = CA_MODES.collect { |m, vals| m.to_s }.join(", ")
+    raise ArgumentError, "CA Mode can only be one of: #{modes}" unless CA_MODES.include?(mode)
 
     @ca_location = mode
 
     configure_indirection(*CA_MODES[@ca_location])
   end
 
-  # Remove all traces of a given host
+  # Puppet::SSL::Host is actually indirected now so the original implementation
+  # has been moved into the certificate_status indirector.  This method is in-use
+  # in `puppet cert -c <certname>`.
   def self.destroy(name)
-    [Key, Certificate, CertificateRequest].collect { |part| part.indirection.destroy(name) }.any? { |x| x }
+    indirection.destroy(name)
   end
 
-  # Search for more than one host, optionally only specifying
-  # an interest in hosts with a given file type.
-  # This just allows our non-indirected class to have one of
-  # indirection methods.
-  def self.search(options = {})
-    classlist = [options[:for] || [Key, CertificateRequest, Certificate]].flatten
-
-    # Collect the results from each class, flatten them, collect all of the names, make the name list unique,
-    # then create a Host instance for each one.
-    classlist.collect { |klass| klass.indirection.search }.flatten.collect { |r| r.name }.uniq.collect do |name|
-      new(name)
+  def self.from_pson(pson)
+    instance = new(pson["name"])
+    if pson["desired_state"]
+      instance.desired_state = pson["desired_state"]
     end
+    instance
+  end
+
+  # Puppet::SSL::Host is actually indirected now so the original implementation
+  # has been moved into the certificate_status indirector.  This method does not
+  # appear to be in use in `puppet cert -l`.
+  def self.search(options = {})
+    indirection.search("*", options)
   end
 
   # Is this a ca host, meaning that all of its files go in the CA location?
@@ -221,6 +239,24 @@ class Puppet::SSL::Host
     @ssl_store
   end
 
+  def to_pson(*args)
+    my_cert = Puppet::SSL::Certificate.indirection.find(name)
+    pson_hash = { :name  => name }
+
+    my_state = state
+
+    pson_hash[:state] = my_state
+    pson_hash[:desired_state] = desired_state if desired_state
+
+    if my_state == 'requested'
+      pson_hash[:fingerprint] = certificate_request.fingerprint
+    else
+      pson_hash[:fingerprint] = my_cert.fingerprint
+    end
+
+    pson_hash.to_pson(*args)
+  end
+
   # Attempt to retrieve a cert, if we don't already have one.
   def wait_for_cert(time)
     begin
@@ -257,6 +293,20 @@ class Puppet::SSL::Host
       end
     end
   end
+
+  def state
+    my_cert = Puppet::SSL::Certificate.indirection.find(name)
+    if certificate_request
+      return 'requested'
+    end
+
+    begin
+      Puppet::SSL::CertificateAuthority.new.verify(my_cert)
+      return 'signed'
+    rescue Puppet::SSL::CertificateAuthority::CertificateVerificationError
+      return 'revoked'
+    end
+  end
 end
 
 require 'puppet/ssl/certificate_authority'
diff --git a/spec/unit/indirector/certificate_status/file_spec.rb b/spec/unit/indirector/certificate_status/file_spec.rb
new file mode 100644
index 0000000..6cc0bb5
--- /dev/null
+++ b/spec/unit/indirector/certificate_status/file_spec.rb
@@ -0,0 +1,188 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
+require 'puppet/ssl/host'
+require 'puppet/indirector/certificate_status'
+require 'tempfile'
+
+describe "Puppet::Indirector::CertificateStatus::File" do
+  include PuppetSpec::Files
+
+  before do
+    Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true
+    @terminus = Puppet::SSL::Host.indirection.terminus(:file)
+
+    @tmpdir = tmpdir("certificate_status_ca_testing")
+    Puppet[:confdir] = @tmpdir
+    Puppet[:vardir] = @tmpdir
+
+    # localcacert is where each client stores the CA certificate
+    # cacert is where the master stores the CA certificate
+    # Since we need to play the role of both for testing we need them to be the same and exist
+    Puppet[:cacert] = Puppet[:localcacert]
+  end
+
+  def generate_csr(host)
+    host.generate_key
+    csr = Puppet::SSL::CertificateRequest.new(host.name)
+    csr.generate(host.key.content)
+    Puppet::SSL::CertificateRequest.indirection.save(csr)
+  end
+
+  def sign_csr(host)
+    host.desired_state = "signed"
+    @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host))
+  end
+
+  def generate_signed_cert(host)
+    generate_csr(host)
+    sign_csr(host)
+
+    @terminus.find(Puppet::Indirector::Request.new(:certificate_status, :find, host.name, host))
+  end
+
+  def generate_revoked_cert(host)
+    generate_signed_cert(host)
+
+    host.desired_state = "revoked"
+
+    @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host))
+  end
+
+  it "should be a terminus on SSL::Host" do
+    @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::File)
+  end
+
+  it "should create a CA instance if none is present" do
+    @terminus.ca.should be_instance_of(Puppet::SSL::CertificateAuthority)
+  end
+
+  describe "when creating the CA" do
+    it "should fail if it is not a valid CA" do
+      Puppet::SSL::CertificateAuthority.expects(:ca?).returns false
+      lambda { @terminus.ca }.should raise_error(ArgumentError, "This process is not configured as a certificate authority")
+    end
+  end
+
+  it "should be indirected with the name 'certificate_status'" do
+    Puppet::SSL::Host.indirection.name.should == :certificate_status
+  end
+
+  describe "when finding" do
+    before do
+      @host = Puppet::SSL::Host.new("foo")
+      Puppet.settings.use(:main)
+    end
+
+    it "should return the Puppet::SSL::Host when a CSR exists for the host" do
+      generate_csr(@host)
+      request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+
+      retrieved_host = @terminus.find(request)
+
+      retrieved_host.name.should == @host.name
+      retrieved_host.certificate_request.content.to_s.chomp.should == @host.certificate_request.content.to_s.chomp
+    end
+
+    it "should return the Puppet::SSL::Host when a public key exist for the host" do
+      generate_signed_cert(@host)
+      request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+
+      retrieved_host = @terminus.find(request)
+
+      retrieved_host.name.should == @host.name
+      retrieved_host.certificate.content.to_s.chomp.should == @host.certificate.content.to_s.chomp
+    end
+
+    it "should return nil when neither a CSR nor public key exist for the host" do
+      request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+      @terminus.find(request).should == nil
+    end
+  end
+
+  describe "when saving" do
+    before do
+      @host = Puppet::SSL::Host.new("foobar")
+      Puppet.settings.use(:main)
+    end
+
+    describe "when signing a cert" do
+      before do
+        @host.desired_state = "signed"
+        @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host)
+      end
+
+      it "should fail if no CSR is on disk" do
+        lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /certificate request/)
+      end
+
+      it "should sign the on-disk CSR when it is present" do
+        signed_host = generate_signed_cert(@host)
+
+        signed_host.state.should == "signed"
+        Puppet::SSL::Certificate.indirection.find("foobar").should be_instance_of(Puppet::SSL::Certificate)
+      end
+    end
+
+    describe "when revoking a cert" do
+      before do
+        @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host)
+      end
+
+      it "should fail if no certificate is on disk" do
+        @host.desired_state = "revoked"
+        lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /Cannot revoke/)
+      end
+
+      it "should revoke the certificate when it is present" do
+        generate_revoked_cert(@host)
+
+        @host.state.should == 'revoked'
+      end
+    end
+  end
+
+  describe "when deleting" do
+    before do
+      Puppet.settings.use(:main)
+    end
+
+    it "should not delete anything if no certificate, request, or key is on disk" do
+      host = Puppet::SSL::Host.new("clean_me")
+      request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_me", host)
+      @terminus.destroy(request).should == "Nothing was deleted"
+    end
+
+    it "should clean certs, cert requests, keys" do
+      signed_host = Puppet::SSL::Host.new("clean_signed_cert")
+      generate_signed_cert(signed_host)
+      signed_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_signed_cert", signed_host)
+      @terminus.destroy(signed_request).should == "Deleted for clean_signed_cert: Puppet::SSL::Certificate, Puppet::SSL::Key"
+
+      requested_host = Puppet::SSL::Host.new("clean_csr")
+      generate_csr(requested_host)
+      csr_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_csr", requested_host)
+      @terminus.destroy(csr_request).should == "Deleted for clean_csr: Puppet::SSL::CertificateRequest, Puppet::SSL::Key"
+    end
+  end
+
+  describe "when searching" do
+    it "should return a list of all hosts with certificate requests, signed certs, or revoked certs" do
+      Puppet.settings.use(:main)
+
+      signed_host = Puppet::SSL::Host.new("signed_host")
+      generate_signed_cert(signed_host)
+
+      requested_host = Puppet::SSL::Host.new("requested_host")
+      generate_csr(requested_host)
+
+      revoked_host = Puppet::SSL::Host.new("revoked_host")
+      generate_revoked_cert(revoked_host)
+
+      retrieved_hosts = @terminus.search(Puppet::Indirector::Request.new(:certificate_status, :search, "all", signed_host))
+
+      results = retrieved_hosts.map {|h| [h.name, h.state]}.sort{ |h,i| h[0] <=> i[0] }
+      results.should == [["ca","signed"],["requested_host","requested"],["revoked_host","revoked"],["signed_host","signed"]]
+    end
+  end
+end
diff --git a/spec/unit/indirector/certificate_status/rest_spec.rb b/spec/unit/indirector/certificate_status/rest_spec.rb
new file mode 100644
index 0000000..f44eac6
--- /dev/null
+++ b/spec/unit/indirector/certificate_status/rest_spec.rb
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
+require 'puppet/ssl/host'
+require 'puppet/indirector/certificate_status'
+
+describe "Puppet::CertificateStatus::Rest" do
+  before do
+    @terminus = Puppet::SSL::Host.indirection.terminus(:rest)
+  end
+
+  it "should be a terminus on Puppet::SSL::Host" do
+    @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::Rest)
+  end
+end
diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb
index d8f15e7..885bd45 100755
--- a/spec/unit/ssl/host_spec.rb
+++ b/spec/unit/ssl/host_spec.rb
@@ -3,16 +3,19 @@
 require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
 
 require 'puppet/ssl/host'
+require 'puppet/sslcertificates'
+require 'puppet/sslcertificates/ca'
 
 describe Puppet::SSL::Host do
   before do
-    @class = Puppet::SSL::Host
-    @host = @class.new("myname")
+    Puppet::SSL::Host.indirection.terminus_class = :file
+    @host = Puppet::SSL::Host.new("myname")
   end
 
   after do
     # Cleaned out any cached localhost instance.
     Puppet::Util::Cacher.expire
+    Puppet::SSL::Host.ca_location = :none
   end
 
   it "should use any provided name as its name" do
@@ -140,13 +143,6 @@ describe Puppet::SSL::Host do
   end
 
   describe "when specifying the CA location" do
-    before do
-      [Puppet::SSL::Key, Puppet::SSL::Certificate, Puppet::SSL::CertificateRequest, Puppet::SSL::CertificateRevocationList].each do |klass|
-        klass.indirection.stubs(:terminus_class=)
-        klass.indirection.stubs(:cache_class=)
-      end
-    end
-
     it "should support the location ':local'" do
       lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error
     end
@@ -168,80 +164,88 @@ describe Puppet::SSL::Host do
     end
 
     describe "as 'local'" do
-      it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
-        Puppet::SSL::Certificate.indirection.expects(:cache_class=).with :file
-        Puppet::SSL::CertificateRequest.indirection.expects(:cache_class=).with :file
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:cache_class=).with :file
-
+      before do
         Puppet::SSL::Host.ca_location = :local
       end
 
-      it "should set the terminus class for Key as :file" do
-        Puppet::SSL::Key.indirection.expects(:terminus_class=).with :file
+      it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
+        Puppet::SSL::Certificate.indirection.cache_class.should == :file
+        Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file
+        Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file
+      end
 
-        Puppet::SSL::Host.ca_location = :local
+      it "should set the terminus class for Key and Host as :file" do
+        Puppet::SSL::Key.indirection.terminus_class.should == :file
+        Puppet::SSL::Host.indirection.terminus_class.should == :file
       end
 
       it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do
-        Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :ca
-        Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :ca
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :ca
-
-        Puppet::SSL::Host.ca_location = :local
+        Puppet::SSL::Certificate.indirection.terminus_class.should == :ca
+        Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca
+        Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca
       end
     end
 
     describe "as 'remote'" do
-      it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
-        Puppet::SSL::Certificate.indirection.expects(:cache_class=).with :file
-        Puppet::SSL::CertificateRequest.indirection.expects(:cache_class=).with :file
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:cache_class=).with :file
-
+      before do
         Puppet::SSL::Host.ca_location = :remote
       end
 
-      it "should set the terminus class for Key as :file" do
-        Puppet::SSL::Key.indirection.expects(:terminus_class=).with :file
-
-        Puppet::SSL::Host.ca_location = :remote
+      it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do
+        Puppet::SSL::Certificate.indirection.cache_class.should == :file
+        Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file
+        Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file
       end
 
-      it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :rest" do
-        Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :rest
-        Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :rest
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :rest
+      it "should set the terminus class for Key as :file" do
+        Puppet::SSL::Key.indirection.terminus_class.should == :file
+      end
 
-        Puppet::SSL::Host.ca_location = :remote
+      it "should set the terminus class for Host, Certificate, CertificateRevocationList, and CertificateRequest as :rest" do
+        Puppet::SSL::Host.indirection.terminus_class.should == :rest
+        Puppet::SSL::Certificate.indirection.terminus_class.should == :rest
+        Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :rest
+        Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :rest
       end
     end
 
     describe "as 'only'" do
-      it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do
-        Puppet::SSL::Key.indirection.expects(:terminus_class=).with :ca
-        Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :ca
-        Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :ca
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :ca
-
+      before do
         Puppet::SSL::Host.ca_location = :only
       end
 
-      it "should reset the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do
-        Puppet::SSL::Certificate.indirection.expects(:cache_class=).with nil
-        Puppet::SSL::CertificateRequest.indirection.expects(:cache_class=).with nil
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:cache_class=).with nil
+      it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do
+        Puppet::SSL::Key.indirection.terminus_class.should == :ca
+        Puppet::SSL::Certificate.indirection.terminus_class.should == :ca
+        Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca
+        Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca
+      end
 
-        Puppet::SSL::Host.ca_location = :only
+      it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do
+        Puppet::SSL::Certificate.indirection.cache_class.should be_nil
+        Puppet::SSL::CertificateRequest.indirection.cache_class.should be_nil
+        Puppet::SSL::CertificateRevocationList.indirection.cache_class.should be_nil
+      end
+
+      it "should set the terminus class for Host to :file" do
+        Puppet::SSL::Host.indirection.terminus_class.should == :file
       end
     end
 
     describe "as 'none'" do
+      before do
+        Puppet::SSL::Host.ca_location = :none
+      end
+
       it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do
-        Puppet::SSL::Key.indirection.expects(:terminus_class=).with :file
-        Puppet::SSL::Certificate.indirection.expects(:terminus_class=).with :file
-        Puppet::SSL::CertificateRequest.indirection.expects(:terminus_class=).with :file
-        Puppet::SSL::CertificateRevocationList.indirection.expects(:terminus_class=).with :file
+        Puppet::SSL::Key.indirection.terminus_class.should == :file
+        Puppet::SSL::Certificate.indirection.terminus_class.should == :file
+        Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :file
+        Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :file
+      end
 
-        Puppet::SSL::Host.ca_location = :none
+      it "should set the terminus class for Host to 'none'" do
+        lambda { Puppet::SSL::Host.indirection.terminus_class }.should raise_error(Puppet::DevError)
       end
     end
   end
@@ -271,8 +275,8 @@ describe Puppet::SSL::Host do
       Puppet::SSL::Host.destroy("myhost").should be_true
     end
 
-    it "should return false if none of the classes returned true" do
-      Puppet::SSL::Host.destroy("myhost").should be_false
+    it "should report that nothing was deleted if none of the classes returned true" do
+      Puppet::SSL::Host.destroy("myhost").should == "Nothing was deleted"
     end
   end
 
@@ -709,4 +713,84 @@ describe Puppet::SSL::Host do
       @host.wait_for_cert(1)
     end
   end
+
+  describe "when handling PSON" do
+    include PuppetSpec::Files
+
+    before do
+      Puppet[:vardir] = tmpdir("ssl_test_vardir")
+      Puppet[:ssldir] = tmpdir("ssl_test_ssldir")
+      Puppet::SSLCertificates::CA.new.mkrootcert
+      # localcacert is where each client stores the CA certificate
+      # cacert is where the master stores the CA certificate
+      # Since we need to play the role of both for testing we need them to be the same and exist
+      Puppet[:cacert] = Puppet[:localcacert]
+
+      @ca=Puppet::SSL::CertificateAuthority.new
+    end
+
+    describe "when converting to PSON" do
+      it "should be able to identify a host with an unsigned certificate request" do
+        host = Puppet::SSL::Host.new("bazinga")
+        host.generate_certificate_request
+        pson_hash = {
+          "fingerprint"          => host.certificate_request.fingerprint,
+          "desired_state"        => 'requested',
+          "name"                 => host.name
+        }
+
+        result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
+        result["fingerprint"].should == pson_hash["fingerprint"]
+        result["name"].should        == pson_hash["name"]
+        result["state"].should       == pson_hash["desired_state"]
+      end
+
+      it "should be able to identify a host with a signed certificate" do
+        host = Puppet::SSL::Host.new("bazinga")
+        host.generate_certificate_request
+        @ca.sign(host.name)
+        pson_hash = {
+          "fingerprint"          => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint,
+          "desired_state"        => 'signed',
+          "name"                 => host.name,
+        }
+
+        result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
+        result["fingerprint"].should == pson_hash["fingerprint"]
+        result["name"].should        == pson_hash["name"]
+        result["state"].should       == pson_hash["desired_state"]
+      end
+
+      it "should be able to identify a host with a revoked certificate" do
+        host = Puppet::SSL::Host.new("bazinga")
+        host.generate_certificate_request
+        @ca.sign(host.name)
+        @ca.revoke(host.name)
+        pson_hash = {
+          "fingerprint"          => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint,
+          "desired_state"        => 'revoked',
+          "name"                 => host.name,
+        }
+
+        result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
+        result["fingerprint"].should == pson_hash["fingerprint"]
+        result["name"].should        == pson_hash["name"]
+        result["state"].should       == pson_hash["desired_state"]
+      end
+    end
+
+    describe "when converting from PSON" do
+      it "should return a Puppet::SSL::Host object with the specified desired state" do
+        host = Puppet::SSL::Host.new("bazinga")
+        host.desired_state="signed"
+        pson_hash = {
+          "name"  => host.name,
+          "desired_state" => host.desired_state,
+        }
+        generated_host = Puppet::SSL::Host.from_pson(pson_hash)
+        generated_host.desired_state.should == host.desired_state
+        generated_host.name.should == host.name
+      end
+    end
+  end
 end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list