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

Matt Robinson matt at puppetlabs.com
Tue May 10 07:59:41 UTC 2011


The following commit has been merged in the experimental branch:
commit 45a9a97285d99db524d5330c236352b29e5884ed
Author: Matt Robinson <matt at puppetlabs.com>
Date:   Thu Oct 28 14:49:37 2010 -0700

    (#5132) Provide a query REST interface for inventory
    
    This REST interface returns a list of nodes that match a fact query.
    Fact queries can use (in)equality testing as a string comparison, and >,
    <, >=, <= numerical comparisons.  Multiple tests can be done as AND
    comparisons, not OR.
    
    The fact queries need to be prefixed by facts, and the comparisons other
    than equality are specified with a .comparison_type after the fact name.
    
    This will be better explained in the REST documentation on the website.
    
    Searches that don't match anything now return empty array instead of a
    404 error.

diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index ab12760..c7bebf8 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -116,6 +116,7 @@ module Puppet
     :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 => [Puppet.application_name.to_s == "master" ? 'yaml' : 'facter', "The node facts terminus."],
+    :inventory_terminus => [ "$facts_terminus", "Should usually be the same as the facts terminus" ],
     :httplog => { :default => "$logdir/http.log",
       :owner => "root",
       :mode => 0640,
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
index 309eed7..9095e48 100644
--- a/lib/puppet/indirector/indirection.rb
+++ b/lib/puppet/indirector/indirection.rb
@@ -238,6 +238,7 @@ class Puppet::Indirector::Indirection
     if result = terminus.search(request)
       raise Puppet::DevError, "Search results from terminus #{terminus.name} are not an array" unless result.is_a?(Array)
       result.each do |instance|
+        next unless instance.respond_to? :expiration
         instance.expiration ||= self.expiration
       end
       return result
diff --git a/lib/puppet/indirector/inventory/yaml.rb b/lib/puppet/indirector/inventory/yaml.rb
new file mode 100644
index 0000000..c6b1a14
--- /dev/null
+++ b/lib/puppet/indirector/inventory/yaml.rb
@@ -0,0 +1,47 @@
+require 'puppet/node/inventory'
+require 'puppet/indirector/yaml'
+
+class Puppet::Node::Inventory::Yaml < Puppet::Indirector::Yaml
+  desc "Return node names matching the fact query"
+
+  # Return the path to a given node's file.
+  def yaml_dir_path
+    base = Puppet.run_mode.master? ? Puppet[:yamldir] : Puppet[:clientyamldir]
+    File.join(base, 'facts', '*.yaml')
+  end
+
+  def node_matches?(facts, options)
+    options.each do |key, value|
+      type, name, operator = key.to_s.split(".")
+      operator ||= 'eq'
+
+      next unless type == "facts"
+      return false unless facts.values[name]
+
+      return false unless case operator
+      when "eq"
+        facts.values[name].to_s == value.to_s
+      when "le"
+        facts.values[name].to_f <= value.to_f
+      when "ge"
+        facts.values[name].to_f >= value.to_f
+      when "lt"
+        facts.values[name].to_f < value.to_f
+      when "gt"
+        facts.values[name].to_f > value.to_f
+      when "ne"
+        facts.values[name].to_s != value.to_s
+      end
+    end
+    return true
+  end
+
+  def search(request)
+    node_names = []
+    Dir.glob(yaml_dir_path).each do |file|
+      facts = YAML.load_file(file)
+      node_names << facts.name if node_matches?(facts, request.options)
+    end
+    node_names
+  end
+end
diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb
index dd4612a..2a47363 100644
--- a/lib/puppet/network/http/api/v1.rb
+++ b/lib/puppet/network/http/api/v1.rb
@@ -57,9 +57,8 @@ module Puppet::Network::HTTP::API::V1
     # fix to not need this, and our goal is to move away from the complication
     # that leads to the fix being too long.
     return :singular if indirection == "facts"
-
-    # "status" really is singular
     return :singular if indirection == "status"
+    return :plural if indirection == "inventory"
 
     result = (indirection =~ /s$/) ? :plural : :singular
 
diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb
index 61ae2d2..e5aa1c5 100644
--- a/lib/puppet/network/http/handler.rb
+++ b/lib/puppet/network/http/handler.rb
@@ -116,7 +116,7 @@ module Puppet::Network::HTTP::Handler
   def do_search(indirection_request, request, response)
     result = indirection_request.model.search(indirection_request.key, indirection_request.to_hash)
 
-    if result.nil? or (result.is_a?(Array) and result.empty?)
+    if result.nil?
       return do_exception(response, "Could not find instances in #{indirection_request.indirection_name} with '#{indirection_request.key}'", 404)
     end
 
diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb
index 2453cd1..e8d58e6 100644
--- a/lib/puppet/node.rb
+++ b/lib/puppet/node.rb
@@ -3,6 +3,7 @@ require 'puppet/indirector'
 # A class for managing nodes, including their facts and environment.
 class Puppet::Node
   require 'puppet/node/facts'
+  require 'puppet/node/inventory'
   require 'puppet/node/environment'
 
   # Set up indirection, so that nodes can be looked for in
diff --git a/lib/puppet/node/inventory.rb b/lib/puppet/node/inventory.rb
new file mode 100644
index 0000000..fd99163
--- /dev/null
+++ b/lib/puppet/node/inventory.rb
@@ -0,0 +1,7 @@
+require 'puppet/node'
+require 'puppet/indirector'
+
+class Puppet::Node::Inventory
+  extend Puppet::Indirector
+  indirects :inventory, :terminus_setting => :inventory_terminus
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index b5b2738..ed4e2c2 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -20,7 +20,7 @@ module PuppetSpec
   FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
 end
 
-require 'spec/lib/puppet_spec/files'
+require 'lib/puppet_spec/files'
 require 'monkey_patches/alias_should_to_must'
 require 'monkey_patches/add_confine_and_runnable_to_rspec_dsl'
 require 'monkey_patches/publicize_methods'
diff --git a/spec/unit/indirector/facts/yaml_spec.rb b/spec/unit/indirector/facts/yaml_spec.rb
index e7bac34..37a1bca 100755
--- a/spec/unit/indirector/facts/yaml_spec.rb
+++ b/spec/unit/indirector/facts/yaml_spec.rb
@@ -10,9 +10,9 @@ describe Puppet::Node::Facts::Yaml do
     Puppet::Node::Facts::Yaml.superclass.should equal(Puppet::Indirector::Yaml)
   end
 
-
   it "should have documentation" do
     Puppet::Node::Facts::Yaml.doc.should_not be_nil
+    Puppet::Node::Facts::Yaml.doc.should_not be_empty
   end
 
   it "should be registered with the facts indirection" do
@@ -20,7 +20,7 @@ describe Puppet::Node::Facts::Yaml do
     Puppet::Node::Facts::Yaml.indirection.should equal(indirection)
   end
 
-  it "should have its name set to :facts" do
+  it "should have its name set to :yaml" do
     Puppet::Node::Facts::Yaml.name.should == :yaml
   end
 end
diff --git a/spec/unit/indirector/inventory/yaml_spec.rb b/spec/unit/indirector/inventory/yaml_spec.rb
new file mode 100644
index 0000000..3a7035a
--- /dev/null
+++ b/spec/unit/indirector/inventory/yaml_spec.rb
@@ -0,0 +1,130 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/node/inventory'
+require 'puppet/indirector/inventory/yaml'
+require 'puppet/indirector/request'
+
+describe Puppet::Node::Inventory::Yaml do
+  def setup_search_matching(matching, nonmatching, query)
+    request = Puppet::Indirector::Request.new(:inventory, :search, nil, query)
+
+    Dir.stubs(:glob).returns(matching.keys + nonmatching.keys)
+    [matching, nonmatching].each do |examples|
+      examples.each do |key, value|
+        YAML.stubs(:load_file).with(key).returns value
+      end
+    end
+    return matching, request
+  end
+
+  it "should return node names that match the search query options" do
+    matching, request = setup_search_matching({
+        '/path/to/matching.yaml'  => Puppet::Node::Facts.new("matchingnode",  "architecture" => "i386", 'processor_count' => '4'),
+        '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "i386", 'processor_count' => '4', 'randomfact' => 'foo')
+      },
+      {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "powerpc", 'processor_count' => '4'),
+        "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386",    'processor_count' => '5'),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3",                              'processor_count' => '4'),
+      },
+      {'facts.architecture' => 'i386', 'facts.processor_count' => '4'}
+    )
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+
+  it "should return empty array when no nodes match the search query options" do
+    matching, request = setup_search_matching({}, {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "powerpc", 'processor_count' => '10'),
+        "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386",    'processor_count' => '5'),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3",                              'processor_count' => '4'),
+      },
+      {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'}
+    )
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+
+
+  it "should return node names that match the search query options with the greater than operator" do
+    matching, request = setup_search_matching({
+        '/path/to/matching.yaml'  => Puppet::Node::Facts.new("matchingnode",  "architecture" => "i386",    'processor_count' => '5'),
+        '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '10', 'randomfact' => 'foo')
+      },
+      {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "powerpc", 'processor_count' => '4'),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386",    'processor_count' => '3'),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3"                                                       ),
+      },
+      {'facts.processor_count.gt' => '4'}
+    )
+
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+
+  it "should return node names that match the search query options with the less than operator" do
+    matching, request = setup_search_matching({
+        '/path/to/matching.yaml'  => Puppet::Node::Facts.new("matchingnode",  "architecture" => "i386",    'processor_count' => '5'),
+        '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '30', 'randomfact' => 'foo')
+      },
+      {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "powerpc", 'processor_count' => '50' ),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386",    'processor_count' => '100'),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3"                                                         ),
+      },
+      {'facts.processor_count.lt' => '50'}
+    )
+
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+
+  it "should return node names that match the search query options with the less than or equal to operator" do
+    matching, request = setup_search_matching({
+        '/path/to/matching.yaml'  => Puppet::Node::Facts.new("matchingnode",  "architecture" => "i386",    'processor_count' => '5'),
+        '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo')
+      },
+      {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "powerpc", 'processor_count' => '100' ),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386",    'processor_count' => '5000'),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3"                                                          ),
+      },
+      {'facts.processor_count.le' => '50'}
+    )
+
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+
+  it "should return node names that match the search query options with the greater than or equal to operator" do
+    matching, request = setup_search_matching({
+        '/path/to/matching.yaml'  => Puppet::Node::Facts.new("matchingnode",  "architecture" => "i386",    'processor_count' => '100'),
+        '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo')
+      },
+      {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "powerpc", 'processor_count' => '40'),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386",    'processor_count' => '9' ),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3"                                                        ),
+      },
+      {'facts.processor_count.ge' => '50'}
+    )
+
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+
+  it "should return node names that match the search query options with the not equal operator" do
+    matching, request = setup_search_matching({
+        '/path/to/matching.yaml'  => Puppet::Node::Facts.new("matchingnode",  "architecture" => 'arm'                           ),
+        '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => 'powerpc', 'randomfact' => 'foo')
+      },
+      {
+        "/path/to/nonmatching.yaml"  => Puppet::Node::Facts.new("nonmatchingnode",  "architecture" => "i386"                           ),
+        "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ),
+        "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3"                                                     ),
+      },
+      {'facts.architecture.ne' => 'i386'}
+    )
+
+    Puppet::Node::Inventory::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name}
+  end
+end
diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb
index c593242..8d50704 100644
--- a/spec/unit/network/http/api/v1_spec.rb
+++ b/spec/unit/network/http/api/v1_spec.rb
@@ -72,6 +72,18 @@ describe Puppet::Network::HTTP::API::V1 do
       @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search
     end
 
+    it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do
+      @tester.uri2indirection("GET", "/env/facts/bar", {}).method.should == :find
+    end
+
+    it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do
+      @tester.uri2indirection("PUT", "/env/facts/bar", {}).method.should == :save
+    end
+
+    it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do
+      @tester.uri2indirection("GET", "/env/inventory/search", {}).method.should == :search
+    end
+
     it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do
       @tester.uri2indirection("DELETE", "/env/foo/bar", {}).method.should == :destroy
     end
diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb
index 76a9c55..355f500 100755
--- a/spec/unit/network/http/handler_spec.rb
+++ b/spec/unit/network/http/handler_spec.rb
@@ -305,17 +305,20 @@ describe Puppet::Network::HTTP::Handler do
         @handler.do_search(@irequest, @request, @response)
       end
 
-      it "should return a 404 when searching returns an empty array" do
-        @model_class.stubs(:name).returns "my name"
-        @handler.expects(:set_response).with { |response, body, status| status == 404 }
+      it "should return [] when searching returns an empty array" do
+        @handler.expects(:accept_header).with(@request).returns "one,two"
         @model_class.stubs(:search).returns([])
+        @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]"
+
+
+        @handler.expects(:set_response).with { |response, data| data == "[]" }
         @handler.do_search(@irequest, @request, @response)
       end
 
       it "should return a 404 when searching returns nil" do
         @model_class.stubs(:name).returns "my name"
         @handler.expects(:set_response).with { |response, body, status| status == 404 }
-        @model_class.stubs(:search).returns([])
+        @model_class.stubs(:search).returns(nil)
         @handler.do_search(@irequest, @request, @response)
       end
     end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list