diff options
author | Jesse Wolfe <jes5199@gmail.com> | 2010-11-03 20:00:46 -0700 |
---|---|---|
committer | Jesse Wolfe <jes5199@gmail.com> | 2010-11-03 20:04:31 -0700 |
commit | 7236a33d6c5c9fbb0f46ffd7826965dbaae6a39b (patch) | |
tree | 2ca24f5af952c5ef5456ec85ad4fe9e5acf14ac3 | |
parent | 9e2a0e41dfb253a19180aeea6b66f65ca8d63133 (diff) | |
parent | 4d3c41ee5316285bb0df38aeeb76746016803c16 (diff) | |
download | puppet-7236a33d6c5c9fbb0f46ffd7826965dbaae6a39b.tar.gz puppet-7236a33d6c5c9fbb0f46ffd7826965dbaae6a39b.tar.xz puppet-7236a33d6c5c9fbb0f46ffd7826965dbaae6a39b.zip |
Merge branch 'next'
This marks the end of our first agile iteration.
26 files changed, 495 insertions, 1113 deletions
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index ab127602b..c7bebf8f5 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/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index c50022fff..1e1ae12b1 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -22,6 +22,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code else facts = Puppet::Node::Facts.convert_from(format, text_facts) end + facts.add_timestamp facts.save end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 309eed7b6..9095e48f8 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 000000000..fe3489a95 --- /dev/null +++ b/lib/puppet/indirector/inventory/yaml.rb @@ -0,0 +1,81 @@ +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' + + return false unless node_matches_option?(type, name, operator, value, facts) + 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 + + private + + def node_matches_option?(type, name, operator, value, facts) + case type + when "meta" + case name + when "timestamp" + compare_timestamp(operator, facts.timestamp, Time.parse(value)) + end + when "facts" + compare_facts(operator, facts.values[name], value) + end + end + + def compare_facts(operator, value1, value2) + return false unless value1 + + case operator + when "eq" + value1.to_s == value2.to_s + when "le" + value1.to_f <= value2.to_f + when "ge" + value1.to_f >= value2.to_f + when "lt" + value1.to_f < value2.to_f + when "gt" + value1.to_f > value2.to_f + when "ne" + value1.to_s != value2.to_s + end + end + + def compare_timestamp(operator, value1, value2) + case operator + when "eq" + value1 == value2 + when "le" + value1 <= value2 + when "ge" + value1 >= value2 + when "lt" + value1 < value2 + when "gt" + value1 > value2 + when "ne" + value1 != value2 + end + end +end diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index dd4612a14..abbb2dfa9 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -30,7 +30,7 @@ module Puppet::Network::HTTP::API::V1 key = URI.unescape(key) - Puppet::Indirector::Request.new(indirection, method, key, params) + [indirection, method, key, params] end def indirection2uri(request) @@ -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 61ae2d2fc..82238aa0a 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -61,11 +61,11 @@ module Puppet::Network::HTTP::Handler # handle an HTTP request def process(request, response) - indirection_request = uri2indirection(http_method(request), path(request), params(request)) + indirection, method, key, params = uri2indirection(http_method(request), path(request), params(request)) - check_authorization(indirection_request) + check_authorization(indirection, method, key, params) - send("do_#{indirection_request.method}", indirection_request, request, response) + send("do_#{method}", indirection, key, params, request, response) rescue SystemExit,NoMemoryError raise rescue Exception => e @@ -96,11 +96,16 @@ module Puppet::Network::HTTP::Handler set_response(response, exception.to_s, status) end + def model(indirection_name) + raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) + indirection.model + end + # Execute our find. - def do_find(indirection_request, request, response) - unless result = indirection_request.model.find(indirection_request.key, indirection_request.to_hash) - Puppet.info("Could not find #{indirection_request.indirection_name} for '#{indirection_request.key}'") - return do_exception(response, "Could not find #{indirection_request.indirection_name} #{indirection_request.key}", 404) + def do_find(indirection_name, key, params, request, response) + unless result = model(indirection_name).find(key, params) + Puppet.info("Could not find #{indirection_name} for '#{key}'") + return do_exception(response, "Could not find #{indirection_name} #{key}", 404) end # The encoding of the result must include the format to use, @@ -113,34 +118,35 @@ module Puppet::Network::HTTP::Handler end # Execute our search. - def do_search(indirection_request, request, response) - result = indirection_request.model.search(indirection_request.key, indirection_request.to_hash) + def do_search(indirection_name, key, params, request, response) + model = self.model(indirection_name) + result = model.search(key, params) - if result.nil? or (result.is_a?(Array) and result.empty?) - return do_exception(response, "Could not find instances in #{indirection_request.indirection_name} with '#{indirection_request.key}'", 404) + if result.nil? + return do_exception(response, "Could not find instances in #{indirection_name} with '#{key}'", 404) end format = format_to_use(request) set_content_type(response, format) - set_response(response, indirection_request.model.render_multiple(format, result)) + set_response(response, model.render_multiple(format, result)) end # Execute our destroy. - def do_destroy(indirection_request, request, response) - result = indirection_request.model.destroy(indirection_request.key, indirection_request.to_hash) + def do_destroy(indirection_name, key, params, request, response) + result = model(indirection_name).destroy(key, params) return_yaml_response(response, result) end # Execute our save. - def do_save(indirection_request, request, response) + def do_save(indirection_name, key, params, request, response) data = body(request).to_s raise ArgumentError, "No data to save" if !data or data.empty? format = request_format(request) - obj = indirection_request.model.convert_from(format, data) - result = save_object(indirection_request, obj) + obj = model(indirection_name).convert_from(format, data) + result = obj.save(key) return_yaml_response(response, result) end @@ -162,12 +168,6 @@ module Puppet::Network::HTTP::Handler set_response(response, body.to_yaml) end - # LAK:NOTE This has to be here for testing; it's a stub-point so - # we keep infinite recursion from happening. - def save_object(ind_request, object) - object.save(ind_request.key) - end - def get?(request) http_method(request) == 'GET' end diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb index 82d5a9de2..b6a163316 100644 --- a/lib/puppet/network/rest_authconfig.rb +++ b/lib/puppet/network/rest_authconfig.rb @@ -32,21 +32,21 @@ module Puppet # check wether this request is allowed in our ACL # raise an Puppet::Network::AuthorizedError if the request # is denied. - def allowed?(request) + def allowed?(indirection, method, key, params) read # we're splitting the request in part because # fail_on_deny could as well be called in the XMLRPC context # with a ClientRequest. - @rights.fail_on_deny( - build_uri(request), - - :node => request.node, - :ip => request.ip, - :method => request.method, - :environment => request.environment, - :authenticated => request.authenticated) + @rights.fail_on_deny( + build_uri(indirection, key), + :node => params[:node], + :ip => params[:ip], + :method => method, + :environment => params[:environment], + :authenticated => params[:authenticated] + ) end def initialize(file = nil, parsenow = true) @@ -90,8 +90,8 @@ module Puppet @rights.restrict_authenticated(acl[:acl], acl[:authenticated]) unless acl[:authenticated].nil? end - def build_uri(request) - "/#{request.indirection_name}/#{request.key}" + def build_uri(indirection_name, key) + "/#{indirection_name}/#{key}" end end end diff --git a/lib/puppet/network/rest_authorization.rb b/lib/puppet/network/rest_authorization.rb index e052245eb..50f094e3e 100644 --- a/lib/puppet/network/rest_authorization.rb +++ b/lib/puppet/network/rest_authorization.rb @@ -15,8 +15,8 @@ module Puppet::Network end # Verify that our client has access. - def check_authorization(request) - authconfig.allowed?(request) + def check_authorization(indirection, method, key, params) + authconfig.allowed?(indirection, method, key, params) end end end diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index 2453cd1d5..e8d58e6be 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/facts.rb b/lib/puppet/node/facts.rb index b77ad22d5..d84d54113 100755 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -1,12 +1,17 @@ +require 'time' + require 'puppet/node' require 'puppet/indirector' +require 'puppet/util/pson' + # Manage a given node's facts. This either accepts facts and stores them, or # returns facts for a given node. class Puppet::Node::Facts # Set up indirection, so that nodes can be looked for in # the node sources. extend Puppet::Indirector + extend Puppet::Util::Pson # We want to expire any cached nodes if the facts are saved. module NodeExpirer @@ -30,7 +35,7 @@ class Puppet::Node::Facts @name = name @values = values - add_internal + add_timestamp end def downcase_if_necessary @@ -54,13 +59,37 @@ class Puppet::Node::Facts strip_internal == other.send(:strip_internal) end - private + def self.from_pson(data) + result = new(data['name'], data['values']) + result.values[:_timestamp] = Time.parse(data['timestamp']) + result.expiration = Time.parse(data['expiration']) + result + end + + def to_pson(*args) + { + 'expiration' => expiration, + 'name' => name, + 'timestamp' => values[:_timestamp], + 'values' => values.reject {|k,v| k == :_timestamp}, + }.to_pson(*args) + end # Add internal data to the facts for storage. - def add_internal - self.values[:_timestamp] = Time.now + def add_timestamp + self.timestamp = Time.now end + def timestamp=(time) + self.values[:_timestamp] = time + end + + def timestamp + self.values[:_timestamp] + end + + private + # Strip out that internal data. def strip_internal newvals = values.dup diff --git a/lib/puppet/node/inventory.rb b/lib/puppet/node/inventory.rb new file mode 100644 index 000000000..fd99163b0 --- /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/integration/indirector/bucket_file/rest_spec.rb b/spec/integration/indirector/bucket_file/rest_spec.rb deleted file mode 100644 index dc10faa67..000000000 --- a/spec/integration/indirector/bucket_file/rest_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/file_bucket/file' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Filebucket REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @host = Puppet::SSL::Host.new(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :file_bucket_file ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - @old_terminus = Puppet::FileBucket::File.indirection.terminus_class - Puppet::FileBucket::File.terminus_class = :rest - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @file_bucket_file = stub_everything 'file_bucket_file' - @mock_model = stub('faked model', :name => "file_bucket_file", :convert_from => @file_bucket_file) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - Puppet::FileBucket::File.terminus_class = @old_terminus - end - - it "should be able save a file to the remote filebucket" do - @file_bucket_file.expects(:save) - - file_bucket_file = Puppet::FileBucket::File.new("pouet") - file_bucket_file.save - end -end diff --git a/spec/integration/indirector/certificate/rest_spec.rb b/spec/integration/indirector/certificate/rest_spec.rb deleted file mode 100755 index 58aa96c48..000000000 --- a/spec/integration/indirector/certificate/rest_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/ssl/certificate' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Certificate REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @host = Puppet::SSL::Host.new(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :certificate ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # Then switch to a remote CA, so that we go through REST. - Puppet::SSL::Host.ca_location = :remote - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "certificate") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - end - - it "should be able to retrieve a remote certificate" do - @mock_model.expects(:find).returns @host.certificate - result = Puppet::SSL::Certificate.find('bar') - - # There's no good '==' method on certs. - result.content.to_s.should == @host.certificate.content.to_s - result.name.should == "bar" - end -end diff --git a/spec/integration/indirector/certificate_request/rest_spec.rb b/spec/integration/indirector/certificate_request/rest_spec.rb deleted file mode 100755 index c718b78ab..000000000 --- a/spec/integration/indirector/certificate_request/rest_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/ssl/certificate_request' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Certificate Request REST Terminus" do - before do - Puppet::Util::Cacher.expire - - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - # Create the CSR and write it to disk - @host = Puppet::SSL::Host.new("foo.madstop.com") - @host.generate_certificate_request - - # Now remove the cached csr - Puppet::SSL::Host.ca_location = :none - Puppet::SSL::Host.destroy("foo.madstop.com") - - @params = { :port => 34343, :handlers => [ :certificate_request ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # Then switch to a remote CA, so that we go through REST. - Puppet::SSL::Host.ca_location = :remote - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "certificate request") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - end - - it "should be able to save a certificate request to the CA" do - key = Puppet::SSL::Key.new("bar.madstop.com") - key.generate - - csr = Puppet::SSL::CertificateRequest.new("bar.madstop.com") - csr.generate(key.content) - - server_csr = mock 'csr' - server_csr.expects(:save) - @mock_model.expects(:convert_from).with("s", csr.content.to_s).returns server_csr - - csr.save - end - - it "should be able to retrieve a remote certificate request" do - # We're finding the cached value :/ - @mock_model.expects(:find).returns @host.certificate_request - result = Puppet::SSL::CertificateRequest.find('foo.madstop.com') - - # There's no good '==' method on certs. - result.content.to_s.should == @host.certificate_request.content.to_s - result.name.should == @host.certificate_request.name - end -end diff --git a/spec/integration/indirector/certificate_revocation_list/rest_spec.rb b/spec/integration/indirector/certificate_revocation_list/rest_spec.rb deleted file mode 100755 index 86f2b0150..000000000 --- a/spec/integration/indirector/certificate_revocation_list/rest_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/ssl/certificate' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Certificate REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :certificate_revocation_list ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # And make sure we've generated the CRL - @crl = ca.crl - - # Now remove the cached crl - Puppet::SSL::Host.ca_location = :none - Puppet::SSL::CertificateRevocationList.destroy(Puppet::SSL::CA_NAME) - - # This is necessary so that we create the SSL store before we start - # using REST. This is necessary to prevent an infinite loop, - # which only occurs during testing. - Puppet::Network::HttpPool.ssl_host.ssl_store - - # Then switch to a remote CA, so that we go through REST. - Puppet::SSL::Host.ca_location = :remote - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "certificate") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - end - - it "should be able to retrieve a remote CRL" do - @mock_model.expects(:find).returns @crl - result = Puppet::SSL::CertificateRevocationList.find('bar') - - # There's no good '==' method on certs. - result.content.to_s.should == @crl.content.to_s - end -end diff --git a/spec/integration/indirector/report/rest_spec.rb b/spec/integration/indirector/report/rest_spec.rb deleted file mode 100644 index fdc218975..000000000 --- a/spec/integration/indirector/report/rest_spec.rb +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/transaction/report' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Report REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @host = Puppet::SSL::Host.new(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :report ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # Let's use REST for our reports :-) - @old_terminus = Puppet::Transaction::Report.indirection.terminus_class - Puppet::Transaction::Report.terminus_class = :rest - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'save', which would cause an immediate - # return. - @report = stub_everything 'report' - @mock_model = stub_everything 'faked model', :name => "report", :convert_from => @report - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - Puppet::Transaction::Report.terminus_class = @old_terminus - end - - it "should be able to send a report to the server" do - @report.expects(:save) - - report = Puppet::Transaction::Report.new - - resourcemetrics = { - :total => 12, - :out_of_sync => 20, - :applied => 45, - :skipped => 1, - :restarted => 23, - :failed_restarts => 1, - :scheduled => 10 - } - report.add_metric(:resources, resourcemetrics) - - timemetrics = { - :resource1 => 10, - :resource2 => 50, - :resource3 => 40, - :resource4 => 20, - } - report.add_metric(:times, timemetrics) - - - report.add_metric( - :changes, - - :total => 20 - ) - - report.save - end -end diff --git a/spec/integration/indirector/rest_spec.rb b/spec/integration/indirector/rest_spec.rb deleted file mode 100755 index 14e9e9593..000000000 --- a/spec/integration/indirector/rest_spec.rb +++ /dev/null @@ -1,525 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/network/server' -require 'puppet/indirector' -require 'puppet/indirector/rest' - -# a fake class that will be indirected via REST -class Puppet::TestIndirectedFoo - extend Puppet::Indirector - indirects :test_indirected_foo, :terminus_setting => :test_indirected_foo_terminus - - attr_reader :value - attr_accessor :name - - def initialize(value = 0) - @value = value - end - - def self.from_yaml(yaml) - YAML.load(yaml) - end - - def name - "bob" - end -end - -# empty Terminus class -- this would normally have to be in a directory findable by the autoloader, but we short-circuit that below -class Puppet::TestIndirectedFoo::Rest < Puppet::Indirector::REST -end - - -describe Puppet::Indirector::REST do - before do - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::SSL::Host.ca_location = :local - - Puppet::TestIndirectedFoo.terminus_class = :rest - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - end - - describe "when using webrick" do - before :each do - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :test_indirected_foo ], :xmlrpc_handlers => [ :status ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "foo") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - # do not trigger the authorization layer - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - describe "when finding a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @model_instance = Puppet::TestIndirectedFoo.new(23) - @mock_model.stubs(:find).returns @model_instance - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should_not raise_error - end - - it 'should return an instance of the model class' do - Puppet::TestIndirectedFoo.find('bar').class.should == Puppet::TestIndirectedFoo - end - - it "should pass options all the way through" do - @mock_model.expects(:find).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instance - Puppet::TestIndirectedFoo.find('bar', :one => "two", :three => "four") - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.find('bar').value.should == @model_instance.value - end - - it 'should set an expiration on model instance' do - Puppet::TestIndirectedFoo.find('bar').expiration.should_not be_nil - end - - it "should use a supported format" do - Puppet::TestIndirectedFoo.expects(:supported_formats).returns ["marshal"] - text = Marshal.dump(@model_instance) - @model_instance.expects(:render).with(Puppet::Network::FormatHandler.format("marshal")).returns text - Puppet::TestIndirectedFoo.find('bar') - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model = stub('faked model', :name => "foo", :find => nil) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should return nil" do - Puppet::TestIndirectedFoo.find('bar').should be_nil - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model = stub('faked model', :name => "foo") - @mock_model.stubs(:find).raises(RuntimeError) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when searching for model instances over REST" do - describe "when matching model instances can be found" do - before :each do - @model_instances = [ Puppet::TestIndirectedFoo.new(23), Puppet::TestIndirectedFoo.new(24) ] - @mock_model.stubs(:search).returns @model_instances - - # Force yaml, because otherwise our mocks can't work correctly - Puppet::TestIndirectedFoo.stubs(:supported_formats).returns %w{yaml} - - @mock_model.stubs(:render_multiple).returns @model_instances.to_yaml - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.search('bar') }.should_not raise_error - end - - it 'should return all matching results' do - Puppet::TestIndirectedFoo.search('bar').length.should == @model_instances.length - end - - it "should pass options all the way through" do - @mock_model.expects(:search).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instances - Puppet::TestIndirectedFoo.search("foo", :one => "two", :three => "four") - end - - it 'should return model instances' do - Puppet::TestIndirectedFoo.search('bar').each do |result| - result.class.should == Puppet::TestIndirectedFoo - end - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.search('bar').collect { |i| i.value }.should == @model_instances.collect { |i| i.value } - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model = stub('faked model', :name => "foo", :find => nil) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should return nil" do - Puppet::TestIndirectedFoo.find('bar').should be_nil - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model = stub('faked model') - @mock_model.stubs(:find).raises(RuntimeError) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when destroying a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns true - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should_not raise_error - end - - it 'should return success' do - Puppet::TestIndirectedFoo.destroy('bar').should == true - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns false - end - - it "should return failure" do - Puppet::TestIndirectedFoo.destroy('bar').should == false - end - end - - describe "when an exception is encountered in destroying a model instance" do - before :each do - @mock_model.stubs(:destroy).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when saving a model instance over REST" do - before :each do - @instance = Puppet::TestIndirectedFoo.new(42) - @mock_model.stubs(:save_object).returns @instance - @mock_model.stubs(:convert_from).returns @instance - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).returns(@instance) - end - - describe "when a successful save can be performed" do - before :each do - end - - it "should not fail" do - lambda { @instance.save }.should_not raise_error - end - - it 'should return an instance of the model class' do - @instance.save.class.should == Puppet::TestIndirectedFoo - end - - it 'should return a matching instance of the model class' do - @instance.save.value.should == @instance.value - end - end - - describe "when a save cannot be completed" do - before :each do - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).returns(false) - end - - it "should return failure" do - @instance.save.should == false - end - end - - describe "when an exception is encountered in performing a save" do - before :each do - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { @instance.save }.should raise_error(Net::HTTPError) - end - end - end - - after :each do - @server.unlisten - end - end - - describe "when using mongrel" do - confine "Mongrel is not available" => Puppet.features.mongrel? - - before :each do - Puppet[:servertype] = 'mongrel' - @params = { :port => 34343, :handlers => [ :test_indirected_foo ] } - - # Make sure we never get a cert, since mongrel can't speak ssl - Puppet::SSL::Certificate.stubs(:find).returns nil - - # We stub ssl to be off, since mongrel can't speak ssl - Net::HTTP.any_instance.stubs(:use_ssl?).returns false - - @server = Puppet::Network::Server.new(@params) - @server.listen - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "foo") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - # do not trigger the authorization layer - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - @server.unlisten - end - - describe "when finding a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @model_instance = Puppet::TestIndirectedFoo.new(23) - @mock_model.stubs(:find).returns @model_instance - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should_not raise_error - end - - it 'should return an instance of the model class' do - Puppet::TestIndirectedFoo.find('bar').class.should == Puppet::TestIndirectedFoo - end - - it "should pass options all the way through" do - @mock_model.expects(:find).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instance - Puppet::TestIndirectedFoo.find('bar', :one => "two", :three => "four") - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.find('bar').value.should == @model_instance.value - end - - it 'should set an expiration on model instance' do - Puppet::TestIndirectedFoo.find('bar').expiration.should_not be_nil - end - - it "should use a supported format" do - Puppet::TestIndirectedFoo.expects(:supported_formats).returns ["marshal"] - format = stub 'format' - text = Marshal.dump(@model_instance) - @model_instance.expects(:render).with(Puppet::Network::FormatHandler.format("marshal")).returns text - Puppet::TestIndirectedFoo.find('bar') - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:find).returns nil - end - - it "should return nil" do - Puppet::TestIndirectedFoo.find('bar').should be_nil - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model.stubs(:find).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when searching for model instances over REST" do - describe "when matching model instances can be found" do - before :each do - @model_instances = [ Puppet::TestIndirectedFoo.new(23), Puppet::TestIndirectedFoo.new(24) ] - - # Force yaml, because otherwise our mocks can't work correctly - Puppet::TestIndirectedFoo.stubs(:supported_formats).returns %w{yaml} - - @mock_model.stubs(:search).returns @model_instances - @mock_model.stubs(:render_multiple).returns @model_instances.to_yaml - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.search('bar') }.should_not raise_error - end - - it 'should return all matching results' do - Puppet::TestIndirectedFoo.search('bar').length.should == @model_instances.length - end - - it "should pass options all the way through" do - @mock_model.expects(:search).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instances - Puppet::TestIndirectedFoo.search('bar', :one => "two", :three => "four") - end - - it 'should return model instances' do - Puppet::TestIndirectedFoo.search('bar').each do |result| - result.class.should == Puppet::TestIndirectedFoo - end - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.search('bar').collect { |i| i.value }.should == @model_instances.collect { |i| i.value } - end - - it 'should set an expiration on model instances' do - Puppet::TestIndirectedFoo.search('bar').each do |result| - result.expiration.should_not be_nil - end - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:search).returns nil - @mock_model.stubs(:render_multiple).returns nil.to_yaml - end - - it "should return nil" do - Puppet::TestIndirectedFoo.search('bar').should == [] - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model.stubs(:find).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when destroying a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns true - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should_not raise_error - end - - it 'should return success' do - Puppet::TestIndirectedFoo.destroy('bar').should == true - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns false - end - - it "should return failure" do - Puppet::TestIndirectedFoo.destroy('bar').should == false - end - end - - describe "when an exception is encountered in destroying a model instance" do - before :each do - @mock_model.stubs(:destroy).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when saving a model instance over REST" do - before :each do - @instance = Puppet::TestIndirectedFoo.new(42) - @mock_model.stubs(:convert_from).returns @instance - - # LAK:NOTE This stub is necessary to prevent the REST call from calling - # REST.save again, thus producing painful infinite recursion. - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).returns(@instance) - end - - describe "when a successful save can be performed" do - before :each do - end - - it "should not fail" do - lambda { @instance.save }.should_not raise_error - end - - it 'should return an instance of the model class' do - @instance.save.class.should == Puppet::TestIndirectedFoo - end - - it 'should return a matching instance of the model class' do - @instance.save.value.should == @instance.value - end - end - - describe "when a save cannot be completed" do - before :each do - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).returns(false) - end - - it "should return failure" do - @instance.save.should == false - end - end - - describe "when an exception is encountered in performing a save" do - before :each do - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { @instance.save }.should raise_error(Net::HTTPError) - end - end - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b5b273857..ed4e2c2fb 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/catalog/compiler_spec.rb b/spec/unit/indirector/catalog/compiler_spec.rb index 2ae5f6ff3..f9980807a 100755 --- a/spec/unit/indirector/catalog/compiler_spec.rb +++ b/spec/unit/indirector/catalog/compiler_spec.rb @@ -9,6 +9,7 @@ require 'puppet/indirector/catalog/compiler' describe Puppet::Resource::Catalog::Compiler do before do + require 'puppet/rails' Puppet::Rails.stubs(:init) Facter.stubs(:to_hash).returns({}) Facter.stubs(:value).returns(Facter::Util::Fact.new("something")) @@ -155,7 +156,8 @@ describe Puppet::Resource::Catalog::Compiler do @compiler = Puppet::Resource::Catalog::Compiler.new @request = stub 'request', :options => {} - @facts = stub 'facts', :save => nil + @facts = Puppet::Node::Facts.new('hostname', "fact" => "value", "architecture" => "i386") + @facts.stubs(:save).returns(nil) end it "should do nothing if no facts are provided" do @@ -165,12 +167,17 @@ describe Puppet::Resource::Catalog::Compiler do @compiler.extract_facts_from_request(@request) end - it "should use the Facts class to deserialize the provided facts" do + it "should use the Facts class to deserialize the provided facts and update the timestamp" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts + @facts.timestamp = Time.parse('2010-11-01') + @now = Time.parse('2010-11-02') + Time.expects(:now).returns(@now) + @compiler.extract_facts_from_request(@request) + @facts.timestamp.should == @now end it "should use the provided fact format" do diff --git a/spec/unit/indirector/facts/yaml_spec.rb b/spec/unit/indirector/facts/yaml_spec.rb index e7bac3471..37a1bcae0 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 000000000..9f0c54353 --- /dev/null +++ b/spec/unit/indirector/inventory/yaml_spec.rb @@ -0,0 +1,221 @@ +#!/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 assert_search_matches(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 + 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" do + assert_search_matches({ + '/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'} + ) + end + + it "should return empty array when no nodes match the search query options" do + assert_search_matches({}, { + "/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'} + ) + end + + + it "should return node names that match the search query options with the greater than operator" do + assert_search_matches({ + '/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'} + ) + end + + it "should return node names that match the search query options with the less than operator" do + assert_search_matches({ + '/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'} + ) + end + + it "should return node names that match the search query options with the less than or equal to operator" do + assert_search_matches({ + '/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'} + ) + end + + it "should return node names that match the search query options with the greater than or equal to operator" do + assert_search_matches({ + '/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'} + ) + end + + it "should return node names that match the search query options with the not equal operator" do + assert_search_matches({ + '/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'} + ) + end + + def apply_timestamp(facts, timestamp) + facts.timestamp = timestamp + facts + end + + it "should be able to query based on meta.timestamp.gt" do + assert_search_matches({ + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + }, + { + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + {'meta.timestamp.gt' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.le" do + assert_search_matches({ + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + }, + {'meta.timestamp.le' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.lt" do + assert_search_matches({ + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + {'meta.timestamp.lt' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.ge" do + assert_search_matches({ + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + {'meta.timestamp.ge' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.eq" do + assert_search_matches({ + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + {'meta.timestamp.eq' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp" do + assert_search_matches({ + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + { + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + {'meta.timestamp' => '2010-10-15'} + ) + end + + it "should be able to query based on meta.timestamp.ne" do + assert_search_matches({ + '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), + '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), + '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), + '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), + }, + { + '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), + }, + {'meta.timestamp.ne' => '2010-10-15'} + ) + end +end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index c593242c0..84b98ddaf 100644 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -32,7 +32,7 @@ describe Puppet::Network::HTTP::API::V1 do end it "should use the first field of the URI as the environment" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).environment.should == Puppet::Node::Environment.new("env") + @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should == "env" end it "should fail if the environment is not alphanumeric" do @@ -40,11 +40,11 @@ describe Puppet::Network::HTTP::API::V1 do end it "should use the environment from the URI even if one is specified in the parameters" do - @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"}).environment.should == Puppet::Node::Environment.new("env") + @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].should == "env" end it "should use the second field of the URI as the indirection name" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).indirection_name.should == :foo + @tester.uri2indirection("GET", "/env/foo/bar", {})[0].should == "foo" end it "should fail if the indirection name is not alphanumeric" do @@ -52,11 +52,11 @@ describe Puppet::Network::HTTP::API::V1 do end it "should use the remainder of the URI as the indirection key" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).key.should == "bar" + @tester.uri2indirection("GET", "/env/foo/bar", {})[2].should == "bar" end it "should support the indirection key being a /-separated file path" do - @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {}).key.should == "bee/baz/bomb" + @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {})[2].should == "bee/baz/bomb" end it "should fail if no indirection key is specified" do @@ -65,19 +65,31 @@ describe Puppet::Network::HTTP::API::V1 do end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).method.should == :find + @tester.uri2indirection("GET", "/env/foo/bar", {})[1].should == :find end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do - @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search + @tester.uri2indirection("GET", "/env/foos/bar", {})[1].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", {})[1].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", {})[1].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", {})[1].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 + @tester.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do - @tester.uri2indirection("PUT", "/env/foo/bar", {}).method.should == :save + @tester.uri2indirection("PUT", "/env/foo/bar", {})[1].should == :save end it "should fail if an indirection method cannot be picked" do @@ -86,7 +98,8 @@ describe Puppet::Network::HTTP::API::V1 do it "should URI unescape the indirection key" do escaped = URI.escape("foo bar") - @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}).key.should == "foo bar" + indirection_name, method, key, params = @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}) + key.should == "foo bar" end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 76a9c5530..16f1b5349 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -79,37 +79,22 @@ describe Puppet::Network::HTTP::Handler do @handler.process(@request, @response) end - it "should call the 'do' method associated with the indirection method" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request + it "should call the 'do' method and delegate authorization to the RestAuthorization layer" do + @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) - request.expects(:method).returns "mymethod" + @handler.expects(:do_mymethod).with("facts", "key", {:node => "name"}, @request, @response) - @handler.expects(:do_mymethod).with(request, @request, @response) - - @handler.process(@request, @response) - end - - it "should delegate authorization to the RestAuthorization layer" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request - - request.expects(:method).returns "mymethod" - - @handler.expects(:do_mymethod).with(request, @request, @response) - - @handler.expects(:check_authorization).with(request) + @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}) @handler.process(@request, @response) end it "should return 403 if the request is not authorized" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request + @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) @handler.expects(:do_mymethod).never - @handler.expects(:check_authorization).with(request).raises(Puppet::Network::AuthorizationError.new("forbindden")) + @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden")) @handler.expects(:set_response).with { |response, body, status| status == 403 } @@ -117,7 +102,7 @@ describe Puppet::Network::HTTP::Handler do end it "should serialize a controller exception when an exception is thrown while finding the model instance" do - @handler.expects(:uri2indirection).returns stub("request", :method => :find) + @handler.expects(:uri2indirection).returns(["facts", :find, "key", {:node => "name"}]) @handler.expects(:do_find).raises(ArgumentError, "The exception") @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 } @@ -141,9 +126,8 @@ describe Puppet::Network::HTTP::Handler do describe "when finding a model instance" do before do - @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "my_result", :model => @model_class - @model_class.stubs(:find).returns @result + Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @@ -153,40 +137,37 @@ describe Puppet::Network::HTTP::Handler do end it "should use the indirection request to find the model class" do - @irequest.expects(:model).returns @model_class - - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key" do @model_class.expects(:find).with do |key, args| key == "my_result" end.returns @result - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) @model_class.expects(:find).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should set the content type to the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should fail if no accept header is provided" do @handler.expects(:accept_header).with(@request).returns nil - lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(ArgumentError) + lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should fail if the accept header does not contain a valid format" do @handler.expects(:accept_header).with(@request).returns "" - lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(RuntimeError) + lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(RuntimeError) end it "should not use an unsuitable format" do @@ -198,7 +179,7 @@ describe Puppet::Network::HTTP::Handler do @handler.expects(:set_content_type).with(@response, bar) # the suitable one - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should render the result using the first format specified in the accept header" do @@ -206,12 +187,12 @@ describe Puppet::Network::HTTP::Handler do @handler.expects(:accept_header).with(@request).returns "one,two" @result.expects(:render).with(@oneformat) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the default status when a model find call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a serialized object when a model find call succeeds" do @@ -220,14 +201,14 @@ describe Puppet::Network::HTTP::Handler do @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" } @model_class.stubs(:find).returns(@model_instance) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when no model instance can be found" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @model_class.stubs(:find).returns(nil) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should write a log message when no model instance can be found" do @@ -236,7 +217,7 @@ describe Puppet::Network::HTTP::Handler do Puppet.expects(:info).with("Could not find my_handler for 'my_result'") - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end @@ -246,13 +227,13 @@ describe Puppet::Network::HTTP::Handler do @handler.expects(:format_to_use).returns(@oneformat) @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object" @model_class.stubs(:find).returns(@model_instance) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end end describe "when searching for model instances" do before do - @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result1 = mock 'result1' @result2 = mock 'results' @@ -269,29 +250,26 @@ describe Puppet::Network::HTTP::Handler do end it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) @model_class.expects(:search).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status when a model search call succeeds" do @model_class.stubs(:search).returns(@result) - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should set the content type to the first format returned by the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a list of serialized objects when a model search call succeeds" do @@ -302,57 +280,56 @@ describe Puppet::Network::HTTP::Handler do @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances" @handler.expects(:set_response).with { |response, data| data == "my rendered instances" } - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @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([]) - @handler.do_search(@irequest, @request, @response) + @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]" + + + @handler.expects(:set_response).with { |response, data| data == "[]" } + @handler.do_search("my_handler", "my_result", {}, @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([]) - @handler.do_search(@irequest, @request, @response) + @model_class.stubs(:search).returns(nil) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end end describe "when destroying a model instance" do before do - @irequest = stub 'indirection_request', :method => :destroy, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result = stub 'result', :render => "the result" @model_class.stubs(:destroy).returns @result end it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key to destroy the instance in the model" do - @irequest.expects(:key).returns "foo bar" @model_class.expects(:destroy).with do |key, args| key == "foo bar" end - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "foo bar", {}, @request, @response) end it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) @model_class.expects(:destroy).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status code a model destroy call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should return a yaml-encoded result when a model destroy call succeeds" do @@ -361,13 +338,13 @@ describe Puppet::Network::HTTP::Handler do @handler.expects(:set_response).with { |response, body, status| body == "the result" } - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end end describe "when saving a model instance" do before do - @irequest = stub 'indirection_request', :method => :save, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + Puppet::Indirector::Indirection.stubs(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @handler.stubs(:body).returns('my stuff') @handler.stubs(:content_type_header).returns("text/yaml") @@ -383,43 +360,41 @@ describe Puppet::Network::HTTP::Handler do end it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the 'body' hook to retrieve the body of the request" do @handler.expects(:body).returns "my body" @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should fail to save model if data is not specified" do @handler.stubs(:body).returns('') - lambda { @handler.do_save(@irequest, @request, @response) }.should raise_error(ArgumentError) + lambda { @handler.do_save("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should use a common method for determining the request parameters" do @model_instance.expects(:save).with('key').once - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "key", {}, @request, @response) end it "should use the default status when a model save call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should return the yaml-serialized result when a model save call succeeds" do @model_instance.stubs(:save).returns(@model_instance) @model_instance.expects(:to_yaml).returns('foo') - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should set the content to yaml" do @handler.expects(:set_content_type).with(@response, @yamlformat) - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the content-type header to know the body format" do @@ -428,7 +403,7 @@ describe Puppet::Network::HTTP::Handler do @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end end end diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb index 351f3f040..e81eb41ed 100755 --- a/spec/unit/network/rest_authconfig_spec.rb +++ b/spec/unit/network/rest_authconfig_spec.rb @@ -30,9 +30,6 @@ describe Puppet::Network::RestAuthConfig do @acl = stub_everything 'rights' @authconfig.rights = @acl - - @request = stub 'request', :indirection_name => "path", :key => "to/resource", :ip => "127.0.0.1", - :node => "me", :method => :save, :environment => :env, :authenticated => true end it "should use the puppet default rest authorization file" do @@ -41,16 +38,10 @@ describe Puppet::Network::RestAuthConfig do Puppet::Network::RestAuthConfig.new(nil, false) end - it "should read the config file when needed" do - @authconfig.expects(:read) - - @authconfig.allowed?(@request) - end - it "should ask for authorization to the ACL subsystem" do @acl.expects(:fail_on_deny).with("/path/to/resource", :node => "me", :ip => "127.0.0.1", :method => :save, :environment => :env, :authenticated => true) - @authconfig.allowed?(@request) + @authconfig.allowed?("path", :save, "to/resource", :ip => "127.0.0.1", :node => "me", :environment => :env, :authenticated => true) end describe "when defining an acl with mk_acl" do diff --git a/spec/unit/network/rest_authorization_spec.rb b/spec/unit/network/rest_authorization_spec.rb deleted file mode 100755 index 0cb0bcee9..000000000 --- a/spec/unit/network/rest_authorization_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -require 'puppet/network/rest_authorization' - -class RestAuthorized - include Puppet::Network::RestAuthorization -end - - -describe Puppet::Network::RestAuthorization do - before :each do - @auth = RestAuthorized.new - @authconig = stub 'authconfig' - @auth.stubs(:authconfig).returns(@authconfig) - - @request = stub_everything 'request' - @request.stubs(:method).returns(:find) - @request.stubs(:node).returns("node") - @request.stubs(:ip).returns("ip") - end - - describe "when testing request authorization" do - it "should delegate to the current rest authconfig" do - @authconfig.expects(:allowed?).with(@request).returns(true) - - @auth.check_authorization(@request) - end - - it "should raise an AuthorizationError if authconfig raises an AuthorizationError" do - @authconfig.expects(:allowed?).with(@request).raises(Puppet::Network::AuthorizationError.new("forbidden")) - - lambda { @auth.check_authorization(@request) }.should raise_error(Puppet::Network::AuthorizationError) - end - - it "should not raise an AuthorizationError if request is allowed" do - @authconfig.expects(:allowed?).with(@request).returns(true) - - lambda { @auth.check_authorization(@request) }.should_not raise_error(Puppet::Network::AuthorizationError) - end - end -end diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb index 394db7913..cb2aa3dc7 100755 --- a/spec/unit/node/facts_spec.rb +++ b/spec/unit/node/facts_spec.rb @@ -109,5 +109,29 @@ describe Puppet::Node::Facts, "when indirecting" do facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four") facts.values[:_timestamp].should be_instance_of(Time) end + + describe "using pson" do + before :each do + @timestamp = Time.parse("Thu Oct 28 11:16:31 -0700 2010") + @expiration = Time.parse("Thu Oct 28 11:21:31 -0700 2010") + end + + it "should accept properly formatted pson" do + pson = %Q({"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}) + format = Puppet::Network::FormatHandler.format('pson') + facts = format.intern(Puppet::Node::Facts,pson) + facts.name.should == 'foo' + facts.expiration.should == @expiration + facts.values.should == {'a' => '1', 'b' => '2', 'c' => '3', :_timestamp => @timestamp} + end + + it "should generate properly formatted pson" do + Time.stubs(:now).returns(@timestamp) + facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) + facts.expiration = @expiration + pson = PSON.parse(facts.to_pson) + pson.should == {"name"=>"foo", "timestamp"=>@timestamp.to_s, "expiration"=>@expiration.to_s, "values"=>{"a"=>1, "b"=>2, "c"=>3}} + end + end end end |