[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, upstream, updated. 2.6.1rc1-141-gcdb2b90

James Turnbull james at lovedthanlost.net
Mon Aug 16 12:47:45 UTC 2010


The following commit has been merged in the upstream branch:
commit 97936c6d3fa3950d22266679b65d4a6877008a74
Author: Rein Henrichs <rein at puppetlabs.com>
Date:   Mon Jun 7 15:45:45 2010 -0700

    [#3921] Add facts_terminus setting to Puppet settings
    
    * defaults to "facter"
    * no longer set the facts terminus class in for puppetmaster, use
      setting instead
    
    Implements an abstract CouchDB terminus and a concrete CouchDB terminus
    used to store node facts. Node facts are stored in a "node" document as
    the "facts" attribute. This node document may also be used by other
    couchdb termini that store node-related information. It is recommended
    to use a separate document (or documents) to store large data structures
    like catalogs, linking them to their related node document using
    embedded ids.
    
    This implementation depends on the "couchrest" gem.

diff --git a/lib/puppet/application/puppetd.rb b/lib/puppet/application/puppetd.rb
index 8b07c90..ca5a0d4 100644
--- a/lib/puppet/application/puppetd.rb
+++ b/lib/puppet/application/puppetd.rb
@@ -215,9 +215,11 @@ Puppet::Application.new(:puppetd) do
         # You can still override this on the command-line with, e.g., :compiler.
         Puppet[:catalog_terminus] = :rest
 
+        # Override the default.
+        Puppet[:facts_terminus] = :facter
+
         Puppet::Resource::Catalog.cache_class = :yaml
 
-        Puppet::Node::Facts.terminus_class = :facter
 
         # We need tomake the client either way, we just don't start it
         # if --no-client is set.
diff --git a/lib/puppet/application/puppetmasterd.rb b/lib/puppet/application/puppetmasterd.rb
index 9b0bf30..6e69445 100644
--- a/lib/puppet/application/puppetmasterd.rb
+++ b/lib/puppet/application/puppetmasterd.rb
@@ -150,9 +150,6 @@ Puppet::Application.new(:puppetmasterd) do
 
         Puppet.settings.use :main, :puppetmasterd, :ssl
 
-        # A temporary solution, to at least make the master work for now.
-        Puppet::Node::Facts.terminus_class = :yaml
-
         # Cache our nodes in yaml.  Currently not configurable.
         Puppet::Node.cache_class = :yaml
 
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index c815133..0ae0553 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -151,6 +151,7 @@ module Puppet
         :node_terminus => ["plain", "Where to find information about nodes."],
         :catalog_terminus => ["compiler", "Where to get node catalogs.  This is useful to change if, for instance,
             you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store."],
+        :facts_terminus => ["facter", "Where to get node facts."],
         :httplog => { :default => "$logdir/http.log",
             :owner => "root",
             :mode => 0640,
@@ -694,6 +695,10 @@ module Puppet
             and other environments normally use ``debug``."]
     )
 
+    setdefaults(:couchdb,
+        :couchdb_url => ["http://127.0.0.1:5984/puppet", "The url where the puppet couchdb database will be created"]
+    )
+
     setdefaults(:transaction,
         :tags => ["", "Tags to use to find resources.  If this is set, then
             only resources tagged with the specified tags will be applied.
diff --git a/lib/puppet/feature/base.rb b/lib/puppet/feature/base.rb
index aac04f2..a6111d8 100644
--- a/lib/puppet/feature/base.rb
+++ b/lib/puppet/feature/base.rb
@@ -31,3 +31,6 @@ Puppet.features.add(:rrd, :libs => ["RRDtool"])
 
 # We have OpenSSL
 Puppet.features.add(:openssl, :libs => ["openssl"])
+
+# We have CouchDB
+Puppet.features.add(:couchdb, :libs => ["couchrest"])
diff --git a/lib/puppet/indirector/couch.rb b/lib/puppet/indirector/couch.rb
new file mode 100644
index 0000000..6818ee8
--- /dev/null
+++ b/lib/puppet/indirector/couch.rb
@@ -0,0 +1,76 @@
+raise "Couch terminus not supported without couchrest gem" unless Puppet.features.couchdb?
+
+require 'couchrest'
+class Puppet::Indirector::Couch < Puppet::Indirector::Terminus
+
+    # The CouchRest database instance. One database instance per Puppet runtime
+    # should be sufficient.
+    #
+    def self.db; @db ||= CouchRest.database! Puppet[:couchdb_url] end
+    def db; self.class.db end
+
+    def find(request)
+        attributes_of get(request)
+    end
+
+    # Create or update the couchdb document with the request's data hash.
+    #
+    def save(request)
+        raise ArgumentError, "PUT does not accept options" unless request.options.empty?
+        update(request) || create(request)
+    end
+
+    private
+
+    # RKH:TODO: Do not depend on error handling, check if the document exists
+    # first. (Does couchrest support this?)
+    #
+    def get(request)
+        db.get(id_for(request))
+    rescue RestClient::ResourceNotFound
+        Puppet.debug "No couchdb document with id: #{id_for(request)}"
+        return nil
+    end
+
+    def update(request)
+        doc = get request
+        return unless doc
+        doc.merge!(hash_from(request))
+        doc.save
+        return true
+    end
+
+    def create(request)
+        db.save_doc hash_from(request)
+    end
+
+    # The attributes hash that is serialized to CouchDB as JSON. It includes
+    # metadata that is used to help aggregate data in couchdb. Add
+    # model-specific attributes in subclasses.
+    #
+    def hash_from(request)
+        {
+            "_id"         => id_for(request),
+            "puppet_type" => document_type_for(request)
+        }
+    end
+
+    # The couchdb response stripped of metadata, used to instantiate the model
+    # instance that is returned by save.
+    #
+    def attributes_of(response)
+        response && response.reject{|k,v| k =~ /^(_rev|puppet_)/ }
+    end
+
+    def document_type_for(request)
+        request.indirection_name
+    end
+
+    # The id used to store the object in couchdb. Implemented in subclasses.
+    #
+    def id_for(request)
+        raise NotImplementedError
+    end
+
+end
+
diff --git a/lib/puppet/indirector/facts/couch.rb b/lib/puppet/indirector/facts/couch.rb
new file mode 100644
index 0000000..522fe33
--- /dev/null
+++ b/lib/puppet/indirector/facts/couch.rb
@@ -0,0 +1,31 @@
+require 'puppet/node/facts'
+require 'puppet/indirector/couch'
+class Puppet::Node::Facts::Couch < Puppet::Indirector::Couch
+
+    # Return the facts object or nil if there is no document
+    def find(request)
+        doc = super
+        doc ? model.new(doc['_id'], doc['facts']) : nil
+    end
+
+    private
+
+    # Facts values are stored to the document's 'facts' attribute. Hostname is
+    # stored to 'name'
+    #
+    def hash_from(request)
+        super.merge('facts' => request.instance.values)
+    end
+
+    # Facts are stored to the 'node' document.
+    def document_type_for(request)
+        'node'
+    end
+
+    # The id used to store the object in couchdb.
+    def id_for(request)
+        request.key.to_s
+    end
+
+end
+
diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb
index dca435c..a8761f8 100755
--- a/lib/puppet/node/facts.rb
+++ b/lib/puppet/node/facts.rb
@@ -16,8 +16,7 @@ class Puppet::Node::Facts
         end
     end
 
-    # Use the node source as the indirection terminus.
-    indirects :facts, :terminus_class => :facter, :extend => NodeExpirer
+    indirects :facts, :terminus_setting => :facts_terminus, :extend => NodeExpirer
 
     attr_accessor :name, :values
 
@@ -49,6 +48,11 @@ class Puppet::Node::Facts
         end
     end
 
+    def ==(other)
+        return false unless self.name == other.name
+        strip_internal == other.send(:strip_internal)
+    end
+
     private
 
     # Add internal data to the facts for storage.
diff --git a/spec/fixtures/yaml/test.local.yaml b/spec/fixtures/yaml/test.local.yaml
new file mode 100644
index 0000000..194d8aa
--- /dev/null
+++ b/spec/fixtures/yaml/test.local.yaml
@@ -0,0 +1,16 @@
+--- !ruby/object:Puppet::Node::Facts
+  expiration: 2010-06-07 19:15:36.519351 -07:00
+  name: test.local
+  values: 
+    sp_number_processors: "2"
+    kernelmajversion: "10.3"
+    kernelversion: 10.3.1
+    sp_secure_vm: secure_vm_enabled
+    ps: ps auxwww
+    macosx_productversion_major: "10.6"
+    hostname: test
+    !ruby/sym _timestamp: Mon Jun 07 18:45:36 -0700 2010
+    facterversion: 1.5.7
+    sp_packages: "1"
+    timezone: PDT
+    environment: production
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e03cc98..e1e7ea6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -23,6 +23,7 @@ require 'spec/autorun'
 
 # So everyone else doesn't have to include this base constant.
 module PuppetSpec
+    FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
 end
 
 # load any monkey-patches
diff --git a/spec/unit/application/puppetd.rb b/spec/unit/application/puppetd.rb
index 88772b2..338eb8e 100755
--- a/spec/unit/application/puppetd.rb
+++ b/spec/unit/application/puppetd.rb
@@ -173,6 +173,7 @@ describe "puppetd" do
             Puppet.stubs(:info)
             FileTest.stubs(:exists?).returns(true)
             Puppet.stubs(:[])
+            Puppet.stubs(:[]=)
             Puppet.stubs(:[]).with(:libdir).returns("/dev/null/lib")
             Puppet.settings.stubs(:print_config?)
             Puppet.settings.stubs(:print_config)
@@ -312,8 +313,8 @@ describe "puppetd" do
             @puppetd.run_setup
         end
 
-        it "should tell the facts to use facter" do
-            Puppet::Node::Facts.expects(:terminus_class=).with(:facter)
+        it "should change the facts_terminus setting to 'facter'" do
+            Puppet.expects(:[]=).with(:facts_terminus, :facter)
 
             @puppetd.run_setup
         end
diff --git a/spec/unit/application/puppetmasterd.rb b/spec/unit/application/puppetmasterd.rb
index f522fcc..f7fe571 100644
--- a/spec/unit/application/puppetmasterd.rb
+++ b/spec/unit/application/puppetmasterd.rb
@@ -182,12 +182,6 @@ describe "PuppetMaster" do
             @puppetmasterd.run_setup
         end
 
-        it "should set node facst terminus to yaml" do
-            Puppet::Node::Facts.expects(:terminus_class=).with(:yaml)
-
-            @puppetmasterd.run_setup
-        end
-
         it "should cache class in yaml" do
             Puppet::Node.expects(:cache_class=).with(:yaml)
 
diff --git a/spec/unit/indirector/facts/couch.rb b/spec/unit/indirector/facts/couch.rb
new file mode 100644
index 0000000..0884012
--- /dev/null
+++ b/spec/unit/indirector/facts/couch.rb
@@ -0,0 +1,98 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/node/facts'
+
+describe "Puppet::Node::Facts::Couch" do
+    confine "couchrest gem is missing; cannot test couch terminus" => Puppet.features.couchdb?
+    require 'puppet/indirector/facts/couch' if Puppet.features.couchdb?
+
+    before do
+        @mock_db = mock('couch db')
+        mock_document = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.values)
+        mock_document.stubs(:database).returns(@mock_db)
+        @mock_db.stubs(:get).with(fake_request.key).returns(mock_document)
+        Puppet::Node::Facts::Couch.stubs(:db).returns(@mock_db)
+    end
+
+    subject { Puppet::Node::Facts::Couch }
+
+    describe "#find" do
+        describe "when the node document exists" do
+            it "should find the request by key" do
+                @mock_db.expects(:get).with(fake_request.key).returns({'_id' => fake_request.key, 'facts' => fake_request.instance.values})
+                subject.new.find(fake_request).should == fake_request.instance
+            end
+        end
+
+        describe "when the node document does not exist" do
+            before do
+                @mock_db.expects(:get).
+                    with(fake_request.key).
+                    raises(RestClient::ResourceNotFound)
+            end
+
+            it "should return nil" do
+                subject.new.find(fake_request).should be_nil
+            end
+
+            it "should send Puppet a debug message" do
+                Puppet.expects(:debug).with("No couchdb document with id: test.local")
+                subject.new.find(fake_request).should be_nil
+            end
+
+        end
+    end
+
+    describe "#save" do
+        describe "with options" do
+            subject do
+                lambda { Puppet::Node::Facts::Couch.new.save(fake_request([1])) }
+            end
+
+            it { should raise_error(ArgumentError, "PUT does not accept options") }
+        end
+
+        it "should save the json to the CouchDB database" do
+            @mock_db.expects(:save_doc).at_least_once.returns({'ok' => true })
+            subject.new.save(fake_request)
+        end
+
+        describe "when the document exists" do
+            before do
+                @doc = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.instance.values)
+                @mock_db.expects(:get).with(fake_request.key).returns(@doc)
+            end
+
+            it "saves the document" do
+                @doc.expects(:save)
+                subject.new.save(fake_request)
+            end
+
+        end
+
+        describe "when the document does not exist" do
+            before do
+                @mock_db.expects(:get).
+                    with(fake_request.key).
+                    raises(RestClient::ResourceNotFound)
+            end
+
+            it "saves the document" do
+                @mock_db.expects(:save_doc)
+                subject.new.save(fake_request)
+            end
+
+        end
+
+    end
+
+    def fake_request(options={})
+        facts = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml', 'test.local.yaml'))
+        Struct.new(:instance, :key, :options).new(facts, facts.name, options)
+    end
+    private :fake_request
+
+end
+

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list