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

Signed-off-by: Max Martin <[email protected]>
---
Local-branch: feature/master/5528-certificates_signing_api
 lib/puppet/indirector/certificate_status.rb        |    4 +
 lib/puppet/indirector/certificate_status/file.rb   |   82 +++++++++
 lib/puppet/indirector/certificate_status/rest.rb   |   10 +
 lib/puppet/network/http/api/v1.rb                  |    1 +
 lib/puppet/ssl/host.rb                             |   78 +++++++--
 .../indirector/certificate_status/file_spec.rb     |  190 ++++++++++++++++++++
 .../indirector/certificate_status/rest_spec.rb     |   15 ++
 spec/unit/ssl/host_spec.rb                         |  183 +++++++++++++------
 8 files changed, 495 insertions(+), 68 deletions(-)
 create mode 100644 lib/puppet/indirector/certificate_status.rb
 create mode 100644 lib/puppet/indirector/certificate_status/file.rb
 create mode 100644 lib/puppet/indirector/certificate_status/rest.rb
 create mode 100644 spec/unit/indirector/certificate_status/file_spec.rb
 create mode 100644 spec/unit/indirector/certificate_status/rest_spec.rb

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..e44de1c
--- /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} for host #{request.key} was deleted"
+      end
+    end
+
+    return "Nothing was deleted" if deleted.empty?
+    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..d00efd9 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 #{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["state"]
+      instance.desired_state = pson["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,23 @@ 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
+
+    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 +292,21 @@ 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 => 
details
+      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..0a090cf
--- /dev/null
+++ b/spec/unit/indirector/certificate_status/file_spec.rb
@@ -0,0 +1,190 @@
+#!/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
+  before do
+    Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true
+    @terminus = Puppet::SSL::Host.indirection.terminus(:file)
+
+    @tmpdir = Tempfile.new("certificate_status_ca_testing")
+    @tmpdir.close
+    File.unlink(@tmpdir.path)
+    Dir.mkdir(@tmpdir.path)
+    Puppet[:confdir] = @tmpdir.path
+    Puppet[:vardir] = @tmpdir.path
+
+    # 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)
+    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 == "Puppet::SSL::Certificate 
for host clean_signed_cert was deleted, Puppet::SSL::Key for host 
clean_signed_cert was deleted"
+
+      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 == 
"Puppet::SSL::CertificateRequest for host clean_csr was deleted, 
Puppet::SSL::Key for host clean_csr was deleted"
+    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..4b82c39 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,75 @@ 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,
+          "state"                => 'requested',
+          "name"                 => host.name
+        }
+
+        PSON.parse(Puppet::SSL::Host.new(host.name).to_pson).should == 
pson_hash
+      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,
+          "state"                => 'signed',
+          "name"                 => host.name,
+        }
+
+        PSON.parse(Puppet::SSL::Host.new(host.name).to_pson).should == 
pson_hash
+      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,
+          "state"                => 'revoked',
+          "name"                 => host.name,
+        }
+
+        PSON.parse(Puppet::SSL::Host.new(host.name).to_pson).should == 
pson_hash
+      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,
+          "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
-- 
1.7.4

-- 
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