From 077312af8670cb035506aa4eefe3d7a677dac029 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Mon, 24 Mar 2008 16:15:36 -0700 Subject: [PATCH 01/48] Added rspec tests for loadedfile --- spec/unit/util/loadedfile.rb | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 spec/unit/util/loadedfile.rb diff --git a/spec/unit/util/loadedfile.rb b/spec/unit/util/loadedfile.rb new file mode 100644 index 000000000..89c7547e0 --- /dev/null +++ b/spec/unit/util/loadedfile.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'tempfile' +require 'puppet/util/loadedfile' + +describe Puppet::Util::LoadedFile do + before(:all) do + # First, save and adjust the timeout so tests don't take forever. + @saved_filetimeout = Puppet[:filetimeout] + Puppet[:filetimeout] = 1 + end + + before(:each) do + @f = Tempfile.new('loadedfile_test') + @loaded = Puppet::Util::LoadedFile.new(@f.path) + end + + it "should recognize when the file has not changed" do + sleep(Puppet[:filetimeout]) + @loaded.changed?.should == false + end + + it "should recognize when the file has changed" do + @f.puts "Hello" + @f.flush + sleep(Puppet[:filetimeout]) + @loaded.changed?.should be_an_instance_of(Time) + end + + after(:each) do + @f.close + end + + after(:all) do + # Restore the saved timeout. + Puppet[:filetimeout] = @saved_filetimeout + end +end From 9cf7150549caf5d21a2a22f30d92cd5a73d00a40 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Tue, 25 Mar 2008 14:09:15 -0700 Subject: [PATCH 02/48] Added some more tests for loadedfile, based off the old unit tests. --- spec/unit/util/loadedfile.rb | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/spec/unit/util/loadedfile.rb b/spec/unit/util/loadedfile.rb index 89c7547e0..b57475f96 100644 --- a/spec/unit/util/loadedfile.rb +++ b/spec/unit/util/loadedfile.rb @@ -9,11 +9,13 @@ describe Puppet::Util::LoadedFile do before(:all) do # First, save and adjust the timeout so tests don't take forever. @saved_filetimeout = Puppet[:filetimeout] - Puppet[:filetimeout] = 1 + Puppet[:filetimeout] = 5 end before(:each) do @f = Tempfile.new('loadedfile_test') + @f.puts "yayness" + @f.flush @loaded = Puppet::Util::LoadedFile.new(@f.path) end @@ -23,12 +25,31 @@ describe Puppet::Util::LoadedFile do end it "should recognize when the file has changed" do - @f.puts "Hello" + @f.puts "booness" @f.flush sleep(Puppet[:filetimeout]) @loaded.changed?.should be_an_instance_of(Time) end + it "should not catch a change until the timeout has elapsed" do + @f.puts "yay" + @f.flush + @loaded.changed?.should be(false) + sleep(Puppet[:filetimeout]) + @loaded.changed?.should_not be(false) + end + + it "should consider a file changed when that file is missing" do + @f.close! + sleep(Puppet[:filetimeout]) + @loaded.changed?.should_not be(false) + end + + it "should disable checking if Puppet[:filetimeout] is negative" do + Puppet[:filetimeout] = -1 + @loaded.changed?.should_not be(false) + end + after(:each) do @f.close end From 874a02f00d15350665de19de246d715c702a439b Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 27 Mar 2008 20:47:50 +1100 Subject: [PATCH 03/48] Added check_puppet.rb Nagios check plugin (See #1162) --- ext/nagios/check_puppet.rb | 117 +++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 ext/nagios/check_puppet.rb diff --git a/ext/nagios/check_puppet.rb b/ext/nagios/check_puppet.rb new file mode 100755 index 000000000..b65ede33a --- /dev/null +++ b/ext/nagios/check_puppet.rb @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby + +require 'optparse' +require 'sys/proctable' +include Sys + +class CheckPuppet + + VERSION = '0.1' + script_name = File.basename($0) + + # default options + OPTIONS = { + :statefile => "/var/puppet/state/state.yaml", + :process => "puppetd", + :interval => 30, + } + + o = OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{script_name} [OPTIONS]" + o.define_head "The check_puppet Nagios plug-in checks that specified " + + "Puppet process is running and the state file is no " + + "older than specified interval." + o.separator "" + o.separator "Mandatory arguments to long options are mandatory for " + + "short options too." + + o.on("-s", "--statefile=statefile", String, "The state file", + "Default: #{OPTIONS[:statefile]}") { |OPTIONS[:statefile]| } + o.on("-p", "--process=processname", String, "The process to check", + "Default: #{OPTIONS[:process]}") { |OPTIONS[:process]| } + o.on("-i", "--interval=value", Integer, + "Default: #{OPTIONS[:interval]} minutes") { |OPTIONS[:interval]| } + + o.separator "" + o.on_tail("-h", "--help", "Show this help message.") do + puts o + exit + end + + o.parse!(ARGV) + end + + def check_proc + + unless ProcTable.ps.find { |p| p.name == OPTIONS[:process]} + @proc = 2 + else + @proc = 0 + end + + end + + def check_state + + # Set variables + curt = Time.now + intv = OPTIONS[:interval] * 60 + + # Check file time + begin + @modt = File.mtime("#{OPTIONS[:statefile]}") + rescue + @file = 3 + end + + diff = (curt - @modt).to_i + + if diff > intv + @file = 2 + else + @file = 0 + end + + end + + def output_status + + case @file + when 0 + state = "state file status okay updated on " + @modt.strftime("%m/%d/%Y at %H:%M:%S") + when 2 + state = "state fille is not up to date and is older than #{OPTIONS[:interval]} minutes" + when 3 + state = "state file status unknown" + end + + case @proc + when 0 + process = "process #{OPTIONS[:process]} is running" + when 2 + process = "process #{OPTIONS[:process]} is not running" + end + + case @proc or @file + when 0 + status = "OK" + exitcode = 0 + when 2 + status = "CRITICAL" + exitcode = 2 + when 3 + status = "UNKNOWN" + exitcide = 3 + end + + puts "PUPPET " + status + ": " + process + ", " + state + exit(exitcode) + end +end + +cp = CheckPuppet.new +cp.check_proc +cp.check_state +cp.output_status + From 9d30b2603365ee71c8b8cb23635079827189a9c2 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Tue, 25 Mar 2008 10:30:41 -0700 Subject: [PATCH 04/48] Fixes #1148 - replaces #!/usr/bin/ruby with #!/usr/bin/env ruby. --- bin/pi | 2 +- bin/ralsh | 2 +- lib/puppet/dsl.rb | 4 ++-- spec/unit/file_serving/configuration.rb | 2 +- spec/unit/file_serving/configuration/parser.rb | 2 +- spec/unit/indirector/checksum/file.rb | 2 +- spec/unit/indirector/direct_file_server.rb | 2 +- spec/unit/indirector/facts/facter.rb | 2 +- spec/unit/indirector/file.rb | 2 +- spec/unit/indirector/file_server.rb | 2 +- spec/unit/parser/ast/node.rb | 2 +- spec/unit/ral/provider/mount/parsed.rb | 2 +- spec/unit/ral/type/mount.rb | 2 +- test/lib/stubba.rb | 2 +- 14 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bin/pi b/bin/pi index 5416e693f..0b700a2d0 100755 --- a/bin/pi +++ b/bin/pi @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # # = Synopsis diff --git a/bin/ralsh b/bin/ralsh index fdf64916f..1d0036717 100755 --- a/bin/ralsh +++ b/bin/ralsh @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # vim: softtabstop=4 shiftwidth=4 expandtab # # = Synopsis diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb index 966feaf9b..714b350f8 100644 --- a/lib/puppet/dsl.rb +++ b/lib/puppet/dsl.rb @@ -1,7 +1,7 @@ # Just quick mess-around to see what a DSL would look like. # # This is what the executable could look like: -##!/usr/bin/ruby +##!/usr/bin/env ruby # #require 'puppet' #require 'puppet/dsl' @@ -23,7 +23,7 @@ # And here's what an example config could look like: # -##!/usr/bin/ruby +##!/usr/bin/env ruby # # # require 'puppet' diff --git a/spec/unit/file_serving/configuration.rb b/spec/unit/file_serving/configuration.rb index eecaefe5f..a0710e20d 100755 --- a/spec/unit/file_serving/configuration.rb +++ b/spec/unit/file_serving/configuration.rb @@ -221,4 +221,4 @@ describe Puppet::FileServing::Configuration do @config.authorized?("/one/my/file").should be_false end end -end \ No newline at end of file +end diff --git a/spec/unit/file_serving/configuration/parser.rb b/spec/unit/file_serving/configuration/parser.rb index df2f629d5..93d30ca1c 100755 --- a/spec/unit/file_serving/configuration/parser.rb +++ b/spec/unit/file_serving/configuration/parser.rb @@ -132,4 +132,4 @@ describe Puppet::FileServing::Configuration::Parser do @parser.parse end end -end \ No newline at end of file +end diff --git a/spec/unit/indirector/checksum/file.rb b/spec/unit/indirector/checksum/file.rb index 4f8ee98b2..3a82faccc 100755 --- a/spec/unit/indirector/checksum/file.rb +++ b/spec/unit/indirector/checksum/file.rb @@ -137,4 +137,4 @@ describe Puppet::Checksum::File do @store.destroy(file) end end -end \ No newline at end of file +end diff --git a/spec/unit/indirector/direct_file_server.rb b/spec/unit/indirector/direct_file_server.rb index 9f3652536..a89b938e7 100755 --- a/spec/unit/indirector/direct_file_server.rb +++ b/spec/unit/indirector/direct_file_server.rb @@ -89,4 +89,4 @@ describe Puppet::Indirector::DirectFileServer do @server.search(@uri, :testing => :one, :other => :two) end end -end \ No newline at end of file +end diff --git a/spec/unit/indirector/facts/facter.rb b/spec/unit/indirector/facts/facter.rb index 0974a60ec..5ec66555d 100755 --- a/spec/unit/indirector/facts/facter.rb +++ b/spec/unit/indirector/facts/facter.rb @@ -73,4 +73,4 @@ describe Puppet::Node::Facts::Facter do describe Puppet::Node::Facts::Facter, " when loading facts from the factpath" do it "should load every fact in each factpath directory" end -end \ No newline at end of file +end diff --git a/spec/unit/indirector/file.rb b/spec/unit/indirector/file.rb index 37740f0d0..a2aa4502d 100755 --- a/spec/unit/indirector/file.rb +++ b/spec/unit/indirector/file.rb @@ -155,4 +155,4 @@ describe Puppet::Indirector::File do @searcher.destroy(file) end end -end \ No newline at end of file +end diff --git a/spec/unit/indirector/file_server.rb b/spec/unit/indirector/file_server.rb index 974b95e0e..79be8cc29 100755 --- a/spec/unit/indirector/file_server.rb +++ b/spec/unit/indirector/file_server.rb @@ -165,4 +165,4 @@ describe Puppet::Indirector::FileServer do @file_server.search(@uri, :testing => :one, :other => :two) end end -end \ No newline at end of file +end diff --git a/spec/unit/parser/ast/node.rb b/spec/unit/parser/ast/node.rb index 757934415..aaba4c2e8 100755 --- a/spec/unit/parser/ast/node.rb +++ b/spec/unit/parser/ast/node.rb @@ -122,4 +122,4 @@ describe Puppet::Parser::AST::Node do @compiler.class_scope(@middle).namespaces.should be_include(@top.namespace) end end -end \ No newline at end of file +end diff --git a/spec/unit/ral/provider/mount/parsed.rb b/spec/unit/ral/provider/mount/parsed.rb index b2ca22987..21276d911 100755 --- a/spec/unit/ral/provider/mount/parsed.rb +++ b/spec/unit/ral/provider/mount/parsed.rb @@ -182,4 +182,4 @@ describe provider_class do it "should specify the filesystem when remounting a filesystem" end -end \ No newline at end of file +end diff --git a/spec/unit/ral/type/mount.rb b/spec/unit/ral/type/mount.rb index 9247601e6..8fa2e6f7c 100755 --- a/spec/unit/ral/type/mount.rb +++ b/spec/unit/ral/type/mount.rb @@ -189,4 +189,4 @@ describe Puppet::Type::Mount::Ensure do @mount.refresh end end -end \ No newline at end of file +end diff --git a/test/lib/stubba.rb b/test/lib/stubba.rb index eade747f6..747054cfc 100644 --- a/test/lib/stubba.rb +++ b/test/lib/stubba.rb @@ -1,2 +1,2 @@ # for backwards compatibility -require 'mocha' \ No newline at end of file +require 'mocha' From 4c63b69f4b634b1b30cfcd6bd5b770a5518b6814 Mon Sep 17 00:00:00 2001 From: Blake Barnett Date: Thu, 27 Mar 2008 20:36:04 -0700 Subject: [PATCH 05/48] Add a bunch of directives, allows a full parse of stanford's huge nagios config Also reformatted a bit --- lib/puppet/external/nagios/base.rb | 116 ++++++++++++++++------------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/lib/puppet/external/nagios/base.rb b/lib/puppet/external/nagios/base.rb index efc3982b4..25c689559 100755 --- a/lib/puppet/external/nagios/base.rb +++ b/lib/puppet/external/nagios/base.rb @@ -228,7 +228,9 @@ class Nagios::Base # This is probably a bad idea. def name=(value) - send(self.class.namevar.to_s + "=", value) + unless self.class.namevar.to_s == "name" + send(self.class.namevar.to_s + "=", value) + end end def namevar @@ -318,59 +320,64 @@ class Nagios::Base self.class.name end - # object types - newtype :command do - setparameters :command_name, :command_line - end + # object types + newtype :command do + setparameters :command_name, :command_line + end - newtype :contact do + newtype :contact do setparameters :contact_name, :alias, :host_notification_period, - :host_notification_commands, :service_notification_period, - :service_notification_commands, - :email, :pager, :service_notification_options, :host_notification_options + :host_notification_commands, :service_notification_period, + :service_notification_commands, :register, :email, :pager, + :service_notification_options, :host_notification_options setsuperior "person" - end + end - newtype :contactgroup do - setparameters :contactgroup_name, :alias, :members - end + newtype :contactgroup do + setparameters :contactgroup_name, :alias, :members + end - newtype :host do + newtype :host do setparameters :host_name, :notifications_enabled, :event_handler_enabled, - :flap_detection_enabled, :process_perf_data, :retain_status_information, - :retain_nonstatus_information, :register, :use, :alias, - :address, :check_command, :max_check_attempts, :notification_interval, - :notification_period, :notification_options, :checks_enabled, - :failure_prediction_enabled, :parents + :flap_detection_enabled, :process_perf_data, :retain_status_information, + :retain_nonstatus_information, :register, :use, :alias, + :address, :check_command, :max_check_attempts, :notification_interval, + :notification_period, :notification_options, :checks_enabled, + :failure_prediction_enabled, :parents, :contact_groups setsuperior "person" - map :address => "ipHostNumber" - end - - newtype :hostextinfo do - auxiliary = true + end + newtype :hostextinfo do + auxiliary = true setparameters :host_name, :notes_url, :icon_image, :icon_image_alt, :vrml_image, - "2d_coords".intern, "3d_coords".intern + "2d_coords".intern, "3d_coords".intern setnamevar :host_name - end + end - newtype :hostgroup do + newtype :hostgroup do setparameters :hostgroup_name, :alias, :contact_groups, :members - end + end - newtype :hostgroupescalation do - auxiliary = true + newtype :hostescalation do + setparameters :name, :first_notification, :last_notification, + :notification_interval, :contact_groups, + :escalation_options, :register, :hostgroup_name + setnamevar :name + end + + newtype :hostgroupescalation do + auxiliary = true setparameters :hostgroup_name, :first_notification, :last_notification, - :contact_groups, :notification_interval + :contact_groups, :notification_interval setnamevar :hostgroup_name - end + end - newtype :service do + newtype :service do attach :host => :host_name setparameters :name, :active_checks_enabled, :passive_checks_enabled, :parallelize_check, :obsess_over_service, :check_freshness, @@ -381,41 +388,48 @@ class Nagios::Base :normal_check_interval, :retry_check_interval, :contact_groups, :notification_interval, :notification_period, :notification_options, :service_description, :host_name, :freshness_threshold, - :check_command + :check_command, :hostgroup_name, :event_handler, :servicegroups, :host suppress :host_name setnamevar :service_description - end + end - newtype :servicedependency do + newtype :servicedependency do auxiliary = true setparameters :host_name, :service_description, :dependent_host_name, - :dependent_service_description, :execution_failure_criteria, - :notification_failure_criteria - - setnamevar :host_name - end - - newtype :serviceescalation do - setparameters :host_name, :service_description, :first_notification, - :last_notification, :contact_groups, :notification_interval + :dependent_service_description, :execution_failure_criteria, + :notification_failure_criteria, :hostgroup_name, + :dependent_hostgroup_name setnamevar :host_name end - newtype :serviceextinfo do + newtype :serviceescalation do + setparameters :host_name, :service_description, :first_notification, + :last_notification, :contact_groups, :notification_interval, :hostgroup_name + + setnamevar :host_name + end + + newtype :servicegroup do + setparameters :servicegroup_name, :alias + + setnamevar :servicegroup_name + end + + newtype :serviceextinfo do auxiliary = true setparameters :host_name, :service_description, :icon_image, :icon_image_alt setnamevar :host_name - end + end - newtype :timeperiod do - setparameters :timeperiod_name, :alias, :sunday, :monday, :tuesday, :wednesday, - :thursday, :friday, :saturday - end + newtype :timeperiod do + setparameters :timeperiod_name, :alias, :sunday, :monday, :tuesday, + :wednesday, :thursday, :friday, :saturday + end end # $Id$ From aa830b9b34f4d7eb39f5de9cca7ba848f851db2d Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Sun, 30 Mar 2008 12:38:24 -0500 Subject: [PATCH 06/48] Adding 0.24.4 header to the changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index eb223d145..f4850731e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +0.24.4 Pass source to pkg_add via the PKG_PATH environment variable if it ends in a '/' indicating it is a directory. Allows pkg_add to resolve dependancies, and make it possible to specify packages From 4f8df9803a9ad2f31d3e86418732a230b5cee3f3 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Mon, 31 Mar 2008 09:10:10 +1100 Subject: [PATCH 07/48] Added updated fix for #1020 --- lib/puppet/network/xmlrpc/client.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/puppet/network/xmlrpc/client.rb b/lib/puppet/network/xmlrpc/client.rb index f6a5e8db6..b55511871 100644 --- a/lib/puppet/network/xmlrpc/client.rb +++ b/lib/puppet/network/xmlrpc/client.rb @@ -48,7 +48,11 @@ module Puppet::Network Puppet.warning "Transient SSL write error; restarting connection and retrying" self.recycle_connection retry - end + elsif detail.message =~ /certificate verify failed/ || /hostname was not match/ || /hostname not match/ + Puppet.warning "Certificate verification failed; consider using the certname configuration option" + self.recycle_connection + retry + end raise XMLRPCClientError, "Certificates were not trusted: %s" % detail rescue ::XMLRPC::FaultException => detail From eae5cee12df708de5cebfbed2fff42a1890070f5 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 31 Mar 2008 11:48:14 -0500 Subject: [PATCH 08/48] Fixing a duplicate word in the mount docs --- lib/puppet/type/mount.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb index ca8e27e17..e18630cc8 100755 --- a/lib/puppet/type/mount.rb +++ b/lib/puppet/type/mount.rb @@ -95,7 +95,7 @@ module Puppet # Solaris specifies two devices, not just one. newproperty(:blockdevice) do - desc "The the device to fsck. This is property is only valid + desc "The device to fsck. This is property is only valid on Solaris, and in most cases will default to the correct value." From 2cac600de8496b39e698c2363e1faa26ab3705da Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Tue, 1 Apr 2008 07:39:49 +1100 Subject: [PATCH 09/48] Fixed duplicate oid for parentnode and environment in schema - addresses #1170 --- ext/ldap/puppet.schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ldap/puppet.schema b/ext/ldap/puppet.schema index bbad23eab..d8dc4260d 100644 --- a/ext/ldap/puppet.schema +++ b/ext/ldap/puppet.schema @@ -12,7 +12,7 @@ attributetype ( 1.1.3.9 NAME 'parentnode' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) -attributetype ( 1.1.3.9 NAME 'environment' +attributetype ( 1.1.3.11 NAME 'environment' DESC 'Puppet Node Environment' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) From 4a39d64180377d12d994110cd38b350282863892 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Tue, 1 Apr 2008 09:58:36 +1100 Subject: [PATCH 10/48] Revert "Added updated fix for #1020" This reverts commit 4f8df9803a9ad2f31d3e86418732a230b5cee3f3. --- lib/puppet/network/xmlrpc/client.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/puppet/network/xmlrpc/client.rb b/lib/puppet/network/xmlrpc/client.rb index b55511871..f6a5e8db6 100644 --- a/lib/puppet/network/xmlrpc/client.rb +++ b/lib/puppet/network/xmlrpc/client.rb @@ -48,11 +48,7 @@ module Puppet::Network Puppet.warning "Transient SSL write error; restarting connection and retrying" self.recycle_connection retry - elsif detail.message =~ /certificate verify failed/ || /hostname was not match/ || /hostname not match/ - Puppet.warning "Certificate verification failed; consider using the certname configuration option" - self.recycle_connection - retry - end + end raise XMLRPCClientError, "Certificates were not trusted: %s" % detail rescue ::XMLRPC::FaultException => detail From e51d05c9aa86db2911a68622fcf983e543576f07 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Tue, 1 Apr 2008 10:34:29 +1100 Subject: [PATCH 11/48] Better fix for #1020 --- lib/puppet/network/xmlrpc/client.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/puppet/network/xmlrpc/client.rb b/lib/puppet/network/xmlrpc/client.rb index f6a5e8db6..357a766a1 100644 --- a/lib/puppet/network/xmlrpc/client.rb +++ b/lib/puppet/network/xmlrpc/client.rb @@ -49,6 +49,11 @@ module Puppet::Network self.recycle_connection retry end + ["certificate verify failed", "hostname was not match", "hostname not match"].each do |str| + if detail.message.include?(str) + Puppet.warning "Certificate validation failed; considering using the certname configuration option" + end + end raise XMLRPCClientError, "Certificates were not trusted: %s" % detail rescue ::XMLRPC::FaultException => detail From f927b97f9ba454dab23255a259642b6b5dc1aae9 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Tue, 1 Apr 2008 10:40:25 +1100 Subject: [PATCH 12/48] Updates to rrdgraph documentation --- lib/puppet/reports/rrdgraph.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb index 2611f0369..03d8a5bdd 100644 --- a/lib/puppet/reports/rrdgraph.rb +++ b/lib/puppet/reports/rrdgraph.rb @@ -1,8 +1,10 @@ Puppet::Reports.register_report(:rrdgraph) do desc "Graph all available data about hosts using the RRD library. You must have the Ruby RRDtool library installed to use this report, which - you can get from `the RubyRRDTool RubyForge page`_. This package requires - the binary rrdtool2 package to be installed. + you can get from `the RubyRRDTool RubyForge page`_. This package may also + be available as ``ruby-rrd`` or ``rrdtool-ruby`` in your distribution's package + management system. The library and/or package will both require the binary + ``rrdtool`` package from your distribution to be installed. .. _the RubyRRDTool RubyForge page: http://rubyforge.org/projects/rubyrrdtool/ From 6f32e95ce25fb6de40836cb7c7678f39c5e77879 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 31 Mar 2008 21:02:52 -0500 Subject: [PATCH 13/48] Adding the report reference back; I don't really know why I removed it, since the information in it isn't anywhere else. --- lib/puppet/reference/report.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/puppet/reference/report.rb diff --git a/lib/puppet/reference/report.rb b/lib/puppet/reference/report.rb new file mode 100644 index 000000000..be8e64751 --- /dev/null +++ b/lib/puppet/reference/report.rb @@ -0,0 +1,23 @@ +require 'puppet/reports' + +report = Puppet::Util::Reference.newreference :report, :doc => "All available transaction reports" do + Puppet::Reports.reportdocs +end + +report.header = " +Puppet clients can report back to the server after each transaction. This +transaction report is sent as a YAML dump of the +``Puppet::Transaction::Report`` class and includes every log message that was +generated during the transaction along with as many metrics as Puppet knows how +to collect. See `ReportsAndReporting Reports and Reporting`:trac: +for more information on how to use reports. + +Currently, clients default to not sending in reports; you can enable reporting +by setting the ``report`` parameter to true. + +To use a report, set the ``reports`` parameter on the server; multiple +reports must be comma-separated. You can also specify ``none`` to disable +reports entirely. + +Puppet provides multiple report handlers that will process client reports: +" From 335972edb68a532a61392ace006b0401e3cca810 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Wed, 2 Apr 2008 11:40:41 +1100 Subject: [PATCH 14/48] Pushed patch to fix #1174 --- conf/redhat/client.init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/redhat/client.init b/conf/redhat/client.init index b63729aa3..b77bd017a 100644 --- a/conf/redhat/client.init +++ b/conf/redhat/client.init @@ -15,7 +15,7 @@ export PATH [ -f /etc/sysconfig/puppet ] && . /etc/sysconfig/puppet lockfile=${LOCKFILE-/var/lock/subsys/puppet} -pidfile=${PIDFILE-/var/run/puppet/puppet.pid} +pidfile=${PIDFILE-/var/run/puppet/puppetd.pid} puppetd=${PUPPETD-/usr/sbin/puppetd} RETVAL=0 From 1c02749e8b54616043f728ed18ddec1dc4353a2d Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 3 Apr 2008 19:30:30 +1100 Subject: [PATCH 15/48] Committed patch from #1160 --- ext/emacs/puppet-mode.el | 165 +++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 76 deletions(-) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index f331474a9..4a91defff 100644 --- a/ext/emacs/puppet-mode.el +++ b/ext/emacs/puppet-mode.el @@ -5,56 +5,48 @@ ;;; Description: A simple mode for editing puppet manifests ;;; -(defconst puppet-mode-version "0.0.1") +(defconst puppet-mode-version "0.1") (defvar puppet-mode-abbrev-table nil "Abbrev table in use in puppet-mode buffers.") (define-abbrev-table 'puppet-mode-abbrev-table ()) -(defvar puppet-mode-map nil "Keymap used in puppet mode.") +(defcustom puppet-indent-level 2 + "*Indentation of Puppet statements." + :type 'integer :group 'puppet) -(if puppet-mode-map - nil - (setq puppet-mode-map (make-sparse-keymap)) -;; (define-key puppet-mode-map "{" 'puppet-electric-brace) -;; (define-key puppet-mode-map "}" 'puppet-electric-brace) -;; (define-key puppet-mode-map "\e\C-a" 'puppet-beginning-of-defun) -;; (define-key puppet-mode-map "\e\C-e" 'puppet-end-of-defun) -;; (define-key puppet-mode-map "\e\C-b" 'puppet-backward-sexp) -;; (define-key puppet-mode-map "\e\C-f" 'puppet-forward-sexp) -;; (define-key puppet-mode-map "\e\C-p" 'puppet-beginning-of-block) -;; (define-key puppet-mode-map "\e\C-n" 'puppet-end-of-block) -;; (define-key puppet-mode-map "\e\C-h" 'puppet-mark-defun) -;; (define-key puppet-mode-map "\e\C-q" 'puppet-indent-exp) -;; (define-key puppet-mode-map "\t" 'puppet-indent-command) -;; (define-key puppet-mode-map "\C-c\C-e" 'puppet-insert-end) -;; (define-key puppet-mode-map "\C-j" 'puppet-reindent-then-newline-and-indent) - (define-key puppet-mode-map "\C-m" 'newline)) +(defcustom puppet-include-indent 2 + "*Indentation of continued Puppet include statements." + :type 'integer :group 'puppet) + +(defvar puppet-mode-map + "Key map used in puppet-mode buffers." + (let ((map (make-sparse-keymap))) + (define-key map "\C-j" 'newline-and-indent) + (define-key map "\C-m" 'newline-and-indent) + map)) (defvar puppet-mode-syntax-table nil - "Syntax table in use in puppet-mode buffers.") - -(if puppet-mode-syntax-table - () - (setq puppet-mode-syntax-table (make-syntax-table)) - (modify-syntax-entry ?\' "\"" puppet-mode-syntax-table) - (modify-syntax-entry ?\" "\"" puppet-mode-syntax-table) - (modify-syntax-entry ?# "<" puppet-mode-syntax-table) - (modify-syntax-entry ?\n ">" puppet-mode-syntax-table) - (modify-syntax-entry ?\\ "\\" puppet-mode-syntax-table) - (modify-syntax-entry ?$ "." puppet-mode-syntax-table) - (modify-syntax-entry ?- "_" puppet-mode-syntax-table) - (modify-syntax-entry ?> "." puppet-mode-syntax-table) - (modify-syntax-entry ?= "." puppet-mode-syntax-table) - (modify-syntax-entry ?\; "." puppet-mode-syntax-table) - (modify-syntax-entry ?\( "()" puppet-mode-syntax-table) - (modify-syntax-entry ?\) ")(" puppet-mode-syntax-table) - (modify-syntax-entry ?\{ "(}" puppet-mode-syntax-table) - (modify-syntax-entry ?\} "){" puppet-mode-syntax-table) - (modify-syntax-entry ?\[ "(]" puppet-mode-syntax-table) - (modify-syntax-entry ?\] ")[" puppet-mode-syntax-table) - ) + "Syntax table in use in puppet-mode buffers." + (let ((table (make-syntax-table))) + (modify-syntax-entry ?\' "\"" table) + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?# "<" table) + (modify-syntax-entry ?\n ">" table) + (modify-syntax-entry ?\\ "\\" table) + (modify-syntax-entry ?$ "." table) + (modify-syntax-entry ?- "_" table) + (modify-syntax-entry ?> "." table) + (modify-syntax-entry ?= "." table) + (modify-syntax-entry ?\; "." table) + (modify-syntax-entry ?\( "()" table) + (modify-syntax-entry ?\) ")(" table) + (modify-syntax-entry ?\{ "(}" table) + (modify-syntax-entry ?\} "){" table) + (modify-syntax-entry ?\[ "(]" table) + (modify-syntax-entry ?\] ")[" table) + table)) (defcustom puppet-indent-tabs-mode nil "*Indentation can insert tabs in puppet mode if this is non-nil." @@ -64,31 +56,6 @@ "*Indentation column of comments." :type 'integer :group 'puppet) -(defun puppet-mode-variables () - (set-syntax-table puppet-mode-syntax-table) - (setq local-abbrev-table puppet-mode-abbrev-table) - ;(make-local-variable 'indent-line-function) - ;(setq indent-line-function 'ruby-indent-line) - (make-local-variable 'require-final-newline) - (setq require-final-newline t) - (make-variable-buffer-local 'comment-start) - (setq comment-start "# ") - (make-variable-buffer-local 'comment-end) - (setq comment-end "") - (make-variable-buffer-local 'comment-column) - (setq comment-column puppet-comment-column) - (make-variable-buffer-local 'comment-start-skip) - (setq comment-start-skip "#+ *") - (setq indent-tabs-mode puppet-indent-tabs-mode) - (make-local-variable 'parse-sexp-ignore-comments) - (setq parse-sexp-ignore-comments t) - (make-local-variable 'paragraph-start) - (setq paragraph-start (concat "$\\|" page-delimiter)) - (make-local-variable 'paragraph-separate) - (setq paragraph-separate paragraph-start) - (make-local-variable 'paragraph-ignore-fill-prefix) - (setq paragraph-ignore-fill-prefix t)) - (defun puppet-comment-line-p () "Return non-nil iff this line is a comment." (save-excursion @@ -113,6 +80,27 @@ that array, else return nil." (if (= (count-matches "\\]" apoint opoint) 0) apoint)))))) +(defun puppet-in-include () + "If point is in a continued list of include statements, return the position +of the initial include plus puppet-include-indent." + (save-excursion + (save-match-data + (let ((include-column nil) + (not-found t)) + (while not-found + (forward-line -1) + (cond + ((puppet-comment-line-p) + (if (bobp) + (setq not-found nil))) + ((looking-at "^\\s-*include\\s-+.*,\\s-*$") + (setq include-column + (+ (current-indentation) puppet-include-indent)) + (setq not-found nil)) + ((not (looking-at ".*,\\s-*$")) + (setq not-found nil)))) + include-column)))) + (defun puppet-indent-line () "Indent current line as puppet code." (interactive) @@ -121,6 +109,7 @@ that array, else return nil." (indent-line-to 0) ; First line is always non-indented (let ((not-indented t) (array-start (puppet-in-array)) + (include-start (puppet-in-include)) cur-indent) (cond (array-start @@ -155,6 +144,8 @@ that array, else return nil." (re-search-forward "\\S-") (forward-char -1) (setq cur-indent (current-column)))) + (include-start + (setq cur-indent include-start)) ((looking-at "^[^{\n]*}") ;; This line contains the end of a block, but the block does ;; not also begin on this line, so decrease the indentation. @@ -162,9 +153,9 @@ that array, else return nil." (forward-line -1) (if (looking-at "^.*}") (progn - (setq cur-indent (- (current-indentation) 2)) + (setq cur-indent (- (current-indentation) puppet-indent-level)) (setq not-indented nil)) - (setq cur-indent (- (current-indentation) 2)))) + (setq cur-indent (- (current-indentation) puppet-indent-level)))) (if (< cur-indent 0) ; We can't indent past the left margin (setq cur-indent 0))) (t @@ -183,7 +174,13 @@ that array, else return nil." (setq cur-indent (current-indentation)) (setq not-indented nil)) ((looking-at "^.*{") ; indent an extra level - (setq cur-indent (+ (current-indentation) 2)) + (setq cur-indent (+ (current-indentation) puppet-indent-level)) + (setq not-indented nil)) + ((looking-at "^.*;\\s-*$") ; Semicolon ends a nested resource + (setq cur-indent (- (current-indentation) puppet-indent-level)) + (setq not-indented nil)) + ((looking-at "^.*:\\s-*$") ; indent an extra level after : + (setq cur-indent (+ (current-indentation) puppet-indent-level)) (setq not-indented nil)) ((bobp) (setq not-indented nil)) @@ -204,13 +201,20 @@ The variable puppet-indent-level controls the amount of indentation. (use-local-map puppet-mode-map) (setq mode-name "Puppet") (setq major-mode 'puppet-mode) - (puppet-mode-variables) - ;; Register our indentation function - (set (make-local-variable 'indent-line-function) 'puppet-indent-line) + (set-syntax-table puppet-mode-syntax-table) + (set (make-local-variable 'local-abbrev-table) puppet-mode-abbrev-table) + (set (make-local-variable 'comment-start) "# ") + (set (make-local-variable 'comment-start-skip) "#+ *") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-column) puppet-comment-column) + (set (make-local-variable 'indent-line-function) 'puppet-indent-line) + (set (make-local-variable 'indent-tabs-mode) puppet-indent-tabs-mode) + (set (make-local-variable 'require-final-newline) t) + (set (make-local-variable 'paragraph-ignore-fill-prefix) t) + (set (make-local-variable 'paragraph-start) "\f\\|[ ]*$") + (set (make-local-variable 'paragraph-separate) "[ \f]*$") (run-hooks 'puppet-mode-hook)) - - (cond ((featurep 'font-lock) (or (boundp 'font-lock-variable-name-face) @@ -253,8 +257,13 @@ The variable puppet-indent-level controls the amount of indentation. ;; defines '("^\\s *\\(define\\|node\\|class\\)\\s +\\([^( \t\n]+\\)" 2 font-lock-function-name-face) + '("\\s +inherits\\s +\\([^( \t\n]+\\)" + 1 font-lock-function-name-face) ;; include - '("^\\s *include\\s +\\([^( \t\n]+\\)" + '("^\\s *include\\s +\\([^( \t\n,]+\\)" + 1 font-lock-reference-face) + ;; hack to catch continued includes + '("^\\s *\\([a-zA-Z0-9:_-]+\\),?\\s *$" 1 font-lock-reference-face) ;; keywords (cons (concat @@ -270,6 +279,7 @@ The variable puppet-indent-level controls the amount of indentation. "include" "inherits" "node" + "realize" "true" ) "\\|") @@ -284,7 +294,10 @@ The variable puppet-indent-level controls the amount of indentation. '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+" 0 font-lock-variable-name-face) ;; usage of types - '("^\\s +\\([a-zA-Z-]+\\)\\s +{" + '("^\\s +\\([a-zA-Z_-]+\\)\\s +{" + 1 font-lock-type-face) + ;; overrides + '("^\\s +\\([a-zA-Z_-]+\\)\\[" 1 font-lock-type-face) ;; general delimited string '("\\(^\\|[[ \t\n<+(,=]\\)\\(%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\3\\)\\)" From 2036d22cfa950ed84957427b98f69c1719b64dd7 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 3 Apr 2008 20:59:51 +1100 Subject: [PATCH 16/48] Fixes debian service enabled/disable issue as detailed in #1161. --- lib/puppet/provider/service/debian.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/provider/service/debian.rb b/lib/puppet/provider/service/debian.rb index f0f6fe1ef..da38c10a2 100755 --- a/lib/puppet/provider/service/debian.rb +++ b/lib/puppet/provider/service/debian.rb @@ -22,7 +22,7 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do # If it's enabled, then it will print output showing removal of # links. - if output =~ /etc\/rc[\dS].d|not installed/ + if output =~ /etc\/rc[\dS].d\/S|not installed/ return :true else return :false From e621985d24fbd417690f763276d7d895c0640042 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 4 Apr 2008 01:16:36 +1100 Subject: [PATCH 17/48] Changed some non-standard Ruby locations to env ruby shebangs --- examples/root/bin/sleeper | 2 +- lib/puppet/external/nagios.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/root/bin/sleeper b/examples/root/bin/sleeper index c22d13f40..980d66ac1 100755 --- a/examples/root/bin/sleeper +++ b/examples/root/bin/sleeper @@ -1,4 +1,4 @@ -#!/usr/bin/ruby -w +#!/usr/bin/env ruby -w ### # sleep indefinitely as a debug diff --git a/lib/puppet/external/nagios.rb b/lib/puppet/external/nagios.rb index 78459fcb6..0dcae4c6d 100755 --- a/lib/puppet/external/nagios.rb +++ b/lib/puppet/external/nagios.rb @@ -1,4 +1,4 @@ -#!/usr/local/bin/ruby -w +#!/usr/bin/env ruby -w #-------------------- # A script to retrieve hosts from ldap and create an importable From 6f1c46955a49647c14ede85304c25c7137dd9ab6 Mon Sep 17 00:00:00 2001 From: Andreas Rogge Date: Wed, 2 Apr 2008 19:55:25 +0200 Subject: [PATCH 18/48] Extend workaround from 56aad69f8cdf8b0b08fdb7985014986223fa4455 to not only fix UIDs but also GIDs Fixes #1169 Fixes #804 (workaround was probably incomplete, as required changes in lib/puppet/util/posix.rb were missing) Signed-off-by: Andreas Rogge --- lib/puppet/type/file/group.rb | 20 +++++++++++++------- lib/puppet/util/posix.rb | 4 ++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/puppet/type/file/group.rb b/lib/puppet/type/file/group.rb index 5f7caf342..cc482ff31 100755 --- a/lib/puppet/type/file/group.rb +++ b/lib/puppet/type/file/group.rb @@ -11,12 +11,8 @@ module Puppet end def id2name(id) - if id > 70000 - return nil - end - if id.is_a?(Symbol) - return id.to_s - end + return id.to_s if id.is_a?(Symbol) + return nil if id > Puppet[:maximum_uid].to_i begin group = Etc.getgrgid(id) rescue ArgumentError @@ -73,7 +69,17 @@ module Puppet @method = :chown end - return stat.gid + currentvalue = stat.gid + + # On OS X, files that are owned by -2 get returned as really + # large GIDs instead of negative ones. This isn't a Ruby bug, + # it's an OS X bug, since it shows up in perl, too. + if currentvalue > Puppet[:maximum_uid].to_i + self.warning "Apparently using negative GID (%s) on a platform that does not consistently handle them" % currentvalue + currentvalue = :silly + end + + return currentvalue end # Determine if the group is valid, and if so, return the GID diff --git a/lib/puppet/util/posix.rb b/lib/puppet/util/posix.rb index c518a8797..cc0340ef7 100755 --- a/lib/puppet/util/posix.rb +++ b/lib/puppet/util/posix.rb @@ -13,7 +13,7 @@ module Puppet::Util::POSIX end prefix = "get" + space.to_s if id.is_a?(Integer) - if id > 1000000 + if id > Puppet[:maximum_uid].to_i Puppet.err "Tried to get %s field for silly id %s" % [field, id] return nil end @@ -40,7 +40,7 @@ module Puppet::Util::POSIX end if id.is_a?(Integer) integer = true - if id > 1000000 + if id > Puppet[:maximum_uid].to_i Puppet.err "Tried to get %s field for silly id %s" % [field, id] return nil end From bb65226e54d5e476581118e7c2c5e42275a6b290 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 4 Apr 2008 11:51:23 +1100 Subject: [PATCH 19/48] Additional fix to emacs for ticket #1160 --- bin/puppetrun | 2 +- ext/emacs/puppet-mode.el | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/puppetrun b/bin/puppetrun index d0823b9c5..14bc510ed 100755 --- a/bin/puppetrun +++ b/bin/puppetrun @@ -177,7 +177,7 @@ end def setupldap begin - @ldap = Puppet::Parser::Interpreter.ldap() + @ldap = Puppet::Parser::Interpreter.new rescue => detail $stderr.puts "Could not connect to LDAP: %s" % detail exit(34) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index 4a91defff..0a7ee1a5f 100644 --- a/ext/emacs/puppet-mode.el +++ b/ext/emacs/puppet-mode.el @@ -21,14 +21,13 @@ :type 'integer :group 'puppet) (defvar puppet-mode-map - "Key map used in puppet-mode buffers." (let ((map (make-sparse-keymap))) (define-key map "\C-j" 'newline-and-indent) (define-key map "\C-m" 'newline-and-indent) - map)) + map) + "Key map used in puppet-mode buffers.") -(defvar puppet-mode-syntax-table nil - "Syntax table in use in puppet-mode buffers." +(defvar puppet-mode-syntax-table (let ((table (make-syntax-table))) (modify-syntax-entry ?\' "\"" table) (modify-syntax-entry ?\" "\"" table) @@ -46,7 +45,8 @@ (modify-syntax-entry ?\} "){" table) (modify-syntax-entry ?\[ "(]" table) (modify-syntax-entry ?\] ")[" table) - table)) + table) + "Syntax table in use in puppet-mode buffers.") (defcustom puppet-indent-tabs-mode nil "*Indentation can insert tabs in puppet mode if this is non-nil." From c13486e3a5a604152faef6f467ab6eb4f6e30acd Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 4 Apr 2008 11:52:08 +1100 Subject: [PATCH 20/48] Revert "Additional fix to emacs for ticket #1160" This reverts commit bb65226e54d5e476581118e7c2c5e42275a6b290. --- bin/puppetrun | 2 +- ext/emacs/puppet-mode.el | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/puppetrun b/bin/puppetrun index 14bc510ed..d0823b9c5 100755 --- a/bin/puppetrun +++ b/bin/puppetrun @@ -177,7 +177,7 @@ end def setupldap begin - @ldap = Puppet::Parser::Interpreter.new + @ldap = Puppet::Parser::Interpreter.ldap() rescue => detail $stderr.puts "Could not connect to LDAP: %s" % detail exit(34) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index 0a7ee1a5f..4a91defff 100644 --- a/ext/emacs/puppet-mode.el +++ b/ext/emacs/puppet-mode.el @@ -21,13 +21,14 @@ :type 'integer :group 'puppet) (defvar puppet-mode-map + "Key map used in puppet-mode buffers." (let ((map (make-sparse-keymap))) (define-key map "\C-j" 'newline-and-indent) (define-key map "\C-m" 'newline-and-indent) - map) - "Key map used in puppet-mode buffers.") + map)) -(defvar puppet-mode-syntax-table +(defvar puppet-mode-syntax-table nil + "Syntax table in use in puppet-mode buffers." (let ((table (make-syntax-table))) (modify-syntax-entry ?\' "\"" table) (modify-syntax-entry ?\" "\"" table) @@ -45,8 +46,7 @@ (modify-syntax-entry ?\} "){" table) (modify-syntax-entry ?\[ "(]" table) (modify-syntax-entry ?\] ")[" table) - table) - "Syntax table in use in puppet-mode buffers.") + table)) (defcustom puppet-indent-tabs-mode nil "*Indentation can insert tabs in puppet mode if this is non-nil." From 911c7fb4735e99cb5dc33ec7c2cb1deb166478b2 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 4 Apr 2008 11:53:18 +1100 Subject: [PATCH 21/48] Additional fix for emacs syntax for ticket #1160 --- ext/emacs/puppet-mode.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index 4a91defff..0a7ee1a5f 100644 --- a/ext/emacs/puppet-mode.el +++ b/ext/emacs/puppet-mode.el @@ -21,14 +21,13 @@ :type 'integer :group 'puppet) (defvar puppet-mode-map - "Key map used in puppet-mode buffers." (let ((map (make-sparse-keymap))) (define-key map "\C-j" 'newline-and-indent) (define-key map "\C-m" 'newline-and-indent) - map)) + map) + "Key map used in puppet-mode buffers.") -(defvar puppet-mode-syntax-table nil - "Syntax table in use in puppet-mode buffers." +(defvar puppet-mode-syntax-table (let ((table (make-syntax-table))) (modify-syntax-entry ?\' "\"" table) (modify-syntax-entry ?\" "\"" table) @@ -46,7 +45,8 @@ (modify-syntax-entry ?\} "){" table) (modify-syntax-entry ?\[ "(]" table) (modify-syntax-entry ?\] ")[" table) - table)) + table) + "Syntax table in use in puppet-mode buffers.") (defcustom puppet-indent-tabs-mode nil "*Indentation can insert tabs in puppet mode if this is non-nil." From 7295626c25ab878aaaf6ec577a42f91c0f18fdbc Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Mon, 7 Apr 2008 12:44:31 -0700 Subject: [PATCH 22/48] Used stubs to decouple our code behavior from the behavior of the underlying filesystem, as well as removing the need to sleep (which caused the tests to take a long time). --- spec/unit/util/loadedfile.rb | 42 ++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/spec/unit/util/loadedfile.rb b/spec/unit/util/loadedfile.rb index b57475f96..083120e20 100644 --- a/spec/unit/util/loadedfile.rb +++ b/spec/unit/util/loadedfile.rb @@ -6,42 +6,51 @@ require 'tempfile' require 'puppet/util/loadedfile' describe Puppet::Util::LoadedFile do - before(:all) do - # First, save and adjust the timeout so tests don't take forever. - @saved_filetimeout = Puppet[:filetimeout] - Puppet[:filetimeout] = 5 - end - before(:each) do @f = Tempfile.new('loadedfile_test') @f.puts "yayness" @f.flush + @loaded = Puppet::Util::LoadedFile.new(@f.path) + + fake_ctime = Time.now - (2 * Puppet[:filetimeout]) + @stat = stub('stat', :ctime => fake_ctime) + @fake_now = Time.now + (2 * Puppet[:filetimeout]) end it "should recognize when the file has not changed" do - sleep(Puppet[:filetimeout]) + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) @loaded.changed?.should == false end it "should recognize when the file has changed" do - @f.puts "booness" - @f.flush - sleep(Puppet[:filetimeout]) + # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() + # just didn't do the job. + File.stubs(:stat).returns(@stat) + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) @loaded.changed?.should be_an_instance_of(Time) end it "should not catch a change until the timeout has elapsed" do - @f.puts "yay" - @f.flush + # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() + # just didn't do the job. + File.stubs(:stat).returns(@stat) @loaded.changed?.should be(false) - sleep(Puppet[:filetimeout]) + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) @loaded.changed?.should_not be(false) end it "should consider a file changed when that file is missing" do @f.close! - sleep(Puppet[:filetimeout]) + # Use fake "now" so that we can be sure changed? actually checks, without sleeping + # for Puppet[:filetimeout] seconds. + Time.stubs(:now).returns(@fake_now) @loaded.changed?.should_not be(false) end @@ -53,9 +62,4 @@ describe Puppet::Util::LoadedFile do after(:each) do @f.close end - - after(:all) do - # Restore the saved timeout. - Puppet[:filetimeout] = @saved_filetimeout - end end From 92765ea4970172c7b96a2911f2ef435e4fbdc459 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 10:21:30 -0500 Subject: [PATCH 23/48] Making a test executable --- spec/unit/util/loadedfile.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 spec/unit/util/loadedfile.rb diff --git a/spec/unit/util/loadedfile.rb b/spec/unit/util/loadedfile.rb old mode 100644 new mode 100755 From 80f8b80eacfa80bfd7b7d4f348838592161ed97a Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 11:19:15 -0500 Subject: [PATCH 24/48] Adding validation to the user type to confirm that the group list does not contain any commas. This seems to be a common problem. --- lib/puppet/type/user.rb | 3 +++ test/ral/type/user.rb | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index 48e35f2c8..71507d172 100755 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -220,6 +220,9 @@ module Puppet if value =~ /^\d+$/ raise ArgumentError, "Group names must be provided, not numbers" end + if value.include?(",") + raise ArgumentError, "Group names must be provided as an array, not a comma-separated list" + end end end diff --git a/test/ral/type/user.rb b/test/ral/type/user.rb index b280acfed..87d5b6075 100755 --- a/test/ral/type/user.rb +++ b/test/ral/type/user.rb @@ -362,6 +362,12 @@ class TestUser < Test::Unit::TestCase user.delete(:groups) end + def test_groups_list_must_not_contain_commas + assert_raise(Puppet::Error) do + Puppet::Type.type(:user).create :name => "luke", :groups => "root,adm" + end + end + def test_autorequire file = tempfile() comp = nil From bd858dff513c36f63bcadc13116558fc151c04be Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 11:22:03 -0500 Subject: [PATCH 25/48] Changing the default environment to production. --- CHANGELOG | 2 ++ lib/puppet/defaults.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index f4850731e..44299970b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ + Changing the default environment to production. + 0.24.4 Pass source to pkg_add via the PKG_PATH environment variable if it ends in a '/' indicating it is a directory. Allows pkg_add diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 0f01c2ee2..eed1a00f3 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -128,7 +128,7 @@ module Puppet This is more useful as a server-side setting than client, but any environment chosen must be in this list. Values should be separated by a comma."], - :environment => {:default => "development", :desc => "The environment Puppet is running in. For clients + :environment => {:default => "production", :desc => "The environment Puppet is running in. For clients (e.g., ``puppetd``) this determines the environment itself, which is used to find modules and much more. For servers (i.e., ``puppetmasterd``) this provides the default environment for nodes From 1458123550140c6e45982c139daeeca80d0afd22 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 7 Apr 2008 23:39:21 -0500 Subject: [PATCH 26/48] Adding an envelope module to handle indirected instance expiration. --- lib/puppet/indirector/envelope.rb | 13 +++++++++ spec/unit/indirector/envelope.rb | 47 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 lib/puppet/indirector/envelope.rb create mode 100755 spec/unit/indirector/envelope.rb diff --git a/lib/puppet/indirector/envelope.rb b/lib/puppet/indirector/envelope.rb new file mode 100644 index 000000000..ef7952ef6 --- /dev/null +++ b/lib/puppet/indirector/envelope.rb @@ -0,0 +1,13 @@ +require 'puppet/indirector' + +# Provide any attributes or functionality needed for indirected +# instances. +module Puppet::Indirector::Envelope + attr_accessor :expiration + + def expired? + return false unless expiration + return false if expiration >= Time.now + return true + end +end diff --git a/spec/unit/indirector/envelope.rb b/spec/unit/indirector/envelope.rb new file mode 100755 index 000000000..17c62023a --- /dev/null +++ b/spec/unit/indirector/envelope.rb @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/envelope' + +describe Puppet::Indirector::Envelope do + before do + @instance = Object.new + @instance.extend(Puppet::Indirector::Envelope) + end + + it "should have an expiration accessor" do + @instance.expiration = "testing" + @instance.expiration.should == "testing" + end + + it "should have an expiration setter" do + @instance.should respond_to(:expiration=) + end + + it "should have a means of testing whether it is expired" do + @instance.should respond_to(:expired?) + end + + describe "when testing if it is expired" do + it "should return false if there is no expiration set" do + @instance.should_not be_expired + end + + it "should return true if the current date is after the expiration date" do + @instance.expiration = Time.now - 10 + @instance.should be_expired + end + + it "should return false if the current date is prior to the expiration date" do + @instance.expiration = Time.now + 10 + @instance.should_not be_expired + end + + it "should return false if the current date is equal to the expiration date" do + now = Time.now + Time.stubs(:now).returns(now) + @instance.expiration = now + @instance.should_not be_expired + end + end +end From 8e1e06ff1c65bc5d8162fce41776e31b9f00d1bf Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 7 Apr 2008 23:39:51 -0500 Subject: [PATCH 27/48] Removing unused code from the file_serving/metadata class. --- lib/puppet/file_serving/metadata.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb index 56712122c..beecaef48 100644 --- a/lib/puppet/file_serving/metadata.rb +++ b/lib/puppet/file_serving/metadata.rb @@ -11,15 +11,6 @@ require 'puppet/file_serving/indirection_hooks' # A class that handles retrieving file metadata. class Puppet::FileServing::Metadata < Puppet::FileServing::FileBase - module MetadataHelper - include Puppet::FileServing::IndirectionHooks - - def post_find(instance) - end - - def post_search(key, options = {}) - end - end include Puppet::Util::Checksums From c6729d134b6b0479e091928ee68abb9008d0f71d Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 7 Apr 2008 23:47:01 -0500 Subject: [PATCH 28/48] Reworking the caching layer to use TTLs instead of versions based on timestamps. This just modifies the indirection class itself, there is still some work to do to remove version code from other classes. --- lib/puppet/indirector/indirection.rb | 96 ++- spec/unit/indirector/indirection.rb | 1065 ++++++++++++++------------ 2 files changed, 631 insertions(+), 530 deletions(-) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index d47433c60..91cc42b17 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -1,4 +1,5 @@ require 'puppet/util/docs' +require 'puppet/indirector/envelope' # The class that connects functional classes with their different collection # back-ends. Each indirection has a set of associated terminus classes, @@ -28,8 +29,7 @@ class Puppet::Indirector::Indirection # Find an indirected model by name. This is provided so that Terminus classes # can specifically hook up with the indirections they are associated with. def self.model(name) - match = @@indirections.find { |i| i.name == name } - return nil unless match + return nil unless match = @@indirections.find { |i| i.name == name } match.model end @@ -65,6 +65,25 @@ class Puppet::Indirector::Indirection @@indirections.delete(self) if @@indirections.include?(self) end + # Set the time-to-live for instances created through this indirection. + def ttl=(value) + raise ArgumentError, "Indirection TTL must be an integer" unless value.is_a?(Fixnum) + @ttl = value + end + + # Default to the runinterval for the ttl. + def ttl + unless defined?(@ttl) + @ttl = Puppet[:runinterval].to_i + end + @ttl + end + + # Calculate the expiration date for a returned instance. + def expiration + Time.now + ttl + end + # Generate the full doc string. def doc text = "" @@ -106,6 +125,12 @@ class Puppet::Indirector::Indirection end end + # Set the options that can be passed on to the terminus instances. + attr_reader :option_struct + def options=(options) + @option_struct = Struct.new(*options) + end + # Return the singleton terminus for this indirection. def terminus(terminus_name = nil) # Get the name of the terminus. @@ -161,29 +186,41 @@ class Puppet::Indirector::Indirection check_authorization(:find, terminus_name, ([key] + args)) # See if our instance is in the cache and up to date. - if cache? and cache.has_most_recent?(key, terminus(terminus_name).version(key)) - Puppet.debug "Using cached %s %s" % [self.name, key] - return cache.find(key, *args) + if cache? and cached = cache.find(key, *args) + if cached.expired? + Puppet.info "Cached %s %s expired at %s; not using" % [self.name, key, cached.expiration] + else + Puppet.debug "Using cached %s %s" % [self.name, key] + return cached + end end # Otherwise, return the result from the terminus, caching if appropriate. if result = terminus(terminus_name).find(key, *args) - result.version ||= Time.now.utc + # Include the envelope module, so we can set the expiration. + result.extend(Puppet::Indirector::Envelope) + result.expiration ||= self.expiration if cache? Puppet.info "Caching %s %s" % [self.name, key] cache.save(result, *args) end - terminus(terminus_name).post_find(result) if terminus(terminus_name).respond_to?(:post_find) - return result end + + return nil end - def destroy(*args) - check_authorization(:destroy, terminus_class, args) + def destroy(key, *args) + check_authorization(:destroy, terminus_class, ([key] + args)) - terminus.destroy(*args) + terminus.destroy(key, *args) + + if cache? and cached = cache.find(key, *args) + cache.destroy(key, *args) + end + + nil end def search(*args) @@ -191,21 +228,22 @@ class Puppet::Indirector::Indirection result = terminus.search(*args) - terminus().post_search(result) if terminus().respond_to?(:post_search) - result end # these become instance methods def save(instance, *args) - check_authorization(:save, terminus_class, ([instance] + args)) + if respond_to?(:select_terminus) + terminus_name = select_terminus(instance.name, *args) + else + terminus_name = terminus_class + end - instance.version ||= Time.now.utc - dest = cache? ? cache : terminus - return if dest.has_most_recent?(instance.name, instance.version) - Puppet.info "Caching %s %s" % [self.name, instance.name] if cache? + check_authorization(:save, terminus_name, ([instance] + args)) + + # If caching is enabled, save our document there, do cache.save(instance, *args) if cache? - terminus.save(instance, *args) + terminus(terminus_class).save(instance, *args) end def version(*args) @@ -226,6 +264,20 @@ class Puppet::Indirector::Indirection end end + # Handle a given indirected call. + def prepare_call(method, arguments) + raise ArgumentError, "Options must be a hash" unless arguments.is_a?(Hash) + + # Set any terminus options. + options = option_struct ? set_options(option_struct, arguments) : nil + + tclass = choose_terminus(options) + + check_authorization(method, tclass, options) + + return terminus(tclass), options + end + # Create a new terminus instance. def make_terminus(terminus_class) # Load our terminus class. @@ -234,4 +286,10 @@ class Puppet::Indirector::Indirection end return klass.new end + + # Create a struct instance with all of the appropriate options set + # from the provided hash. + def set_options(struct, arguments) + struct.new(struct.members.inject([]) { |array, param| arguments[param.to_sym]; array } ) + end end diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index fe456b61e..5338a4858 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -2,579 +2,622 @@ require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector' - - - -describe Puppet::Indirector::Indirection, " when initializing" do - # (LAK) I've no idea how to test this, really. - it "should store a reference to itself before it consumes its options" do - proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error - Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection) - Puppet::Indirector::Indirection.instance(:testingness).delete - end - - it "should keep a reference to the indirecting model" do - model = mock 'model' - @indirection = Puppet::Indirector::Indirection.new(model, :myind) - @indirection.model.should equal(model) - end - - it "should set the name" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) - @indirection.name.should == :myind - end - - it "should require indirections to have unique names" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError) - end - - it "should extend itself with any specified module" do - mod = Module.new - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod) - @indirection.metaclass.included_modules.should include(mod) - end - - after do - @indirection.delete if defined? @indirection - end -end +require 'puppet/indirector/indirection' describe Puppet::Indirector::Indirection do - before :each do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = stub 'terminus', :has_most_recent? => false - @indirection.stubs(:terminus).returns(@terminus) - @indirection.stubs(:terminus_class).returns(:whatever) - @instance = stub 'instance', :version => nil, :version= => nil, :name => "whatever" - @name = :mything + describe "when initializing" do + # (LAK) I've no idea how to test this, really. + it "should store a reference to itself before it consumes its options" do + proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error + Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection) + Puppet::Indirector::Indirection.instance(:testingness).delete + end + + it "should keep a reference to the indirecting model" do + model = mock 'model' + @indirection = Puppet::Indirector::Indirection.new(model, :myind) + @indirection.model.should equal(model) + end + + it "should set the name" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) + @indirection.name.should == :myind + end + + it "should require indirections to have unique names" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError) + end + + it "should extend itself with any specified module" do + mod = Module.new + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod) + @indirection.metaclass.included_modules.should include(mod) + end + + it "should allow specification of supported options" do + lambda { @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :options => [:one, :two]) }.should_not raise_error + end + + it "should define a new Struct class to support the specified options" do + struct = mock 'struct' + Struct.expects(:new).with(:one, :two) + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :options => [:one, :two]) + end + + after do + @indirection.delete if defined? @indirection + end end - - describe Puppet::Indirector::Indirection, " when looking for a model instance" do - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:find).with(@name).returns(@instance) - @indirection.find(@name).should == @instance + describe "when an instance" do + before :each do + @terminus_class = mock 'terminus_class' + @terminus = mock 'terminus' + @terminus_class.stubs(:new).returns(@terminus) + @cache = mock 'cache' + @cache_class = mock 'cache_class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) + + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @indirection.terminus_class = :test_terminus + + @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" + @name = :mything end - it "should not attempt to set a timestamp if the terminus cannot find the instance" do - @terminus.expects(:find).with(@name).returns(nil) - proc { @indirection.find(@name) }.should_not raise_error + it "should allow setting the ttl" do + @indirection.ttl = 300 + @indirection.ttl.should == 300 end - it "should pass the instance to the :post_find hook if there is one" do - class << @terminus - def post_find + it "should default to the :runinterval setting, converted to an integer, for its ttl" do + Puppet.settings.expects(:value).returns "1800" + @indirection.ttl.should == 1800 + end + + it "should calculate the current expiration by adding the TTL to the current time" do + @indirection.stubs(:ttl).returns(100) + now = Time.now + Time.stubs(:now).returns now + @indirection.expiration.should == (Time.now + 100) + end + + describe "and looking for a model instance" do + it "should let the appropriate terminus perform the lookup" do + @terminus.expects(:find).with(@name).returns(@instance) + @indirection.find(@name).should == @instance + end + + it "should return nil if nothing is returned by the terminus" do + @terminus.expects(:find).with(@name).returns(nil) + @indirection.find(@name).should be_nil + end + + it "should extend any found instance with the Envelope module" do + @terminus.stubs(:find).returns(@instance) + + @instance.expects(:extend).with(Puppet::Indirector::Envelope) + @indirection.find(@name) + end + + it "should set the expiration date on any instances without one set" do + # Otherwise, our stub method doesn't get used, so the tests fail. + @instance.stubs(:extend) + @terminus.stubs(:find).returns(@instance) + + @indirection.expects(:expiration).returns :yay + + @instance.expects(:expiration).returns(nil) + @instance.expects(:expiration=).with(:yay) + + @indirection.find(@name) + end + + it "should not override an already-set expiration date on returned instances" do + # Otherwise, our stub method doesn't get used, so the tests fail. + @instance.stubs(:extend) + @terminus.stubs(:find).returns(@instance) + + @indirection.expects(:expiration).never + + @instance.expects(:expiration).returns(:yay) + @instance.expects(:expiration=).never + + @indirection.find(@name) + end + + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + + @instance.stubs(:expired?).returns false end - end - @terminus.expects(:post_find).with(@instance) - @terminus.expects(:find).with(@name).returns(@instance) - @indirection.find(@name) - end - end - - describe Puppet::Indirector::Indirection, " when removing a model instance" do - it "should let the appropriate terminus remove the instance" do - @terminus.expects(:destroy).with(@name).returns(@instance) - @indirection.destroy(@name).should == @instance - end - end + it "should first look in the cache for an instance" do + @terminus.expects(:find).never + @cache.expects(:find).with(@name).returns @instance - describe Puppet::Indirector::Indirection, " when searching for multiple model instances" do - - it "should let the appropriate terminus find the matching instances" do - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name).should == @instance - end - - it "should pass the instances to the :post_search hook if there is one" do - class << @terminus - def post_search + @indirection.find(@name) end - end - @terminus.expects(:post_search).with(@instance) - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name) - end - end - describe Puppet::Indirector::Indirection, " when storing a model instance" do + it "should return the cached object if it is not expired" do + @instance.stubs(:expired?).returns false - it "should let the appropriate terminus store the instance" do - @terminus.expects(:save).with(@instance).returns(@instance) - @indirection.save(@instance).should == @instance - end - end - - describe Puppet::Indirector::Indirection, " when handling instance versions" do + @cache.stubs(:find).returns @instance + @indirection.find(@name).should equal(@instance) + end - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:version).with(@name).returns(5) - @indirection.version(@name).should == 5 - end + it "should send a debug log if it is using the cached object" do + Puppet.expects(:debug) + @cache.stubs(:find).returns @instance - it "should add versions to found instances that do not already have them" do - @terminus.expects(:find).with(@name).returns(@instance) - time = mock 'time' - time.expects(:utc).returns(:mystamp) - Time.expects(:now).returns(time) - @instance.expects(:version=).with(:mystamp) - @indirection.find(@name) - end + @indirection.find(@name) + end - it "should add versions to saved instances that do not already have them" do - time = mock 'time' - time.expects(:utc).returns(:mystamp) - Time.expects(:now).returns(time) - @instance.expects(:version=).with(:mystamp) - @terminus.stubs(:save) - @indirection.save(@instance) - end + it "should not return the cached object if it is expired" do + @instance.stubs(:expired?).returns true - # We've already tested this, basically, but... - it "should use the current time in UTC for versions" do - @instance.expects(:version=).with do |time| - time.utc? - end - @terminus.stubs(:save) - @indirection.save(@instance) - end - end + @cache.stubs(:find).returns @instance + @terminus.stubs(:find).returns nil + @indirection.find(@name).should be_nil + end + it "should send an info log if it is using the cached object" do + Puppet.expects(:info) + @instance.stubs(:expired?).returns true - describe Puppet::Indirector::Indirection, " when an authorization hook is present" do + @cache.stubs(:find).returns @instance + @terminus.stubs(:find).returns nil + @indirection.find(@name) + end - before do - # So the :respond_to? turns out right. - class << @terminus - def authorized? + it "should cache any objects not retrieved from the cache" do + @cache.expects(:find).with(@name).returns nil + + @terminus.expects(:find).with(@name).returns(@instance) + @cache.expects(:save).with(@instance) + + @indirection.find(@name) + end + + it "should send an info log that the object is being cached" do + @cache.stubs(:find).returns nil + + @terminus.stubs(:find).returns(@instance) + @cache.stubs(:save) + + Puppet.expects(:info) + + @indirection.find(@name) end end end - it "should not check authorization if a node name is not provided" do - @terminus.expects(:authorized?).never - @terminus.stubs(:find) - @indirection.find("/my/key") + describe "and storing a model instance" do + it "should let the appropriate terminus store the instance" do + @terminus.expects(:save).with(@instance).returns(@instance) + @indirection.save(@instance).should == @instance + end + + it "should check authorization" do + @indirection.expects(:check_authorization).with(:save, :test_terminus, [@instance]) + @terminus.stubs(:save) + + @indirection.save(@instance) + end + + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + + @instance.stubs(:expired?).returns false + end + + it "should save the object to the cache" do + @cache.expects(:save).with(@instance) + @terminus.stubs(:save) + @indirection.save(@instance) + end + end + end + + describe "and removing a model instance" do + it "should delegate the instance removal to the appropriate terminus" do + @terminus.expects(:destroy).with(@name) + @indirection.destroy(@name) + end + + it "should return nil" do + @terminus.stubs(:destroy) + @indirection.destroy(@name).should be_nil + end + + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) + + @instance.stubs(:expired?).returns false + end + + it "should destroy any found object in the cache" do + cached = mock 'cache' + @cache.expects(:find).with(@name).returns cached + @cache.expects(:destroy).with(@name) + @terminus.stubs(:destroy) + + @indirection.destroy(@name) + end + end end - it "should fail while finding instances if authorization returns false" do - @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false) - @terminus.stubs(:find) - proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + describe "and searching for multiple model instances" do + it "should let the appropriate terminus find the matching instances" do + @terminus.expects(:search).with(@name).returns(@instance) + @indirection.search(@name).should == @instance + end end - it "should continue finding instances if authorization returns true" do - @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true) - @terminus.stubs(:find) - @indirection.find("/my/key", :node => "mynode") + describe "and an authorization hook is present" do + before do + # So the :respond_to? turns out correctly. + class << @terminus + def authorized? + end + end + end + + it "should not check authorization if a node name is not provided" do + @terminus.expects(:authorized?).never + @terminus.stubs(:find) + @indirection.find("/my/key") + end + + it "should fail while finding instances if authorization returns false" do + @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false) + @terminus.stubs(:find) + proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue finding instances if authorization returns true" do + @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true) + @terminus.stubs(:find) + @indirection.find("/my/key", :node => "mynode") + end + + it "should fail while saving instances if authorization returns false" do + @terminus.expects(:authorized?).with(:save, @instance, :node => "mynode").returns(false) + @terminus.stubs(:save) + proc { @indirection.save(@instance, :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue saving instances if authorization returns true" do + @terminus.expects(:authorized?).with(:save, @instance, :node => "mynode").returns(true) + @terminus.stubs(:save) + @indirection.save(@instance, :node => "mynode") + end + + it "should fail while destroying instances if authorization returns false" do + @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false) + @terminus.stubs(:destroy) + proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue destroying instances if authorization returns true" do + @terminus.expects(:authorized?).with(:destroy, @instance, :node => "mynode").returns(true) + @terminus.stubs(:destroy) + @indirection.destroy(@instance, :node => "mynode") + end + + it "should fail while searching for instances if authorization returns false" do + @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false) + @terminus.stubs(:search) + proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue searching for instances if authorization returns true" do + @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true) + @terminus.stubs(:search) + @indirection.search("/my/key", :node => "mynode") + end end - it "should fail while saving instances if authorization returns false" do - @terminus.expects(:authorized?).with(:save, :myinstance, :node => "mynode").returns(false) - @terminus.stubs(:save) - proc { @indirection.save(:myinstance, :node => "mynode") }.should raise_error(ArgumentError) - end - - it "should continue saving instances if authorization returns true" do - instance = stub 'instance', :version => 1.0, :name => "eh" - @terminus.expects(:authorized?).with(:save, instance, :node => "mynode").returns(true) - @terminus.stubs(:save) - @indirection.save(instance, :node => "mynode") - end - - it "should fail while destroying instances if authorization returns false" do - @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false) - @terminus.stubs(:destroy) - proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError) - end - - it "should continue destroying instances if authorization returns true" do - instance = stub 'instance', :version => 1.0, :name => "eh" - @terminus.expects(:authorized?).with(:destroy, instance, :node => "mynode").returns(true) - @terminus.stubs(:destroy) - @indirection.destroy(instance, :node => "mynode") - end - - it "should fail while searching for instances if authorization returns false" do - @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false) - @terminus.stubs(:search) - proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError) - end - - it "should continue searching for instances if authorization returns true" do - @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true) - @terminus.stubs(:search) - @indirection.search("/my/key", :node => "mynode") + after :each do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache end end - after :each do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end - -describe Puppet::Indirector::Indirection, " when managing indirection instances" do - it "should allow an indirection to be retrieved by name" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) - end - - it "should return nil when the named indirection has not been created" do - Puppet::Indirector::Indirection.instance(:test).should be_nil - end - - it "should allow an indirection's model to be retrieved by name" do - mock_model = mock('model') - @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) - Puppet::Indirector::Indirection.model(:test).should equal(mock_model) - end - - it "should return nil when no model matches the requested name" do - Puppet::Indirector::Indirection.model(:test).should be_nil - end - - after do - @indirection.delete if defined? @indirection - end -end - -describe Puppet::Indirector::Indirection, " when choosing the terminus class" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) - end - - it "should choose the default terminus class if one is specified and no specific terminus class is provided" do - @indirection.terminus_class = :default - @indirection.terminus_class.should equal(:default) - end - - it "should use the provided Puppet setting if told to do so" do - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2")) - Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus") - @indirection.terminus_setting = :my_setting - @indirection.terminus_class.should equal(:my_terminus) - end - - it "should fail if the provided terminus class is not valid" do - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil) - proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError) - end - - it "should fail if no terminus class is picked" do - proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) - end - - after do - @indirection.delete if defined? @indirection - end -end - -describe Puppet::Indirector::Indirection, " when specifying the terminus class to use" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus - end - - it "should allow specification of a terminus type" do - @indirection.should respond_to(:terminus_class=) - end - - it "should fail to redirect if no terminus type has been specified" do - proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) - end - - it "should fail when the terminus class name is an empty string" do - proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) - end - - it "should fail when the terminus class name is nil" do - proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) - end - - it "should fail when the specified terminus class cannot be found" do - Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) - proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError) - end - - it "should select the specified terminus class if a terminus class name is provided" do - Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class) - @indirection.terminus(:foo).should equal(@terminus) - end - - it "should use the configured terminus class if no terminus name is specified" do - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - @indirection.terminus_class = :foo - @indirection.terminus().should equal(@terminus) - end - - after do - @indirection.delete if defined? @indirection - end -end - -describe Puppet::Indirector::Indirection, " when a select_terminus hook is available" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - - # And provide a select_terminus hook - @indirection.meta_def(:select_terminus) do |uri| - :other + describe "when managing indirection instances" do + it "should allow an indirection to be retrieved by name" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) + end + + it "should return nil when the named indirection has not been created" do + Puppet::Indirector::Indirection.instance(:test).should be_nil end - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus - - @other_terminus = mock 'other_terminus' - @other_terminus_class = stub 'other_terminus_class', :new => @other_terminus - - @cache_terminus = mock 'cache_terminus' - @cache_terminus_class = stub 'cache_terminus_class', :new => @cache_terminus - - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :other).returns(@other_terminus_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache).returns(@cache_terminus_class) - - # Set it to a default type. - @indirection.terminus_class = :foo - - @uri = "full://url/path" - @result = stub 'result', :version => 1.0 - end - - it "should use the terminus name provided by passing the key to the :select_terminus hook when finding instances" do - # Set up the expectation - @other_terminus.expects(:find).with(@uri).returns(@result) - - @indirection.find(@uri) - end - - it "should use the terminus name provided by passing the key to the :select_terminus hook when testing if a cached instance is up to date" do - @indirection.cache_class = :cache - - @other_terminus.expects(:version).with(@uri).returns(2.0) - - @cache_terminus.expects(:has_most_recent?).with(@uri, 2.0).returns(true) - @cache_terminus.expects(:find).returns(:whatever) - - @indirection.find(@uri).should == :whatever - end - - it "should pass all arguments to the :select_terminus hook" do - @indirection.expects(:select_terminus).with(@uri, :node => "johnny").returns(:other) - @other_terminus.stubs(:find) - - @indirection.find(@uri, :node => "johnny") - end - - it "should pass the original key to the terminus rather than a modified key" do - # This is the same test as before - @other_terminus.expects(:find).with(@uri).returns(@result) - @indirection.find(@uri) - end - - after do - @indirection.delete if defined? @indirection - end -end - -describe Puppet::Indirector::Indirection, " when managing terminus instances" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = mock 'terminus class' - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - end - - it "should create an instance of the chosen terminus class" do - @terminus_class.stubs(:new).returns(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - end - - it "should allow the clearance of cached terminus instances" do - terminus1 = mock 'terminus1' - terminus2 = mock 'terminus2' - @terminus_class.stubs(:new).returns(terminus1, terminus2, ArgumentError) - @indirection.terminus(:foo).should equal(terminus1) - @indirection.class.clear_cache - @indirection.terminus(:foo).should equal(terminus2) - end - - # Make sure it caches the terminus. - it "should return the same terminus instance each time for a given name" do - @terminus_class.stubs(:new).returns(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - end - - it "should not create a terminus instance until one is actually needed" do - Puppet::Indirector.expects(:terminus).never - indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest) - end - - after do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end - -describe Puppet::Indirector::Indirection, " when deciding whether to cache" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @terminus = mock 'terminus' - @terminus_class = mock 'terminus class' - @terminus_class.stubs(:new).returns(@terminus) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - @indirection.terminus_class = :foo - end - - it "should provide a method for setting the cache terminus class" do - @indirection.should respond_to(:cache_class=) - end - - it "should fail to cache if no cache type has been specified" do - proc { @indirection.cache }.should raise_error(Puppet::DevError) - end - - it "should fail to set the cache class when the cache class name is an empty string" do - proc { @indirection.cache_class = "" }.should raise_error(ArgumentError) - end - - it "should fail to set the cache class when the cache class name is nil" do - proc { @indirection.cache_class = nil }.should raise_error(ArgumentError) - end - - it "should fail to set the cache class when the specified cache class cannot be found" do - Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) - proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) - end - - after do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end - -describe Puppet::Indirector::Indirection do - before :each do - Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") - @terminus_class = mock 'terminus_class' - @terminus = mock 'terminus' - @terminus_class.stubs(:new).returns(@terminus) - @cache = mock 'cache' - @cache_class = mock 'cache_class' - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - @indirection.terminus_class = :test_terminus - end - - describe Puppet::Indirector::Indirection, " when managing the cache terminus" do - - it "should not create a cache terminus at initialization" do - # This is weird, because all of the code is in the setup. If we got - # new() called on the cache class, we'd get an exception here. + it "should allow an indirection's model to be retrieved by name" do + mock_model = mock('model') + @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) + Puppet::Indirector::Indirection.model(:test).should equal(mock_model) + end + + it "should return nil when no model matches the requested name" do + Puppet::Indirector::Indirection.model(:test).should be_nil end - it "should reuse the cache terminus" do - @cache_class.expects(:new).returns(@cache) - Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") - @indirection.cache_class = :cache_terminus - @indirection.cache.should equal(@cache) - @indirection.cache.should equal(@cache) - end - - it "should remove the cache terminus when all other terminus instances are cleared" do - cache2 = mock 'cache2' - @cache_class.stubs(:new).returns(@cache, cache2) - @indirection.cache_class = :cache_terminus - @indirection.cache.should equal(@cache) - @indirection.clear_cache - @indirection.cache.should equal(cache2) + after do + @indirection.delete if defined? @indirection end end - describe Puppet::Indirector::Indirection, " when saving and using a cache" do - + describe "when routing to the correct the terminus class" do before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) - @name = "testing" - @instance = stub 'instance', :version => 5, :name => @name + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = stub 'terminus class', :new => @terminus + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) + end + + it "should have a method for choosing the appropriate terminus class" + + it "should fail if no terminus class can be picked" do + proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) end - it "should not update the cache or terminus if the new object is not different" do - @cache.expects(:has_most_recent?).with(@name, 5).returns(true) - @indirection.save(@instance) + it "should use the select_terminus hook if one is available" + + it "should choose the default terminus class if one is specified" do + @indirection.terminus_class = :default + @indirection.terminus_class.should equal(:default) end - it "should update the original and the cache if the cached object is different" do - @cache.expects(:has_most_recent?).with(@name, 5).returns(false) - @terminus.expects(:save).with(@instance) - @cache.expects(:save).with(@instance) - @indirection.save(@instance) + it "should use the provided Puppet setting if told to do so" do + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2")) + Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus") + @indirection.terminus_setting = :my_setting + @indirection.terminus_class.should equal(:my_terminus) + end + + it "should fail if the provided terminus class is not valid" do + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil) + proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError) + end + + after do + @indirection.delete if defined? @indirection end end - - describe Puppet::Indirector::Indirection, " when finding and using a cache" do + describe "when specifying the terminus class to use" do before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = stub 'terminus class', :new => @terminus end - it "should return the cached object if the cache is up to date" do - cached = mock 'cached object' - - name = "myobject" - - @terminus.expects(:version).with(name).returns(1) - @cache.expects(:has_most_recent?).with(name, 1).returns(true) - - @cache.expects(:find).with(name).returns(cached) - - @indirection.find(name).should equal(cached) + it "should allow specification of a terminus type" do + @indirection.should respond_to(:terminus_class=) end - it "should return the original object if the cache is not up to date" do - real = stub 'real object', :version => 1 - - name = "myobject" - - @cache.stubs(:save) - @cache.expects(:has_most_recent?).with(name, 1).returns(false) - @terminus.expects(:version).with(name).returns(1) - - @terminus.expects(:find).with(name).returns(real) - - @indirection.find(name).should equal(real) + it "should fail to redirect if no terminus type has been specified" do + proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) end - it "should cache any newly returned objects" do - real = stub 'real object', :version => 1 + it "should fail when the terminus class name is an empty string" do + proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) + end - name = "myobject" + it "should fail when the terminus class name is nil" do + proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) + end - @terminus.expects(:version).with(name).returns(1) - @cache.expects(:has_most_recent?).with(name, 1).returns(false) + it "should fail when the specified terminus class cannot be found" do + Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) + proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError) + end - @terminus.expects(:find).with(name).returns(real) - @cache.expects(:save).with(real) + it "should select the specified terminus class if a terminus class name is provided" do + Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class) + @indirection.terminus(:foo).should equal(@terminus) + end - @indirection.find(name).should equal(real) + it "should use the configured terminus class if no terminus name is specified" do + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + @indirection.terminus_class = :foo + @indirection.terminus().should equal(@terminus) + end + + after do + @indirection.delete if defined? @indirection end end - - after :each do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache + + describe "when a select_terminus hook is available" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + + # And provide a select_terminus hook + @indirection.meta_def(:select_terminus) do |uri| + :other + end + + @terminus = mock 'terminus' + @terminus_class = stub 'terminus class', :new => @terminus + + @other_terminus = mock 'other_terminus' + @other_terminus_class = stub 'other_terminus_class', :new => @other_terminus + + @cache_terminus = mock 'cache_terminus' + @cache_terminus_class = stub 'cache_terminus_class', :new => @cache_terminus + + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :other).returns(@other_terminus_class) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache).returns(@cache_terminus_class) + + # Set it to a default type. + @indirection.terminus_class = :foo + + @uri = "full://url/path" + @result = stub 'result', :expiration => 1, :expired? => false + end + + it "should use the terminus name provided by passing the key to the :select_terminus hook when finding instances" do + # Set up the expectation + @other_terminus.expects(:find).with(@uri).returns(@result) + + @indirection.find(@uri) + end + + it "should pass all arguments to the :select_terminus hook" do + @indirection.expects(:select_terminus).with(@uri, :node => "johnny").returns(:other) + @other_terminus.stubs(:find) + + @indirection.find(@uri, :node => "johnny") + end + + it "should pass the original key to the terminus rather than a modified key" do + # This is the same test as before + @other_terminus.expects(:find).with(@uri).returns(@result) + @indirection.find(@uri) + end + + after do + @indirection.delete if defined? @indirection + end + end + + describe "when managing terminus instances" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = mock 'terminus class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + end + + it "should create an instance of the chosen terminus class" do + @terminus_class.stubs(:new).returns(@terminus) + @indirection.terminus(:foo).should equal(@terminus) + end + + it "should allow the clearance of cached terminus instances" do + terminus1 = mock 'terminus1' + terminus2 = mock 'terminus2' + @terminus_class.stubs(:new).returns(terminus1, terminus2, ArgumentError) + @indirection.terminus(:foo).should equal(terminus1) + @indirection.class.clear_cache + @indirection.terminus(:foo).should equal(terminus2) + end + + # Make sure it caches the terminus. + it "should return the same terminus instance each time for a given name" do + @terminus_class.stubs(:new).returns(@terminus) + @indirection.terminus(:foo).should equal(@terminus) + @indirection.terminus(:foo).should equal(@terminus) + end + + it "should not create a terminus instance until one is actually needed" do + Puppet::Indirector.expects(:terminus).never + indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest) + end + + after do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache + end + end + + describe "when deciding whether to cache" do + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' + @terminus_class = mock 'terminus class' + @terminus_class.stubs(:new).returns(@terminus) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) + @indirection.terminus_class = :foo + end + + it "should provide a method for setting the cache terminus class" do + @indirection.should respond_to(:cache_class=) + end + + it "should fail to cache if no cache type has been specified" do + proc { @indirection.cache }.should raise_error(Puppet::DevError) + end + + it "should fail to set the cache class when the cache class name is an empty string" do + proc { @indirection.cache_class = "" }.should raise_error(ArgumentError) + end + + it "should fail to set the cache class when the cache class name is nil" do + proc { @indirection.cache_class = nil }.should raise_error(ArgumentError) + end + + it "should fail to set the cache class when the specified cache class cannot be found" do + Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) + proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) + end + + after do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache + end + end + + describe "when using a cache" do + before :each do + Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") + @terminus_class = mock 'terminus_class' + @terminus = mock 'terminus' + @terminus_class.stubs(:new).returns(@terminus) + @cache = mock 'cache' + @cache_class = mock 'cache_class' + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) + Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @indirection.terminus_class = :test_terminus + end + + describe "and managing the cache terminus" do + it "should not create a cache terminus at initialization" do + # This is weird, because all of the code is in the setup. If we got + # new() called on the cache class, we'd get an exception here. + end + + it "should reuse the cache terminus" do + @cache_class.expects(:new).returns(@cache) + Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") + @indirection.cache_class = :cache_terminus + @indirection.cache.should equal(@cache) + @indirection.cache.should equal(@cache) + end + + it "should remove the cache terminus when all other terminus instances are cleared" do + cache2 = mock 'cache2' + @cache_class.stubs(:new).returns(@cache, cache2) + @indirection.cache_class = :cache_terminus + @indirection.cache.should equal(@cache) + @indirection.clear_cache + @indirection.cache.should equal(cache2) + end + end + + describe "and saving" do + end + + describe "and finding" do + end + + after :each do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache + end end end From 941177a4902f4bfe7b8b3f528ce6648752f5409c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 7 Apr 2008 23:54:58 -0500 Subject: [PATCH 29/48] Changing how destroy works, just a bit -- it now accepts the name of the instance to be destroyed, rather than the instance itself. --- lib/puppet/indirector/file.rb | 13 ++++++++----- lib/puppet/indirector/indirection.rb | 2 ++ lib/puppet/indirector/memory.rb | 6 +++--- spec/unit/indirector/file.rb | 20 ++++++++------------ spec/unit/indirector/memory.rb | 2 +- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lib/puppet/indirector/file.rb b/lib/puppet/indirector/file.rb index c2d36c46b..8c984154b 100644 --- a/lib/puppet/indirector/file.rb +++ b/lib/puppet/indirector/file.rb @@ -2,21 +2,23 @@ require 'puppet/indirector/terminus' # An empty terminus type, meant to just return empty objects. class Puppet::Indirector::File < Puppet::Indirector::Terminus - def destroy(file) + # Remove files on disk. + def destroy(name) if respond_to?(:path) - path = path(file.name) + path = path(name) else - path = file.path + path = name end - raise Puppet::Error.new("File %s does not exist; cannot destroy" % [file]) unless File.exist?(path) + raise Puppet::Error.new("File %s does not exist; cannot destroy" % [name]) unless File.exist?(path) begin File.unlink(path) rescue => detail - raise Puppet::Error, "Could not remove %s: %s" % [file, detail] + raise Puppet::Error, "Could not remove %s: %s" % [name, detail] end end + # Return a model instance for a given file on disk. def find(name) if respond_to?(:path) path = path(name) @@ -35,6 +37,7 @@ class Puppet::Indirector::File < Puppet::Indirector::Terminus return model.new(content) end + # Save a new file to disk. def save(file) if respond_to?(:path) path = path(file.name) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 91cc42b17..6f1497319 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -211,6 +211,7 @@ class Puppet::Indirector::Indirection return nil end + # Remove something via the terminus. def destroy(key, *args) check_authorization(:destroy, terminus_class, ([key] + args)) @@ -223,6 +224,7 @@ class Puppet::Indirector::Indirection nil end + # Search for more than one instance. Should always return an array. def search(*args) check_authorization(:search, terminus_class, args) diff --git a/lib/puppet/indirector/memory.rb b/lib/puppet/indirector/memory.rb index 5bfcec95d..b97e6ffb6 100644 --- a/lib/puppet/indirector/memory.rb +++ b/lib/puppet/indirector/memory.rb @@ -6,9 +6,9 @@ class Puppet::Indirector::Memory < Puppet::Indirector::Terminus @instances = {} end - def destroy(instance) - raise ArgumentError.new("Could not find %s to destroy" % instance) unless @instances.include?(instance.name) - @instances.delete(instance.name) + def destroy(name) + raise ArgumentError.new("Could not find %s to destroy" % name) unless @instances.include?(name) + @instances.delete(name) end def find(name) diff --git a/spec/unit/indirector/file.rb b/spec/unit/indirector/file.rb index a2aa4502d..fa10743ef 100755 --- a/spec/unit/indirector/file.rb +++ b/spec/unit/indirector/file.rb @@ -121,38 +121,34 @@ describe Puppet::Indirector::File do describe Puppet::Indirector::File, " when removing files" do it "should provide a method to remove files at a specified path" do - file = stub 'file', :path => @path, :name => @path File.expects(:exist?).with(@path).returns(true) File.expects(:unlink).with(@path) - @searcher.destroy(file) + @searcher.destroy(@path) end it "should throw an exception if the file is not found" do - file = stub 'file', :path => @path, :name => @path File.expects(:exist?).with(@path).returns(false) - proc { @searcher.destroy(file) }.should raise_error(Puppet::Error) + proc { @searcher.destroy(@path) }.should raise_error(Puppet::Error) end it "should fail intelligently if the file cannot be removed" do - file = stub 'file', :path => @path, :name => @path File.expects(:exist?).with(@path).returns(true) File.expects(:unlink).with(@path).raises(ArgumentError) - proc { @searcher.destroy(file) }.should raise_error(Puppet::Error) + proc { @searcher.destroy(@path) }.should raise_error(Puppet::Error) end it "should use the path() method to calculate the path if it exists" do - @searcher.meta_def(:path) do |name| - name.upcase + @searcher.meta_def(:path) do |thing| + thing.to_s.upcase end - file = stub 'file', :name => "/yay" - File.expects(:exist?).with("/YAY").returns(true) - File.expects(:unlink).with("/YAY") + File.expects(:exist?).with("/MY/FILE").returns(true) + File.expects(:unlink).with("/MY/FILE") - @searcher.destroy(file) + @searcher.destroy(@path) end end end diff --git a/spec/unit/indirector/memory.rb b/spec/unit/indirector/memory.rb index c0fca6bd9..2e9a7f8f3 100755 --- a/spec/unit/indirector/memory.rb +++ b/spec/unit/indirector/memory.rb @@ -22,7 +22,7 @@ describe "A Memory Terminus", :shared => true do it "should be able to remove previously saved instances" do @searcher.save(@instance) - @searcher.destroy(@instance) + @searcher.destroy(@instance.name) @searcher.find(@name).should be_nil end From 0bd57995420615e81d74665e66b5a1937ba97792 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 00:02:39 -0500 Subject: [PATCH 30/48] Fixing one other test that was failing because of the change to Indirection#destroy. --- spec/unit/indirector/checksum/file.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/unit/indirector/checksum/file.rb b/spec/unit/indirector/checksum/file.rb index 3a82faccc..64425904f 100755 --- a/spec/unit/indirector/checksum/file.rb +++ b/spec/unit/indirector/checksum/file.rb @@ -128,13 +128,11 @@ describe Puppet::Checksum::File do end describe Puppet::Checksum::File, " when deleting files" do - it "should remove the file at the calculated path" do File.expects(:exist?).with(@path).returns(true) File.expects(:unlink).with(@path) - file = stub 'file', :name => @value - @store.destroy(file) + @store.destroy(@value) end end end From 4032a270d90ef93b39ed1df17ebe1040a11128b9 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 00:20:26 -0500 Subject: [PATCH 31/48] Fixing the integration tests related to the destroy fix. Yay. --- spec/integration/checksum.rb | 2 +- spec/integration/node.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/integration/checksum.rb b/spec/integration/checksum.rb index cb187c656..c94f3e47e 100755 --- a/spec/integration/checksum.rb +++ b/spec/integration/checksum.rb @@ -38,7 +38,7 @@ describe Puppet::Checksum, " when using the file terminus" do File.stubs(:exist?).returns(true) File.expects(:unlink).with(@file) - Puppet::Checksum.destroy(@sum) + Puppet::Checksum.destroy(@sum.name) end after do diff --git a/spec/integration/node.rb b/spec/integration/node.rb index 631d4403e..d9c2618c9 100755 --- a/spec/integration/node.rb +++ b/spec/integration/node.rb @@ -34,7 +34,7 @@ describe Puppet::Node, " when using the memory terminus" do it "should be able to remove previously saved nodes" do @node.save - Puppet::Node.destroy(@node) + Puppet::Node.destroy(@node.name) Puppet::Node.find(@name).should be_nil end From f9881edd5d4b9f86c1da1c66fb12324d0f9c3c41 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 02:17:31 -0500 Subject: [PATCH 32/48] Adding a Request class to the Indirection layer. This class is currently only used internally by the Indirection instances, but I expect that I will soon be pushing it to all of the terminus types. I still need to fix a couple of tests that will get broken by this commit. --- lib/puppet/indirector/indirection.rb | 89 +++++------ lib/puppet/indirector/request.rb | 18 +++ spec/unit/indirector/indirection.rb | 213 ++++++++++++++++----------- spec/unit/indirector/request.rb | 40 +++++ 4 files changed, 222 insertions(+), 138 deletions(-) create mode 100644 lib/puppet/indirector/request.rb create mode 100755 spec/unit/indirector/request.rb diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 6f1497319..735fc54da 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -1,5 +1,6 @@ require 'puppet/util/docs' require 'puppet/indirector/envelope' +require 'puppet/indirector/request' # The class that connects functional classes with their different collection # back-ends. Each indirection has a set of associated terminus classes, @@ -125,12 +126,6 @@ class Puppet::Indirector::Indirection end end - # Set the options that can be passed on to the terminus instances. - attr_reader :option_struct - def options=(options) - @option_struct = Struct.new(*options) - end - # Return the singleton terminus for this indirection. def terminus(terminus_name = nil) # Get the name of the terminus. @@ -173,17 +168,8 @@ class Puppet::Indirector::Indirection end def find(key, *args) - # Select the appropriate terminus if there's a hook - # for doing so. This allows the caller to pass in some kind - # of URI that the indirection can use for routing to the appropriate - # terminus. - if respond_to?(:select_terminus) - terminus_name = select_terminus(key, *args) - else - terminus_name = terminus_class - end - - check_authorization(:find, terminus_name, ([key] + args)) + request = request(key, :find, *args) + terminus = prepare(request) # See if our instance is in the cache and up to date. if cache? and cached = cache.find(key, *args) @@ -196,7 +182,7 @@ class Puppet::Indirector::Indirection end # Otherwise, return the result from the terminus, caching if appropriate. - if result = terminus(terminus_name).find(key, *args) + if result = terminus.find(key, *args) # Include the envelope module, so we can set the expiration. result.extend(Puppet::Indirector::Envelope) result.expiration ||= self.expiration @@ -213,7 +199,8 @@ class Puppet::Indirector::Indirection # Remove something via the terminus. def destroy(key, *args) - check_authorization(:destroy, terminus_class, ([key] + args)) + request = request(key, :destroy, *args) + terminus = prepare(request) terminus.destroy(key, *args) @@ -225,59 +212,52 @@ class Puppet::Indirector::Indirection end # Search for more than one instance. Should always return an array. - def search(*args) - check_authorization(:search, terminus_class, args) + def search(key, *args) + request = request(key, :search, *args) + terminus = prepare(request) - result = terminus.search(*args) + result = terminus.search(key, *args) result end - # these become instance methods + # Save the instance in the appropriate terminus. This method is + # normally an instance method on the indirected class. def save(instance, *args) - if respond_to?(:select_terminus) - terminus_name = select_terminus(instance.name, *args) - else - terminus_name = terminus_class - end - - check_authorization(:save, terminus_name, ([instance] + args)) + request = request(instance.name, :save, *args) + terminus = prepare(request) # If caching is enabled, save our document there, do cache.save(instance, *args) if cache? - terminus(terminus_class).save(instance, *args) - end - - def version(*args) - terminus.version(*args) + terminus.save(instance, *args) end private # Check authorization if there's a hook available; fail if there is one # and it returns false. - def check_authorization(method, terminus_name, arguments) - # Don't check authorization if there's no node. - # LAK:FIXME This is a hack and is quite possibly not the design we want. - return unless arguments[-1].is_a?(Hash) and arguments[-1][:node] + def check_authorization(request, terminus) + return unless request.options[:node] - if terminus(terminus_name).respond_to?(:authorized?) and ! terminus(terminus_name).authorized?(method, *arguments) - raise ArgumentError, "Not authorized to call %s with %s" % [method, arguments[0]] + return unless terminus.respond_to?(:authorized?) + + unless terminus.authorized?(request) + raise ArgumentError, "Not authorized to call %s with %s" % [request.method, request.options.inspect] end end - # Handle a given indirected call. - def prepare_call(method, arguments) - raise ArgumentError, "Options must be a hash" unless arguments.is_a?(Hash) + # Setup a request, pick the appropriate terminus, check the request's authorization, and return it. + def prepare(request) + # Pick our terminus. + if respond_to?(:select_terminus) + terminus_name = select_terminus(request) + else + terminus_name = terminus_class + end - # Set any terminus options. - options = option_struct ? set_options(option_struct, arguments) : nil + check_authorization(request, terminus(terminus_name)) - tclass = choose_terminus(options) - - check_authorization(method, tclass, options) - - return terminus(tclass), options + return terminus(terminus_name) end # Create a new terminus instance. @@ -289,9 +269,8 @@ class Puppet::Indirector::Indirection return klass.new end - # Create a struct instance with all of the appropriate options set - # from the provided hash. - def set_options(struct, arguments) - struct.new(struct.members.inject([]) { |array, param| arguments[param.to_sym]; array } ) + # Set up our request object. + def request(key, method, arguments = nil) + Puppet::Indirector::Request.new(self.name, key, method, arguments) end end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb new file mode 100644 index 000000000..a5495641d --- /dev/null +++ b/lib/puppet/indirector/request.rb @@ -0,0 +1,18 @@ +require 'puppet/indirector' + +# Provide any attributes or functionality needed for indirected +# instances. +class Puppet::Indirector::Request + attr_accessor :indirection_name, :key, :method, :options + + def initialize(indirection_name, key, method, options = {}) + @indirection_name, @key, @method, @options = indirection_name, key, method, (options || {}) + + raise ArgumentError, "Request options must be a hash, not %s" % @options.class unless @options.is_a?(Hash) + end + + # Look up the indirection based on the name provided. + def indirection + Puppet::Indirector::Indirection.instance(@indirection_name) + end +end diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index 5338a4858..8768076c6 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -35,16 +35,6 @@ describe Puppet::Indirector::Indirection do @indirection.metaclass.included_modules.should include(mod) end - it "should allow specification of supported options" do - lambda { @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :options => [:one, :two]) }.should_not raise_error - end - - it "should define a new Struct class to support the specified options" do - struct = mock 'struct' - Struct.expects(:new).with(:one, :two) - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :options => [:one, :two]) - end - after do @indirection.delete if defined? @indirection end @@ -85,6 +75,35 @@ describe Puppet::Indirector::Indirection do end describe "and looking for a model instance" do + it "should create a request with the indirection name, the sought-after name, the :find method, and any passed arguments" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).with(@indirection.name, @name, :find, {:one => :two}).returns request + + @indirection.stubs(:check_authorization) + @terminus.stubs(:find) + + @indirection.find(@name, :one => :two) + end + + it "should let the :select_terminus method choose the terminus if the method is defined" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + @indirection.expects(:select_terminus).with(request).returns :test_terminus + + @indirection.stubs(:check_authorization) + @terminus.expects(:find) + + @indirection.find(@name) + + end + it "should let the appropriate terminus perform the lookup" do @terminus.expects(:find).with(@name).returns(@instance) @indirection.find(@name).should == @instance @@ -197,18 +216,39 @@ describe Puppet::Indirector::Indirection do end describe "and storing a model instance" do + it "should create a request with the indirection name, the instance's name, the :save method, and any passed arguments" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).with(@indirection.name, @instance.name, :save, {:one => :two}).returns request + + @indirection.stubs(:check_authorization) + @terminus.stubs(:save) + + @indirection.save(@instance, :one => :two) + end + + it "should let the :select_terminus method choose the terminus if the method is defined" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + @indirection.expects(:select_terminus).with(request).returns :test_terminus + + @indirection.stubs(:check_authorization) + @terminus.expects(:save) + + @indirection.save(@instance) + end + it "should let the appropriate terminus store the instance" do @terminus.expects(:save).with(@instance).returns(@instance) @indirection.save(@instance).should == @instance end - it "should check authorization" do - @indirection.expects(:check_authorization).with(:save, :test_terminus, [@instance]) - @terminus.stubs(:save) - - @indirection.save(@instance) - end - describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @@ -226,6 +266,34 @@ describe Puppet::Indirector::Indirection do end describe "and removing a model instance" do + it "should create a request with the indirection name, the name of the instance being destroyed, the :destroy method, and any passed arguments" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).with(@indirection.name, "me", :destroy, {:one => :two}).returns request + + @indirection.stubs(:check_authorization) + @terminus.stubs(:destroy) + + @indirection.destroy("me", :one => :two) + end + + it "should let the :select_terminus method choose the terminus if the method is defined" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + @indirection.expects(:select_terminus).with(request).returns :test_terminus + + @indirection.stubs(:check_authorization) + @terminus.expects(:destroy) + + @indirection.destroy(@name) + end + it "should delegate the instance removal to the appropriate terminus" do @terminus.expects(:destroy).with(@name) @indirection.destroy(@name) @@ -256,6 +324,34 @@ describe Puppet::Indirector::Indirection do end describe "and searching for multiple model instances" do + it "should create a request with the indirection name, the search key, the :search method, and any passed arguments" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).with(@indirection.name, "me", :search, {:one => :two}).returns request + + @indirection.stubs(:check_authorization) + @terminus.stubs(:search) + + @indirection.search("me", :one => :two) + end + + it "should let the :select_terminus method choose the terminus if the method is defined" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + @indirection.expects(:select_terminus).with(request).returns :test_terminus + + @indirection.stubs(:check_authorization) + @terminus.expects(:search) + + @indirection.search("me") + end + it "should let the appropriate terminus find the matching instances" do @terminus.expects(:search).with(@name).returns(@instance) @indirection.search(@name).should == @instance @@ -277,50 +373,59 @@ describe Puppet::Indirector::Indirection do @indirection.find("/my/key") end + it "should pass the request to the terminus's authorization method" do + request = stub 'request', :options => {:node => "yayhost"} + Puppet::Indirector::Request.expects(:new).returns(request) + @terminus.expects(:authorized?).with(request).returns(true) + @terminus.stubs(:find) + + @indirection.find("/my/key", :node => "mynode") + end + it "should fail while finding instances if authorization returns false" do - @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(false) + @terminus.expects(:authorized?).returns(false) @terminus.stubs(:find) proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError) end it "should continue finding instances if authorization returns true" do - @terminus.expects(:authorized?).with(:find, "/my/key", :node => "mynode").returns(true) + @terminus.expects(:authorized?).returns(true) @terminus.stubs(:find) @indirection.find("/my/key", :node => "mynode") end it "should fail while saving instances if authorization returns false" do - @terminus.expects(:authorized?).with(:save, @instance, :node => "mynode").returns(false) + @terminus.expects(:authorized?).returns(false) @terminus.stubs(:save) proc { @indirection.save(@instance, :node => "mynode") }.should raise_error(ArgumentError) end it "should continue saving instances if authorization returns true" do - @terminus.expects(:authorized?).with(:save, @instance, :node => "mynode").returns(true) + @terminus.expects(:authorized?).returns(true) @terminus.stubs(:save) @indirection.save(@instance, :node => "mynode") end it "should fail while destroying instances if authorization returns false" do - @terminus.expects(:authorized?).with(:destroy, "/my/key", :node => "mynode").returns(false) + @terminus.expects(:authorized?).returns(false) @terminus.stubs(:destroy) proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError) end it "should continue destroying instances if authorization returns true" do - @terminus.expects(:authorized?).with(:destroy, @instance, :node => "mynode").returns(true) + @terminus.expects(:authorized?).returns(true) @terminus.stubs(:destroy) @indirection.destroy(@instance, :node => "mynode") end it "should fail while searching for instances if authorization returns false" do - @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(false) + @terminus.expects(:authorized?).returns(false) @terminus.stubs(:search) proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError) end it "should continue searching for instances if authorization returns true" do - @terminus.expects(:authorized?).with(:search, "/my/key", :node => "mynode").returns(true) + @terminus.expects(:authorized?).returns(true) @terminus.stubs(:search) @indirection.search("/my/key", :node => "mynode") end @@ -365,15 +470,11 @@ describe Puppet::Indirector::Indirection do @terminus_class = stub 'terminus class', :new => @terminus Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) end - - it "should have a method for choosing the appropriate terminus class" it "should fail if no terminus class can be picked" do proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) end - it "should use the select_terminus hook if one is available" - it "should choose the default terminus class if one is specified" do @indirection.terminus_class = :default @indirection.terminus_class.should equal(:default) @@ -440,60 +541,6 @@ describe Puppet::Indirector::Indirection do end end - describe "when a select_terminus hook is available" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) - - # And provide a select_terminus hook - @indirection.meta_def(:select_terminus) do |uri| - :other - end - - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus - - @other_terminus = mock 'other_terminus' - @other_terminus_class = stub 'other_terminus_class', :new => @other_terminus - - @cache_terminus = mock 'cache_terminus' - @cache_terminus_class = stub 'cache_terminus_class', :new => @cache_terminus - - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :other).returns(@other_terminus_class) - Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache).returns(@cache_terminus_class) - - # Set it to a default type. - @indirection.terminus_class = :foo - - @uri = "full://url/path" - @result = stub 'result', :expiration => 1, :expired? => false - end - - it "should use the terminus name provided by passing the key to the :select_terminus hook when finding instances" do - # Set up the expectation - @other_terminus.expects(:find).with(@uri).returns(@result) - - @indirection.find(@uri) - end - - it "should pass all arguments to the :select_terminus hook" do - @indirection.expects(:select_terminus).with(@uri, :node => "johnny").returns(:other) - @other_terminus.stubs(:find) - - @indirection.find(@uri, :node => "johnny") - end - - it "should pass the original key to the terminus rather than a modified key" do - # This is the same test as before - @other_terminus.expects(:find).with(@uri).returns(@result) - @indirection.find(@uri) - end - - after do - @indirection.delete if defined? @indirection - end - end - describe "when managing terminus instances" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) diff --git a/spec/unit/indirector/request.rb b/spec/unit/indirector/request.rb new file mode 100755 index 000000000..ee60f600f --- /dev/null +++ b/spec/unit/indirector/request.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' +require 'puppet/indirector/request' + +describe Puppet::Indirector::Request do + describe "when initializing" do + it "should require an indirection name, a key, and a method" do + lambda { Puppet::Indirector::Request.new }.should raise_error(ArgumentError) + end + + it "should support options specified as a hash" do + lambda { Puppet::Indirector::Request.new(:ind, :key, :method, :one => :two) }.should_not raise_error(ArgumentError) + end + + it "should support nil options" do + lambda { Puppet::Indirector::Request.new(:ind, :key, :method, nil) }.should_not raise_error(ArgumentError) + end + + it "should support unspecified options" do + lambda { Puppet::Indirector::Request.new(:ind, :key, :method) }.should_not raise_error(ArgumentError) + end + + it "should fail if options are specified as anything other than nil or a hash" do + lambda { Puppet::Indirector::Request.new(:ind, :key, :method, [:one, :two]) }.should raise_error(ArgumentError) + end + + it "should use an empty options hash if nil was provided" do + Puppet::Indirector::Request.new(:ind, :key, :method, nil).options.should == {} + end + end + + it "should look use the Indirection class to return the appropriate indirection" do + ind = mock 'indirection' + Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind + request = Puppet::Indirector::Request.new(:myind, :key, :method) + + request.indirection.should equal(ind) + end +end From 69a321f07dc0cbec7a0471a3378e9330d12c47b7 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 02:38:20 -0500 Subject: [PATCH 33/48] Fixing the tests that were failing because of the use of the indirection request object. --- lib/puppet/file_serving/indirection_hooks.rb | 6 +- spec/unit/file_serving/indirection_hooks.rb | 194 ++++++++++--------- 2 files changed, 108 insertions(+), 92 deletions(-) diff --git a/lib/puppet/file_serving/indirection_hooks.rb b/lib/puppet/file_serving/indirection_hooks.rb index 141642efe..66ed169dc 100644 --- a/lib/puppet/file_serving/indirection_hooks.rb +++ b/lib/puppet/file_serving/indirection_hooks.rb @@ -12,7 +12,8 @@ module Puppet::FileServing::IndirectionHooks PROTOCOL_MAP = {"puppet" => :rest, "file" => :file, "puppetmounts" => :file_server} # Pick an appropriate terminus based on the protocol. - def select_terminus(full_uri, options = {}) + def select_terminus(request) + full_uri = request.key # Short-circuit to :file if it's a fully-qualified path. return PROTOCOL_MAP["file"] if full_uri =~ /^#{::File::SEPARATOR}/ begin @@ -29,11 +30,12 @@ module Puppet::FileServing::IndirectionHooks terminus = :file_server end + # This is the backward-compatible module terminus. if terminus == :file_server and uri.path =~ %r{^/([^/]+)\b} modname = $1 if modname == "modules" terminus = :modules - elsif terminus(:modules).find_module(modname, options[:node]) + elsif terminus(:modules).find_module(modname, request.options[:node]) Puppet.warning "DEPRECATION NOTICE: Found file '%s' in module without using the 'modules' mount; please prefix path with '/modules'" % uri.path terminus = :modules end diff --git a/spec/unit/file_serving/indirection_hooks.rb b/spec/unit/file_serving/indirection_hooks.rb index 34614b7b8..160e3ff0a 100755 --- a/spec/unit/file_serving/indirection_hooks.rb +++ b/spec/unit/file_serving/indirection_hooks.rb @@ -7,104 +7,118 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/file_serving/indirection_hooks' -describe Puppet::FileServing::IndirectionHooks, " when being used to select termini" do - before do - @object = Object.new - @object.extend(Puppet::FileServing::IndirectionHooks) - end - - it "should escape the key before parsing" do - uri = stub 'uri', :scheme => "puppet", :host => "blah", :path => "/something" - URI.expects(:escape).with("mykey").returns("http://myhost/blah") - URI.expects(:parse).with("http://myhost/blah").returns(uri) - @object.select_terminus("mykey") - end - - it "should use the URI class to parse the key" do - uri = stub 'uri', :scheme => "puppet", :host => "blah", :path => "/something" - URI.expects(:parse).with("http://myhost/blah").returns(uri) - @object.select_terminus("http://myhost/blah") - end - - it "should choose :rest when the protocol is 'puppet'" do - @object.select_terminus("puppet://host/module/file").should == :rest - end - - it "should choose :file_server when the protocol is 'puppetmounts' and the mount name is not 'modules'" do - modules = mock 'modules' - @object.stubs(:terminus).with(:modules).returns(modules) - modules.stubs(:find_module).returns(nil) - - @object.select_terminus("puppetmounts://host/notmodules/file").should == :file_server - end - - it "should choose :file_server when no server name is provided, the process name is 'puppet', and the mount name is not 'modules'" do - modules = mock 'modules' - @object.stubs(:terminus).with(:modules).returns(modules) - modules.stubs(:find_module).returns(nil) - - Puppet.settings.expects(:value).with(:name).returns("puppet") - @object.select_terminus("puppet:///notmodules/file").should == :file_server - end - - it "should choose :modules if it would normally choose :file_server but the mount name is 'modules'" do - @object.select_terminus("puppetmounts://host/modules/mymod/file").should == :modules - end - - it "should choose :modules it would normally choose :file_server but a module exists with the mount name" do - modules = mock 'modules' - - @object.expects(:terminus).with(:modules).returns(modules) - modules.expects(:find_module).with("mymod", nil).returns(:thing) - - @object.select_terminus("puppetmounts://host/mymod/file").should == :modules - end - - it "should choose :rest when no server name is provided and the process name is not 'puppet'" do - Puppet.settings.expects(:value).with(:name).returns("puppetd") - @object.select_terminus("puppet:///module/file").should == :rest - end - - it "should choose :file when the protocol is 'file'" do - @object.select_terminus("file://host/module/file").should == :file - end - - it "should choose :file when the URI is a normal path name" do - @object.select_terminus("/module/file").should == :file - end - - # This is so that we only choose modules over mounts, not file - it "should choose :file when the protocol is 'file' and the fully qualified path starts with '/modules'" do - @object.select_terminus("file://host/modules/file").should == :file - end - - it "should fail when a protocol other than :puppet, :file, or :puppetmounts is used" do - proc { @object.select_terminus("http:///module/file") }.should raise_error(ArgumentError) - end -end - -describe Puppet::FileServing::IndirectionHooks, " when looking for a module whose name matches the mount name" do +describe Puppet::FileServing::IndirectionHooks do before do @object = Object.new @object.extend(Puppet::FileServing::IndirectionHooks) - @modules = mock 'modules' - @object.stubs(:terminus).with(:modules).returns(@modules) + @request = stub 'request', :key => "http://myhost/blah", :options => {:node => "whatever"} end - it "should use the modules terminus to look up the module" do - @modules.expects(:find_module).with("mymod", nil) - @object.select_terminus("puppetmounts://host/mymod/my/file") + describe "when being used to select termini" do + it "should escape the key before parsing" do + uri = stub 'uri', :scheme => "puppet", :host => "blah", :path => "/something" + URI.expects(:escape).with("http://myhost/blah").returns("escaped_blah") + URI.expects(:parse).with("escaped_blah").returns(uri) + @object.select_terminus(@request) + end + + it "should use the URI class to parse the key" do + uri = stub 'uri', :scheme => "puppet", :host => "blah", :path => "/something" + URI.expects(:parse).with("http://myhost/blah").returns(uri) + @object.select_terminus @request + end + + it "should choose :rest when the protocol is 'puppet'" do + @request.stubs(:key).returns "puppet://host/module/file" + @object.select_terminus(@request).should == :rest + end + + it "should choose :file_server when the protocol is 'puppetmounts' and the mount name is not 'modules'" do + modules = mock 'modules' + @object.stubs(:terminus).with(:modules).returns(modules) + modules.stubs(:find_module).returns(nil) + + @request.stubs(:key).returns "puppetmounts://host/notmodules/file" + + @object.select_terminus(@request).should == :file_server + end + + it "should choose :file_server when no server name is provided, the process name is 'puppet', and the mount name is not 'modules'" do + modules = mock 'modules' + @object.stubs(:terminus).with(:modules).returns(modules) + modules.stubs(:find_module).returns(nil) + + Puppet.settings.expects(:value).with(:name).returns("puppet") + @request.stubs(:key).returns "puppet:///notmodules/file" + @object.select_terminus(@request).should == :file_server + end + + it "should choose :modules if it would normally choose :file_server but the mount name is 'modules'" do + @request.stubs(:key).returns "puppetmounts://host/modules/mymod/file" + @object.select_terminus(@request).should == :modules + end + + it "should choose :modules if it would normally choose :file_server but a module exists with the mount name" do + modules = mock 'modules' + + @object.expects(:terminus).with(:modules).returns(modules) + modules.expects(:find_module).with("mymod", @request.options[:node]).returns(:thing) + + @request.stubs(:key).returns "puppetmounts://host/mymod/file" + @object.select_terminus(@request).should == :modules + end + + it "should choose :rest when no server name is provided and the process name is not 'puppet'" do + Puppet.settings.expects(:value).with(:name).returns("puppetd") + @request.stubs(:key).returns "puppet:///module/file" + @object.select_terminus(@request).should == :rest + end + + it "should choose :file when the protocol is 'file'" do + @request.stubs(:key).returns "file://host/module/file" + @object.select_terminus(@request).should == :file + end + + it "should choose :file when the URI is a normal path name" do + @request.stubs(:key).returns "/module/file" + @object.select_terminus(@request).should == :file + end + + # This is so that we only choose modules over mounts, not file + it "should choose :file when the protocol is 'file' and the fully qualified path starts with '/modules'" do + @request.stubs(:key).returns "/module/file" + @object.select_terminus(@request).should == :file + end + + it "should fail when a protocol other than :puppet, :file, or :puppetmounts is used" do + @request.stubs(:key).returns "http:///module/file" + proc { @object.select_terminus(@request) }.should raise_error(ArgumentError) + end end - it "should pass the node name to the modules terminus" do - @modules.expects(:find_module).with("mymod", nil) - @object.select_terminus("puppetmounts://host/mymod/my/file") - end + describe "when looking for a module whose name matches the mount name" do + before do + @modules = mock 'modules' + @object.stubs(:terminus).with(:modules).returns(@modules) - it "should log a deprecation warning if a module is found" do - @modules.expects(:find_module).with("mymod", nil).returns(:something) - Puppet.expects(:warning) - @object.select_terminus("puppetmounts://host/mymod/my/file") + @request.stubs(:key).returns "puppetmounts://host/mymod/file" + end + + it "should use the modules terminus to look up the module" do + @modules.expects(:find_module).with("mymod", @request.options[:node]) + @object.select_terminus @request + end + + it "should pass the node name to the modules terminus" do + @modules.expects(:find_module).with("mymod", @request.options[:node]) + @object.select_terminus @request + end + + it "should log a deprecation warning if a module is found" do + @modules.expects(:find_module).with("mymod", @request.options[:node]).returns(:something) + Puppet.expects(:warning) + @object.select_terminus @request + end end end From 38f0f483faeba28d12a5f4e1d52e8d10ab96e68b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 10:18:42 -0500 Subject: [PATCH 34/48] Fixing an errant comment --- lib/puppet/indirector/indirection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 735fc54da..1b6613035 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -227,7 +227,7 @@ class Puppet::Indirector::Indirection request = request(instance.name, :save, *args) terminus = prepare(request) - # If caching is enabled, save our document there, do + # If caching is enabled, save our document there cache.save(instance, *args) if cache? terminus.save(instance, *args) end From 768315bd6e8ecd064ab473187c10b4633f09523c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 11:39:29 -0500 Subject: [PATCH 35/48] Adding the ability for indirection requests to be created with instances instead of just keys. --- lib/puppet/indirector/request.rb | 11 +++++++++-- spec/unit/indirector/request.rb | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index a5495641d..708862b28 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -3,10 +3,17 @@ require 'puppet/indirector' # Provide any attributes or functionality needed for indirected # instances. class Puppet::Indirector::Request - attr_accessor :indirection_name, :key, :method, :options + attr_accessor :indirection_name, :key, :method, :options, :instance def initialize(indirection_name, key, method, options = {}) - @indirection_name, @key, @method, @options = indirection_name, key, method, (options || {}) + @indirection_name, @method, @options = indirection_name, method, (options || {}) + + if key.is_a?(String) or key.is_a?(Symbol) + @key = key + else + @instance = key + @key = @instance.name + end raise ArgumentError, "Request options must be a hash, not %s" % @options.class unless @options.is_a?(Hash) end diff --git a/spec/unit/indirector/request.rb b/spec/unit/indirector/request.rb index ee60f600f..fd57c5297 100755 --- a/spec/unit/indirector/request.rb +++ b/spec/unit/indirector/request.rb @@ -9,6 +9,21 @@ describe Puppet::Indirector::Request do lambda { Puppet::Indirector::Request.new }.should raise_error(ArgumentError) end + it "should use provided value as the key if it is a string" do + Puppet::Indirector::Request.new(:ind, "mykey", :method).key.should == "mykey" + end + + it "should use provided value as the key if it is a symbol" do + Puppet::Indirector::Request.new(:ind, :mykey, :method).key.should == :mykey + end + + it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do + instance = mock 'instance', :name => "mykey" + request = Puppet::Indirector::Request.new(:ind, instance, :method) + request.key.should == "mykey" + request.instance.should equal(instance) + end + it "should support options specified as a hash" do lambda { Puppet::Indirector::Request.new(:ind, :key, :method, :one => :two) }.should_not raise_error(ArgumentError) end From 644d6baae132a097170631f90521e878e31a5a0a Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 14:09:29 -0500 Subject: [PATCH 36/48] Fixing some tests that were failing because new base types were added to Naginator, but no new related resource types were added. --- lib/puppet/type/nagios_hostescalation.rb | 3 + lib/puppet/type/nagios_servicegroup.rb | 3 + spec/unit/ral/type/nagios.rb | 80 +++++++++++++----------- 3 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 lib/puppet/type/nagios_hostescalation.rb create mode 100644 lib/puppet/type/nagios_servicegroup.rb diff --git a/lib/puppet/type/nagios_hostescalation.rb b/lib/puppet/type/nagios_hostescalation.rb new file mode 100644 index 000000000..5d18af2a6 --- /dev/null +++ b/lib/puppet/type/nagios_hostescalation.rb @@ -0,0 +1,3 @@ +require 'puppet/util/nagios_maker' + +Puppet::Util::NagiosMaker.create_nagios_type :hostescalation diff --git a/lib/puppet/type/nagios_servicegroup.rb b/lib/puppet/type/nagios_servicegroup.rb new file mode 100644 index 000000000..fef669639 --- /dev/null +++ b/lib/puppet/type/nagios_servicegroup.rb @@ -0,0 +1,3 @@ +require 'puppet/util/nagios_maker' + +Puppet::Util::NagiosMaker.create_nagios_type :servicegroup diff --git a/spec/unit/ral/type/nagios.rb b/spec/unit/ral/type/nagios.rb index 8aca7d401..35f00b0e5 100755 --- a/spec/unit/ral/type/nagios.rb +++ b/spec/unit/ral/type/nagios.rb @@ -4,51 +4,59 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/external/nagios' -Nagios::Base.eachtype do |name, nagios_type| - puppet_type = Puppet::Type.type("nagios_" + name.to_s) +describe "Nagios resource types" do + Nagios::Base.eachtype do |name, nagios_type| + puppet_type = Puppet::Type.type("nagios_" + name.to_s) - describe puppet_type do - it "should be defined as a Puppet resource type" do + it "should have a valid type for #{name}" do puppet_type.should_not be_nil end - it "should have documentation" do - puppet_type.instance_variable_get("@doc").should_not == "" - end + next unless puppet_type - it "should have %s as its namevar" % nagios_type.namevar do - puppet_type.namevar.should == nagios_type.namevar - end - - it "should have documentation for its %s parameter" % nagios_type.namevar do - puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil - end - - it "should have an ensure property" do - puppet_type.should be_validproperty(:ensure) - end - - it "should have a target property" do - puppet_type.should be_validproperty(:target) - end - - it "should have documentation for its target property" do - puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil - end - - nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| - it "should have a %s property" % param do - puppet_type.should be_validproperty(param) + describe puppet_type do + it "should be defined as a Puppet resource type" do + puppet_type.should_not be_nil end - it "should have documentation for its %s property" % param do - puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil + it "should have documentation" do + puppet_type.instance_variable_get("@doc").should_not == "" end - end - nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| - it "should have not have a %s property" % param do - puppet_type.should_not be_validproperty(:param) + it "should have %s as its namevar" % nagios_type.namevar do + puppet_type.namevar.should == nagios_type.namevar + end + + it "should have documentation for its %s parameter" % nagios_type.namevar do + puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil + end + + it "should have an ensure property" do + puppet_type.should be_validproperty(:ensure) + end + + it "should have a target property" do + puppet_type.should be_validproperty(:target) + end + + it "should have documentation for its target property" do + puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil + end + + nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| + it "should have a %s property" % param do + puppet_type.should be_validproperty(param) + end + + it "should have documentation for its %s property" % param do + puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil + end + end + + nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| + it "should have not have a %s property" % param do + puppet_type.should_not be_validproperty(:param) + end end end end From bf728d23caca4f58ae4ede1a2d477c9fc15e0bdc Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 18:21:18 -0500 Subject: [PATCH 37/48] Intermediate commit. This commit adds a Request instance into the indirection, pushing it all the way to the terminus instances. It's a big commit because it requires modifying every terminus class. There are still some thorny design issues. In particular, who should be responsible for making the request object? I've tried having both the indirection class and the Indirector module creating it, and both have their issues. Also, the Catalog class previously allowed passing Node instances directly to the find method, which is now no longer possible because the Request class would treat the node as the instance being found. We need the request class to have two modes, one when it's passed an instance and one when it's passed a key. --- lib/puppet/indirector.rb | 49 ++-- lib/puppet/indirector/checksum/file.rb | 4 +- lib/puppet/indirector/direct_file_server.rb | 14 +- lib/puppet/indirector/file.rb | 28 +-- lib/puppet/indirector/file_metadata/file.rb | 4 +- lib/puppet/indirector/indirection.rb | 50 ++-- lib/puppet/indirector/memory.rb | 14 +- lib/puppet/indirector/plain.rb | 4 +- lib/puppet/indirector/request.rb | 2 +- lib/puppet/indirector/yaml.rb | 28 +-- lib/puppet/transaction/report.rb | 4 + .../indirector/direct_file_server.rb | 10 +- spec/integration/node/catalog.rb | 34 +++ spec/shared_behaviours/file_serving.rb | 12 +- spec/shared_behaviours/memory_terminus.rb | 32 +++ spec/unit/indirector.rb | 98 ++++++-- spec/unit/indirector/catalog/compiler.rb | 4 +- spec/unit/indirector/checksum/file.rb | 17 +- spec/unit/indirector/direct_file_server.rb | 24 +- spec/unit/indirector/file.rb | 50 ++-- spec/unit/indirector/file_metadata/file.rb | 54 +++-- spec/unit/indirector/indirection.rb | 215 +++++++++--------- spec/unit/indirector/memory.rb | 30 +-- spec/unit/indirector/node/memory.rb | 5 +- spec/unit/indirector/plain.rb | 6 +- spec/unit/indirector/request.rb | 18 +- spec/unit/indirector/yaml.rb | 34 +-- spec/unit/node.rb | 4 +- spec/unit/node/catalog.rb | 4 +- spec/unit/node/facts.rb | 6 +- spec/unit/transaction/report.rb | 14 +- 31 files changed, 475 insertions(+), 397 deletions(-) create mode 100755 spec/integration/node/catalog.rb create mode 100644 spec/shared_behaviours/memory_terminus.rb diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index c30c097b2..a8a7a84d1 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -9,6 +9,7 @@ module Puppet::Indirector require 'puppet/indirector/indirection' require 'puppet/indirector/terminus' + require 'puppet/indirector/envelope' # Declare that the including class indirects its methods to # this terminus. The terminus name must be the name of a Puppet @@ -20,6 +21,7 @@ module Puppet::Indirector # populate this class with the various new methods extend ClassMethods include InstanceMethods + include Puppet::Indirector::Envelope # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node) # & hook the instantiated Terminus into this class (Node: @indirection = terminus) @@ -28,41 +30,32 @@ module Puppet::Indirector end module ClassMethods - attr_reader :indirection + attr_reader :indirection - def cache_class=(klass) - indirection.cache_class = klass - end + def cache_class=(klass) + indirection.cache_class = klass + end - def terminus_class=(klass) - indirection.terminus_class = klass - end + def terminus_class=(klass) + indirection.terminus_class = klass + end - def find(*args) - indirection.find(*args) - end + def find(*args) + indirection.find Puppet::Indirector::Request.new(indirection.name, :find, *args) + end - def destroy(*args) - indirection.destroy(*args) - end + def destroy(*args) + indirection.destroy Puppet::Indirector::Request.new(indirection.name, :destroy, *args) + end - def search(*args) - indirection.search(*args) - end - - def version(*args) - indirection.version(*args) - end + def search(*args) + indirection.search Puppet::Indirector::Request.new(indirection.name, :search, *args) + end end module InstanceMethods - # Make it easy for the model to set versions, - # which are used for caching and such. - attr_accessor :version - - # these become instance methods - def save(*args) - self.class.indirection.save(self, *args) - end + def save(*args) + self.class.indirection.save Puppet::Indirector::Request.new(self.class.indirection.name, :save, self, *args) + end end end diff --git a/lib/puppet/indirector/checksum/file.rb b/lib/puppet/indirector/checksum/file.rb index 3b196a1f8..5489b40e8 100644 --- a/lib/puppet/indirector/checksum/file.rb +++ b/lib/puppet/indirector/checksum/file.rb @@ -18,8 +18,8 @@ class Puppet::Checksum::File < Puppet::Indirector::File path.join(File::SEPARATOR) end - def save(file) - path = File.dirname(path(file.name)) + def save(request) + path = File.dirname(path(request.key)) # Make the directories if necessary. unless FileTest.directory?(path) diff --git a/lib/puppet/indirector/direct_file_server.rb b/lib/puppet/indirector/direct_file_server.rb index 31cc9aa16..1711356f9 100644 --- a/lib/puppet/indirector/direct_file_server.rb +++ b/lib/puppet/indirector/direct_file_server.rb @@ -11,17 +11,17 @@ class Puppet::Indirector::DirectFileServer < Puppet::Indirector::Terminus include Puppet::Util::URIHelper include Puppet::FileServing::TerminusHelper - def find(key, options = {}) - uri = key2uri(key) + def find(request) + uri = key2uri(request.key) return nil unless FileTest.exists?(uri.path) - instance = model.new(key, :path => uri.path) - instance.links = options[:links] if options[:links] + instance = model.new(request.key, :path => uri.path) + instance.links = request.options[:links] if request.options[:links] return instance end - def search(key, options = {}) - uri = key2uri(key) + def search(request) + uri = key2uri(request.key) return nil unless FileTest.exists?(uri.path) - path2instances(key, uri.path, options) + path2instances(request.key, uri.path, request.options) end end diff --git a/lib/puppet/indirector/file.rb b/lib/puppet/indirector/file.rb index 8c984154b..e5382155f 100644 --- a/lib/puppet/indirector/file.rb +++ b/lib/puppet/indirector/file.rb @@ -3,27 +3,27 @@ require 'puppet/indirector/terminus' # An empty terminus type, meant to just return empty objects. class Puppet::Indirector::File < Puppet::Indirector::Terminus # Remove files on disk. - def destroy(name) + def destroy(request) if respond_to?(:path) - path = path(name) + path = path(request.key) else - path = name + path = request.key end - raise Puppet::Error.new("File %s does not exist; cannot destroy" % [name]) unless File.exist?(path) + raise Puppet::Error.new("File %s does not exist; cannot destroy" % [request.key]) unless File.exist?(path) begin File.unlink(path) rescue => detail - raise Puppet::Error, "Could not remove %s: %s" % [name, detail] + raise Puppet::Error, "Could not remove %s: %s" % [request.key, detail] end end # Return a model instance for a given file on disk. - def find(name) + def find(request) if respond_to?(:path) - path = path(name) + path = path(request.key) else - path = name + path = request.key end return nil unless File.exist?(path) @@ -38,20 +38,20 @@ class Puppet::Indirector::File < Puppet::Indirector::Terminus end # Save a new file to disk. - def save(file) + def save(request) if respond_to?(:path) - path = path(file.name) + path = path(request.key) else - path = file.path + path = request.key end dir = File.dirname(path) - raise Puppet::Error.new("Cannot save %s; parent directory %s does not exist" % [file, dir]) unless File.directory?(dir) + raise Puppet::Error.new("Cannot save %s; parent directory %s does not exist" % [request.key, dir]) unless File.directory?(dir) begin - File.open(path, "w") { |f| f.print file.content } + File.open(path, "w") { |f| f.print request.instance.content } rescue => detail - raise Puppet::Error, "Could not write %s: %s" % [file, detail] + raise Puppet::Error, "Could not write %s: %s" % [request.key, detail] end end end diff --git a/lib/puppet/indirector/file_metadata/file.rb b/lib/puppet/indirector/file_metadata/file.rb index b36846bbe..c46015c38 100644 --- a/lib/puppet/indirector/file_metadata/file.rb +++ b/lib/puppet/indirector/file_metadata/file.rb @@ -9,14 +9,14 @@ require 'puppet/indirector/direct_file_server' class Puppet::Indirector::FileMetadata::File < Puppet::Indirector::DirectFileServer desc "Retrieve file metadata directly from the local filesystem." - def find(key, options = {}) + def find(request) return unless data = super data.collect_attributes return data end - def search(key, options = {}) + def search(request) return unless result = super result.each { |instance| instance.collect_attributes } diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 1b6613035..56cd687af 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -126,6 +126,11 @@ class Puppet::Indirector::Indirection end end + # Set up our request object. + def request(key, method, arguments = nil) + Puppet::Indirector::Request.new(self.name, key, method, arguments) + end + # Return the singleton terminus for this indirection. def terminus(terminus_name = nil) # Get the name of the terminus. @@ -167,28 +172,27 @@ class Puppet::Indirector::Indirection end end - def find(key, *args) - request = request(key, :find, *args) + def find(request) terminus = prepare(request) # See if our instance is in the cache and up to date. - if cache? and cached = cache.find(key, *args) + if cache? and cached = cache.find(request) if cached.expired? - Puppet.info "Cached %s %s expired at %s; not using" % [self.name, key, cached.expiration] + Puppet.info "Cached %s %s expired at %s; not using" % [self.name, request.key, cached.expiration] else - Puppet.debug "Using cached %s %s" % [self.name, key] + Puppet.debug "Using cached %s %s" % [self.name, request.key] return cached end end # Otherwise, return the result from the terminus, caching if appropriate. - if result = terminus.find(key, *args) - # Include the envelope module, so we can set the expiration. - result.extend(Puppet::Indirector::Envelope) + if result = terminus.find(request) result.expiration ||= self.expiration if cache? - Puppet.info "Caching %s %s" % [self.name, key] - cache.save(result, *args) + Puppet.info "Caching %s %s" % [self.name, request.key] + cached_request = request.clone + cached_request.instance = result + cache.save(cached_request) end return result @@ -198,38 +202,35 @@ class Puppet::Indirector::Indirection end # Remove something via the terminus. - def destroy(key, *args) - request = request(key, :destroy, *args) + def destroy(request) terminus = prepare(request) - terminus.destroy(key, *args) + terminus.destroy(request) - if cache? and cached = cache.find(key, *args) - cache.destroy(key, *args) + if cache? and cached = cache.find(request) + cache.destroy(request) end nil end # Search for more than one instance. Should always return an array. - def search(key, *args) - request = request(key, :search, *args) + def search(request) terminus = prepare(request) - result = terminus.search(key, *args) + result = terminus.search(request) result end # Save the instance in the appropriate terminus. This method is # normally an instance method on the indirected class. - def save(instance, *args) - request = request(instance.name, :save, *args) + def save(request) terminus = prepare(request) # If caching is enabled, save our document there - cache.save(instance, *args) if cache? - terminus.save(instance, *args) + cache.save(request) if cache? + terminus.save(request) end private @@ -268,9 +269,4 @@ class Puppet::Indirector::Indirection end return klass.new end - - # Set up our request object. - def request(key, method, arguments = nil) - Puppet::Indirector::Request.new(self.name, key, method, arguments) - end end diff --git a/lib/puppet/indirector/memory.rb b/lib/puppet/indirector/memory.rb index b97e6ffb6..19acc14e2 100644 --- a/lib/puppet/indirector/memory.rb +++ b/lib/puppet/indirector/memory.rb @@ -6,16 +6,16 @@ class Puppet::Indirector::Memory < Puppet::Indirector::Terminus @instances = {} end - def destroy(name) - raise ArgumentError.new("Could not find %s to destroy" % name) unless @instances.include?(name) - @instances.delete(name) + def destroy(request) + raise ArgumentError.new("Could not find %s to destroy" % request.key) unless @instances.include?(request.key) + @instances.delete(request.key) end - def find(name) - @instances[name] + def find(request) + @instances[request.key] end - def save(instance) - @instances[instance.name] = instance + def save(request) + @instances[request.key] = request.instance end end diff --git a/lib/puppet/indirector/plain.rb b/lib/puppet/indirector/plain.rb index 8bdf8469c..2caa0946d 100644 --- a/lib/puppet/indirector/plain.rb +++ b/lib/puppet/indirector/plain.rb @@ -3,7 +3,7 @@ require 'puppet/indirector/terminus' # An empty terminus type, meant to just return empty objects. class Puppet::Indirector::Plain < Puppet::Indirector::Terminus # Just return nothing. - def find(name) - indirection.model.new(name) + def find(request) + indirection.model.new(request.key) end end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index 708862b28..68b7ee160 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -5,7 +5,7 @@ require 'puppet/indirector' class Puppet::Indirector::Request attr_accessor :indirection_name, :key, :method, :options, :instance - def initialize(indirection_name, key, method, options = {}) + def initialize(indirection_name, method, key, options = {}) @indirection_name, @method, @options = indirection_name, method, (options || {}) if key.is_a?(String) or key.is_a?(Symbol) diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb index 4dd29159e..23bca02b8 100644 --- a/lib/puppet/indirector/yaml.rb +++ b/lib/puppet/indirector/yaml.rb @@ -3,23 +3,22 @@ require 'puppet/indirector/terminus' # The base class for YAML indirection termini. class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus # Read a given name's file in and convert it from YAML. - def find(name) - raise ArgumentError.new("You must specify the name of the object to retrieve") unless name - file = path(name) + def find(request) + file = path(request.key) return nil unless FileTest.exist?(file) begin return from_yaml(File.read(file)) rescue => detail - raise Puppet::Error, "Could not read YAML data for %s %s: %s" % [indirection.name, name, detail] + raise Puppet::Error, "Could not read YAML data for %s %s: %s" % [indirection.name, request.key, detail] end end # Convert our object to YAML and store it to the disk. - def save(object) - raise ArgumentError.new("You can only save objects that respond to :name") unless object.respond_to?(:name) + def save(request) + raise ArgumentError.new("You can only save objects that respond to :name") unless request.instance.respond_to?(:name) - file = path(object.name) + file = path(request.key) basedir = File.dirname(file) @@ -29,15 +28,15 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus end begin - File.open(file, "w", 0660) { |f| f.print to_yaml(object) } + File.open(file, "w", 0660) { |f| f.print to_yaml(request.instance) } rescue TypeError => detail - Puppet.err "Could not save %s %s: %s" % [self.name, object.name, detail] + Puppet.err "Could not save %s %s: %s" % [self.name, request.key, detail] end end - def version(name) - return nil unless FileTest.exist?(path(name)) - return File.stat(path(name)).mtime + # Return the path to a given node's file. + def path(name) + File.join(Puppet[:yamldir], self.class.indirection_name.to_s, name.to_s + ".yaml") end private @@ -49,9 +48,4 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus def to_yaml(object) YAML.dump(object) end - - # Return the path to a given node's file. - def path(name) - File.join(Puppet[:yamldir], self.class.indirection_name.to_s, name.to_s + ".yaml") - end end diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 56f8a602a..bd62ebbe6 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -34,6 +34,10 @@ class Puppet::Transaction::Report end end + def name + host + end + # Create a new metric. def newmetric(name, hash) metric = Puppet::Util::Metric.new(name) diff --git a/spec/integration/indirector/direct_file_server.rb b/spec/integration/indirector/direct_file_server.rb index 383486986..40b753a6c 100755 --- a/spec/integration/indirector/direct_file_server.rb +++ b/spec/integration/indirector/direct_file_server.rb @@ -19,7 +19,7 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with the files it "should return an instance of the model" do FileTest.expects(:exists?).with(@filepath).returns(true) - @terminus.find("file://host#{@filepath}").should be_instance_of(Puppet::FileServing::Content) + @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}")).should be_instance_of(Puppet::FileServing::Content) end it "should return an instance capable of returning its content" do @@ -27,7 +27,7 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with the files File.stubs(:lstat).with(@filepath).returns(stub("stat", :ftype => "file")) File.expects(:read).with(@filepath).returns("my content") - instance = @terminus.find("file://host#{@filepath}") + instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}")) instance.content.should == "my content" end @@ -50,10 +50,12 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with FileServi end Dir.expects(:entries).with(@filepath).returns @subfiles + + @request = @terminus.indirection.request(:search, "file:///my/file", :recurse => true) end it "should return an instance for every file in the fileset" do - result = @terminus.search("file:///my/file", :recurse => true) + result = @terminus.search(@request) result.should be_instance_of(Array) result.length.should == 3 result.each { |r| r.should be_instance_of(Puppet::FileServing::Content) } @@ -65,7 +67,7 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with FileServi File.expects(:read).with(File.join(@filepath, name)).returns("#{name} content") end - @terminus.search("file:///my/file", :recurse => true).each do |instance| + @terminus.search(@request).each do |instance| case instance.key when /one/: instance.content.should == "one content" when /two/: instance.content.should == "two content" diff --git a/spec/integration/node/catalog.rb b/spec/integration/node/catalog.rb new file mode 100755 index 000000000..d0ddfd8aa --- /dev/null +++ b/spec/integration/node/catalog.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-10-18. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Node::Catalog do + describe "when using the indirector" do + after { Puppet::Node::Catalog.indirection.clear_cache } + + it "should be able to delegate to the :yaml terminus" do + Puppet::Node::Catalog.indirection.stubs(:terminus_class).returns :yaml + + # Load now, before we stub the exists? method. + Puppet::Node::Catalog.indirection.terminus(:yaml) + + file = File.join(Puppet[:yamldir], "catalog", "me.yaml") + FileTest.expects(:exist?).with(file).returns false + Puppet::Node::Catalog.find("me").should be_nil + end + + it "should be able to delegate to the :compiler terminus" do + Puppet::Node::Catalog.indirection.stubs(:terminus_class).returns :compiler + + # Load now, before we stub the exists? method. + compiler = Puppet::Node::Catalog.indirection.terminus(:compiler) + + compiler.expects(:compile).with("me").returns nil + + Puppet::Node::Catalog.find("me").should be_nil + end + end +end diff --git a/spec/shared_behaviours/file_serving.rb b/spec/shared_behaviours/file_serving.rb index b5ab6b0fd..82f207243 100644 --- a/spec/shared_behaviours/file_serving.rb +++ b/spec/shared_behaviours/file_serving.rb @@ -6,7 +6,7 @@ describe "Puppet::FileServing::Files", :shared => true do it "should use the rest terminus when the 'puppet' URI scheme is used and a host name is present" do uri = "puppet://myhost/mymod/my/file" - @indirection.terminus(:rest).expects(:find).with(uri) + @indirection.terminus(:rest).expects(:find) @test_class.find(uri) end @@ -14,7 +14,7 @@ describe "Puppet::FileServing::Files", :shared => true do uri = "puppet:///mymod/my/file" Puppet.settings.stubs(:value).with(:name).returns("puppetd") Puppet.settings.stubs(:value).with(:modulepath).returns("") - @indirection.terminus(:rest).expects(:find).with(uri) + @indirection.terminus(:rest).expects(:find) @test_class.find(uri) end @@ -27,27 +27,27 @@ describe "Puppet::FileServing::Files", :shared => true do Puppet.settings.stubs(:value).with(:libdir).returns("") Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever") Puppet.settings.stubs(:value).with(:environment).returns("") - @indirection.terminus(:file_server).expects(:find).with(uri) + @indirection.terminus(:file_server).expects(:find) @indirection.terminus(:file_server).stubs(:authorized?).returns(true) @test_class.find(uri) end it "should use the file_server terminus when the 'puppetmounts' URI scheme is used" do uri = "puppetmounts:///mymod/my/file" - @indirection.terminus(:file_server).expects(:find).with(uri) + @indirection.terminus(:file_server).expects(:find) @indirection.terminus(:file_server).stubs(:authorized?).returns(true) @test_class.find(uri) end it "should use the file terminus when the 'file' URI scheme is used" do uri = "file:///mymod/my/file" - @indirection.terminus(:file).expects(:find).with(uri) + @indirection.terminus(:file).expects(:find) @test_class.find(uri) end it "should use the file terminus when a fully qualified path is provided" do uri = "/mymod/my/file" - @indirection.terminus(:file).expects(:find).with(uri) + @indirection.terminus(:file).expects(:find) @test_class.find(uri) end end diff --git a/spec/shared_behaviours/memory_terminus.rb b/spec/shared_behaviours/memory_terminus.rb new file mode 100644 index 000000000..a00dc9f74 --- /dev/null +++ b/spec/shared_behaviours/memory_terminus.rb @@ -0,0 +1,32 @@ +# +# Created by Luke Kanies on 2008-4-8. +# Copyright (c) 2008. All rights reserved. + +describe "A Memory Terminus", :shared => true do + it "should find no instances by default" do + @searcher.find(@request).should be_nil + end + + it "should be able to find instances that were previously saved" do + @searcher.save(@request) + @searcher.find(@request).should equal(@instance) + end + + it "should replace existing saved instances when a new instance with the same name is saved" do + @searcher.save(@request) + two = stub 'second', :name => @name + trequest = stub 'request', :key => @name, :instance => two + @searcher.save(trequest) + @searcher.find(@request).should equal(two) + end + + it "should be able to remove previously saved instances" do + @searcher.save(@request) + @searcher.destroy(@request) + @searcher.find(@request).should be_nil + end + + it "should fail when asked to destroy an instance that does not exist" do + proc { @searcher.destroy(@request) }.should raise_error(ArgumentError) + end +end diff --git a/spec/unit/indirector.rb b/spec/unit/indirector.rb index 1a5867c51..892e70c1f 100755 --- a/spec/unit/indirector.rb +++ b/spec/unit/indirector.rb @@ -21,6 +21,10 @@ describe Puppet::Indirector, "when registering an indirection" do before do @thingie = Class.new do extend Puppet::Indirector + attr_reader :name + def initialize(name) + @name = name + end end end @@ -55,48 +59,92 @@ describe Puppet::Indirector, "when registering an indirection" do end end -describe Puppet::Indirector, " when redirecting a model" do +describe "Delegated Indirection Method", :shared => true do + it "should create an indirection request with the indirection name, the method being delegated, and all of the arguments to the method call" do + Puppet::Indirector::Request.expects(:new).with(@indirection.name, @method, "me", :one => :two) + @indirection.stubs(@method) + @thingie.send(@method, "me", :one => :two) + end + + it "should delegate to the indirection" do + @indirection.expects(@method) + @thingie.send(@method, "me") + end + + it "should pass the indirection's request instance to the indirection's method" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + @indirection.expects(@method).with(request) + @thingie.send(@method, "me") + end + + it "should return the results of the delegation as its result" do + request = mock 'request' + @indirection.expects(@method).returns "yay" + @thingie.send(@method, "me").should == "yay" + end +end + +describe Puppet::Indirector, "when redirecting a model" do before do @thingie = Class.new do extend Puppet::Indirector + attr_reader :name + def initialize(name) + @name = name + end end @indirection = @thingie.send(:indirects, :test) end - it "should give the model the ability set a version" do - thing = @thingie.new - thing.should respond_to(:version=) + it "should include the Envelope module in the model" do + @thingie.ancestors.should be_include(Puppet::Indirector::Envelope) end - it "should give the model the ability retrieve a version" do - thing = @thingie.new - thing.should respond_to(:version) + describe "when finding instances via the model" do + before { @method = :find } + it_should_behave_like "Delegated Indirection Method" end - it "should give the model the ability to lookup a model instance by letting the indirection perform the lookup" do - @indirection.expects(:find) - @thingie.find + describe "when destroying instances via the model" do + before { @method = :destroy } + it_should_behave_like "Delegated Indirection Method" end - it "should give the model the ability to remove model instances from a terminus by letting the indirection remove the instance" do - @indirection.expects(:destroy) - @thingie.destroy + describe "when searching for instances via the model" do + before { @method = :search } + it_should_behave_like "Delegated Indirection Method" end - it "should give the model the ability to search for model instances by letting the indirection find the matching instances" do - @indirection.expects(:search) - @thingie.search - end + # This is an instance method, so it behaves a bit differently. + describe "when saving instances via the model" do + before do + @instance = @thingie.new("me") + end - it "should give the model the ability to store a model instance by letting the indirection store the instance" do - thing = @thingie.new - @indirection.expects(:save).with(thing) - thing.save - end + it "should pass the method name, the instance, plus all passed arguments to the indirection's request method" do + Puppet::Indirector::Request.expects(:new).with(@indirection.name, :save, @instance, :one => :two) + @indirection.stubs(:save) + @instance.save(:one => :two) + end - it "should give the model the ability to look up an instance's version by letting the indirection perform the lookup" do - @indirection.expects(:version).with(:thing) - @thingie.version(:thing) + it "should delegate to the indirection" do + @indirection.expects(:save) + @instance.save + end + + it "should pass the indirection's request instance to the indirection's method" do + request = mock 'request' + Puppet::Indirector::Request.expects(:new).returns request + @indirection.expects(:save).with(request) + @instance.save + end + + it "should return the results of the delegation as its result" do + request = mock 'request' + @indirection.expects(:save).returns "yay" + @instance.save.should == "yay" + end end it "should give the model the ability to set the indirection terminus class" do diff --git a/spec/unit/indirector/catalog/compiler.rb b/spec/unit/indirector/catalog/compiler.rb index a4a0acd58..5a26302d1 100755 --- a/spec/unit/indirector/catalog/compiler.rb +++ b/spec/unit/indirector/catalog/compiler.rb @@ -75,7 +75,9 @@ describe Puppet::Node::Catalog::Compiler, " when finding nodes" do it "should fail if it cannot find the node" do @node.stubs :merge Puppet::Node.expects(:find_by_any_name).with(@name).returns(nil) - proc { @compiler.find(@name) }.should raise_error(Puppet::Error) + request = stub 'request', :key => @name + @compiler.find(request) + proc { @compiler.find(request) }.should raise_error(Puppet::Error) end end diff --git a/spec/unit/indirector/checksum/file.rb b/spec/unit/indirector/checksum/file.rb index 64425904f..857d7b050 100755 --- a/spec/unit/indirector/checksum/file.rb +++ b/spec/unit/indirector/checksum/file.rb @@ -38,6 +38,8 @@ describe Puppet::Checksum::File do Puppet.stubs(:[]).with(:bucketdir).returns(@dir) @path = @store.path(@value) + + @request = stub 'request', :key => @value end @@ -76,7 +78,7 @@ describe Puppet::Checksum::File do # The smallest test that will use the calculated path it "should look for the calculated path" do File.expects(:exist?).with(@path).returns(false) - @store.find(@value) + @store.find(@request) end it "should return an instance of Puppet::Checksum created with the content if the file exists" do @@ -87,18 +89,18 @@ describe Puppet::Checksum::File do File.expects(:exist?).with(@path).returns(true) File.expects(:read).with(@path).returns(content) - @store.find(@value).should equal(sum) + @store.find(@request).should equal(sum) end it "should return nil if no file is found" do File.expects(:exist?).with(@path).returns(false) - @store.find(@value).should be_nil + @store.find(@request).should be_nil end it "should fail intelligently if a found file cannot be read" do File.expects(:exist?).with(@path).returns(true) File.expects(:read).with(@path).raises(RuntimeError) - proc { @store.find(@value) }.should raise_error(Puppet::Error) + proc { @store.find(@request) }.should raise_error(Puppet::Error) end end @@ -112,7 +114,7 @@ describe Puppet::Checksum::File do File.expects(:open).with(@path, "w") file = stub 'file', :name => @value - @store.save(file) + @store.save(@request) end it "should make any directories necessary for storage" do @@ -122,8 +124,7 @@ describe Puppet::Checksum::File do File.expects(:directory?).with(File.dirname(@path)).returns(true) File.expects(:open).with(@path, "w") - file = stub 'file', :name => @value - @store.save(file) + @store.save(@request) end end @@ -132,7 +133,7 @@ describe Puppet::Checksum::File do File.expects(:exist?).with(@path).returns(true) File.expects(:unlink).with(@path) - @store.destroy(@value) + @store.destroy(@request) end end end diff --git a/spec/unit/indirector/direct_file_server.rb b/spec/unit/indirector/direct_file_server.rb index a89b938e7..a8583716a 100755 --- a/spec/unit/indirector/direct_file_server.rb +++ b/spec/unit/indirector/direct_file_server.rb @@ -23,19 +23,21 @@ describe Puppet::Indirector::DirectFileServer do @server = @direct_file_class.new @uri = "file:///my/local" + + @request = stub 'request', :key => @uri, :options => {} end describe Puppet::Indirector::DirectFileServer, "when finding a single file" do it "should return nil if the file does not exist" do FileTest.expects(:exists?).with("/my/local").returns false - @server.find(@uri).should be_nil + @server.find(@request).should be_nil end it "should return a Content instance created with the full path to the file if the file exists" do FileTest.expects(:exists?).with("/my/local").returns true @model.expects(:new).returns(:mycontent) - @server.find(@uri).should == :mycontent + @server.find(@request).should == :mycontent end end @@ -49,18 +51,20 @@ describe Puppet::Indirector::DirectFileServer do it "should create the Content instance with the original key as the key" do @model.expects(:new).with { |key, options| key == @uri }.returns(@data) - @server.find(@uri) + @server.find(@request) end it "should pass the full path to the instance" do @model.expects(:new).with { |key, options| options[:path] == "/my/local" }.returns(@data) - @server.find(@uri) + @server.find(@request) end it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do @model.expects(:new).returns(@data) @data.expects(:links=).with(:manage) - @server.find(@uri, :links => :manage) + + @request.stubs(:options).returns(:links => :manage) + @server.find(@request) end end @@ -68,25 +72,27 @@ describe Puppet::Indirector::DirectFileServer do it "should return nil if the file does not exist" do FileTest.expects(:exists?).with("/my/local").returns false - @server.find(@uri).should be_nil + @server.find(@request).should be_nil end it "should pass the original key to :path2instances" do FileTest.expects(:exists?).with("/my/local").returns true @server.expects(:path2instances).with { |uri, path, options| uri == @uri } - @server.search(@uri) + @server.search(@request) end it "should use :path2instances from the terminus_helper to return instances if the file exists" do FileTest.expects(:exists?).with("/my/local").returns true @server.expects(:path2instances) - @server.search(@uri) + @server.search(@request) end it "should pass any options on to :path2instances" do FileTest.expects(:exists?).with("/my/local").returns true @server.expects(:path2instances).with { |uri, path, options| options == {:testing => :one, :other => :two}} - @server.search(@uri, :testing => :one, :other => :two) + + @request.stubs(:options).returns(:testing => :one, :other => :two) + @server.search(@request) end end end diff --git a/spec/unit/indirector/file.rb b/spec/unit/indirector/file.rb index fa10743ef..67ead4cdb 100755 --- a/spec/unit/indirector/file.rb +++ b/spec/unit/indirector/file.rb @@ -21,6 +21,8 @@ describe Puppet::Indirector::File do @path = "/my/file" @dir = "/my" + + @request = stub 'request', :key => @path end describe Puppet::Indirector::File, " when finding files" do @@ -37,7 +39,7 @@ describe Puppet::Indirector::File do File.expects(:exist?).with(@path).returns(true) File.expects(:read).with(@path).returns(content) - @searcher.find(@path) + @searcher.find(@request) end it "should create the model instance with the content as the only argument to initialization" do @@ -48,18 +50,18 @@ describe Puppet::Indirector::File do File.expects(:exist?).with(@path).returns(true) File.expects(:read).with(@path).returns(content) - @searcher.find(@path).should equal(file) + @searcher.find(@request).should equal(file) end it "should return nil if no file is found" do File.expects(:exist?).with(@path).returns(false) - @searcher.find(@path).should be_nil + @searcher.find(@request).should be_nil end it "should fail intelligently if a found file cannot be read" do File.expects(:exist?).with(@path).returns(true) File.expects(:read).with(@path).raises(RuntimeError) - proc { @searcher.find(@path) }.should raise_error(Puppet::Error) + proc { @searcher.find(@request) }.should raise_error(Puppet::Error) end it "should use the path() method to calculate the path if it exists" do @@ -68,42 +70,39 @@ describe Puppet::Indirector::File do end File.expects(:exist?).with(@path.upcase).returns(false) - @searcher.find(@path) + @searcher.find(@request) end end describe Puppet::Indirector::File, " when saving files" do + before do + @content = "my content" + @file = stub 'file', :content => @content, :path => @path, :name => @path + @request.stubs(:instance).returns @file + end it "should provide a method to save file contents at a specified path" do filehandle = mock 'file' - content = "my content" File.expects(:directory?).with(@dir).returns(true) File.expects(:open).with(@path, "w").yields(filehandle) - filehandle.expects(:print).with(content) + filehandle.expects(:print).with(@content) - file = stub 'file', :content => content, :path => @path, :name => @path - - @searcher.save(file) + @searcher.save(@request) end it "should fail intelligently if the file's parent directory does not exist" do File.expects(:directory?).with(@dir).returns(false) - file = stub 'file', :path => @path, :name => @path - - proc { @searcher.save(file) }.should raise_error(Puppet::Error) + proc { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should fail intelligently if a file cannot be written" do filehandle = mock 'file' - content = "my content" File.expects(:directory?).with(@dir).returns(true) File.expects(:open).with(@path, "w").yields(filehandle) - filehandle.expects(:print).with(content).raises(ArgumentError) + filehandle.expects(:print).with(@content).raises(ArgumentError) - file = stub 'file', :content => content, :path => @path, :name => @path - - proc { @searcher.save(file) }.should raise_error(Puppet::Error) + proc { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should use the path() method to calculate the path if it exists" do @@ -111,10 +110,11 @@ describe Puppet::Indirector::File do name.upcase end - file = stub 'file', :name => "/yay" + # Reset the key to something without a parent dir, so no checks are necessary + @request.stubs(:key).returns "/my" - File.expects(:open).with("/YAY", "w") - @searcher.save(file) + File.expects(:open).with("/MY", "w") + @searcher.save(@request) end end @@ -124,20 +124,20 @@ describe Puppet::Indirector::File do File.expects(:exist?).with(@path).returns(true) File.expects(:unlink).with(@path) - @searcher.destroy(@path) + @searcher.destroy(@request) end it "should throw an exception if the file is not found" do File.expects(:exist?).with(@path).returns(false) - proc { @searcher.destroy(@path) }.should raise_error(Puppet::Error) + proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) end it "should fail intelligently if the file cannot be removed" do File.expects(:exist?).with(@path).returns(true) File.expects(:unlink).with(@path).raises(ArgumentError) - proc { @searcher.destroy(@path) }.should raise_error(Puppet::Error) + proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) end it "should use the path() method to calculate the path if it exists" do @@ -148,7 +148,7 @@ describe Puppet::Indirector::File do File.expects(:exist?).with("/MY/FILE").returns(true) File.expects(:unlink).with("/MY/FILE") - @searcher.destroy(@path) + @searcher.destroy(@request) end end end diff --git a/spec/unit/indirector/file_metadata/file.rb b/spec/unit/indirector/file_metadata/file.rb index 0a37a6895..9474620c7 100755 --- a/spec/unit/indirector/file_metadata/file.rb +++ b/spec/unit/indirector/file_metadata/file.rb @@ -15,34 +15,38 @@ describe Puppet::Indirector::FileMetadata::File do it "should be a subclass of the DirectFileServer terminus" do Puppet::Indirector::FileMetadata::File.superclass.should equal(Puppet::Indirector::DirectFileServer) end -end -describe Puppet::Indirector::FileMetadata::File, "when creating the instance for a single found file" do - before do - @metadata = Puppet::Indirector::FileMetadata::File.new - @uri = "file:///my/local" - @data = mock 'metadata' - @data.stubs(:collect_attributes) - FileTest.expects(:exists?).with("/my/local").returns true + describe "when creating the instance for a single found file" do + before do + @metadata = Puppet::Indirector::FileMetadata::File.new + @uri = "file:///my/local" + @data = mock 'metadata' + @data.stubs(:collect_attributes) + FileTest.expects(:exists?).with("/my/local").returns true + + @request = stub 'request', :key => @uri, :options => {} + end + + it "should collect its attributes when a file is found" do + @data.expects(:collect_attributes) + + Puppet::FileServing::Metadata.expects(:new).returns(@data) + @metadata.find(@request).should == @data + end end - it "should collect its attributes when a file is found" do - @data.expects(:collect_attributes) + describe "when searching for multiple files" do + before do + @metadata = Puppet::Indirector::FileMetadata::File.new + @uri = "file:///my/local" - Puppet::FileServing::Metadata.expects(:new).returns(@data) - @metadata.find(@uri).should == @data - end -end - -describe Puppet::Indirector::FileMetadata::File, "when searching for multiple files" do - before do - @metadata = Puppet::Indirector::FileMetadata::File.new - @uri = "file:///my/local" - end - - it "should collect the attributes of the instances returned" do - FileTest.expects(:exists?).with("/my/local").returns true - @metadata.expects(:path2instances).returns( [mock("one", :collect_attributes => nil), mock("two", :collect_attributes => nil)] ) - @metadata.search(@uri) + @request = stub 'request', :key => @uri, :options => {} + end + + it "should collect the attributes of the instances returned" do + FileTest.expects(:exists?).with("/my/local").returns true + @metadata.expects(:path2instances).returns( [mock("one", :collect_attributes => nil), mock("two", :collect_attributes => nil)] ) + @metadata.search(@request) + end end end diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index 8768076c6..f33444bbf 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -55,6 +55,8 @@ describe Puppet::Indirector::Indirection do @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" @name = :mything + + @request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} end it "should allow setting the ttl" do @@ -73,57 +75,67 @@ describe Puppet::Indirector::Indirection do Time.stubs(:now).returns now @indirection.expiration.should == (Time.now + 100) end - - describe "and looking for a model instance" do - it "should create a request with the indirection name, the sought-after name, the :find method, and any passed arguments" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).with(@indirection.name, @name, :find, {:one => :two}).returns request - @indirection.stubs(:check_authorization) - @terminus.stubs(:find) + it "should have a method for creating an indirection request instance" do + @indirection.should respond_to(:request) + end - @indirection.find(@name, :one => :two) + describe "creates a request" do + it "should create it with its name as the request's indirection name" do + Puppet::Indirector::Request.expects(:new).with { |name, *other| @indirection.name == name } + @indirection.request(:funtest, "yayness") end - it "should let the :select_terminus method choose the terminus if the method is defined" do + it "should require a method and key" do + Puppet::Indirector::Request.expects(:new).with { |name, method, key, *other| method == :funtest and key == "yayness" } + @indirection.request(:funtest, "yayness") + end + + it "should support optional arguments" do + Puppet::Indirector::Request.expects(:new).with { |name, method, key, other| other == {:one => :two} } + @indirection.request(:funtest, "yayness", :one => :two) + end + + it "should default to the arguments being nil" do + Puppet::Indirector::Request.expects(:new).with { |name, method, key, args| args.nil? } + @indirection.request(:funtest, "yayness") + end + + it "should return the request" do request = mock 'request' Puppet::Indirector::Request.expects(:new).returns request - + @indirection.request(:funtest, "yayness").should equal(request) + end + end + + describe "and looking for a model instance" do + it "should let the :select_terminus method choose the terminus if the method is defined" do # Define the method, so our respond_to? hook matches. class << @indirection def select_terminus(request) end end - @indirection.expects(:select_terminus).with(request).returns :test_terminus + @indirection.expects(:select_terminus).with(@request).returns :test_terminus @indirection.stubs(:check_authorization) @terminus.expects(:find) - @indirection.find(@name) + @indirection.find(@request) end it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:find).with(@name).returns(@instance) - @indirection.find(@name).should == @instance + @terminus.expects(:find).with(@request).returns(@instance) + @indirection.find(@request).should == @instance end it "should return nil if nothing is returned by the terminus" do - @terminus.expects(:find).with(@name).returns(nil) - @indirection.find(@name).should be_nil - end - - it "should extend any found instance with the Envelope module" do - @terminus.stubs(:find).returns(@instance) - - @instance.expects(:extend).with(Puppet::Indirector::Envelope) - @indirection.find(@name) + @terminus.expects(:find).with(@request).returns(nil) + @indirection.find(@request).should be_nil end it "should set the expiration date on any instances without one set" do - # Otherwise, our stub method doesn't get used, so the tests fail. - @instance.stubs(:extend) @terminus.stubs(:find).returns(@instance) @indirection.expects(:expiration).returns :yay @@ -131,12 +143,10 @@ describe Puppet::Indirector::Indirection do @instance.expects(:expiration).returns(nil) @instance.expects(:expiration=).with(:yay) - @indirection.find(@name) + @indirection.find(@request) end it "should not override an already-set expiration date on returned instances" do - # Otherwise, our stub method doesn't get used, so the tests fail. - @instance.stubs(:extend) @terminus.stubs(:find).returns(@instance) @indirection.expects(:expiration).never @@ -144,7 +154,7 @@ describe Puppet::Indirector::Indirection do @instance.expects(:expiration).returns(:yay) @instance.expects(:expiration=).never - @indirection.find(@name) + @indirection.find(@request) end describe "when caching is enabled" do @@ -157,23 +167,23 @@ describe Puppet::Indirector::Indirection do it "should first look in the cache for an instance" do @terminus.expects(:find).never - @cache.expects(:find).with(@name).returns @instance + @cache.expects(:find).with(@request).returns @instance - @indirection.find(@name) + @indirection.find(@request) end it "should return the cached object if it is not expired" do @instance.stubs(:expired?).returns false @cache.stubs(:find).returns @instance - @indirection.find(@name).should equal(@instance) + @indirection.find(@request).should equal(@instance) end it "should send a debug log if it is using the cached object" do Puppet.expects(:debug) @cache.stubs(:find).returns @instance - @indirection.find(@name) + @indirection.find(@request) end it "should not return the cached object if it is expired" do @@ -181,7 +191,7 @@ describe Puppet::Indirector::Indirection do @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil - @indirection.find(@name).should be_nil + @indirection.find(@request).should be_nil end it "should send an info log if it is using the cached object" do @@ -190,63 +200,67 @@ describe Puppet::Indirector::Indirection do @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil - @indirection.find(@name) + @indirection.find(@request) end it "should cache any objects not retrieved from the cache" do - @cache.expects(:find).with(@name).returns nil + @cache.expects(:find).with(@request).returns nil - @terminus.expects(:find).with(@name).returns(@instance) - @cache.expects(:save).with(@instance) + @terminus.expects(:find).with(@request).returns(@instance) + @cache.expects(:save) - @indirection.find(@name) + @request.expects(:clone).returns(stub('newreq', :instance= => nil)) + + @indirection.find(@request) + end + + it "should cache a clone of the request with the instance set to the cached object" do + @cache.expects(:find).with(@request).returns nil + + newreq = mock 'request' + @request.expects(:clone).returns newreq + newreq.expects(:instance=).with(@instance) + + @terminus.expects(:find).with(@request).returns(@instance) + @cache.expects(:save).with(newreq) + + @indirection.find(@request) end it "should send an info log that the object is being cached" do @cache.stubs(:find).returns nil + @request.expects(:clone).returns(stub('newreq', :instance= => nil)) + @terminus.stubs(:find).returns(@instance) @cache.stubs(:save) Puppet.expects(:info) - @indirection.find(@name) + @indirection.find(@request) end end end describe "and storing a model instance" do - it "should create a request with the indirection name, the instance's name, the :save method, and any passed arguments" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).with(@indirection.name, @instance.name, :save, {:one => :two}).returns request - - @indirection.stubs(:check_authorization) - @terminus.stubs(:save) - - @indirection.save(@instance, :one => :two) - end - it "should let the :select_terminus method choose the terminus if the method is defined" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).returns request - # Define the method, so our respond_to? hook matches. class << @indirection - def select_terminus(request) + def select_terminus(req) end end - @indirection.expects(:select_terminus).with(request).returns :test_terminus + @indirection.expects(:select_terminus).with(@request).returns :test_terminus @indirection.stubs(:check_authorization) @terminus.expects(:save) - @indirection.save(@instance) + @indirection.save(@request) end it "should let the appropriate terminus store the instance" do - @terminus.expects(:save).with(@instance).returns(@instance) - @indirection.save(@instance).should == @instance + @terminus.expects(:save).with(@request).returns(@instance) + @indirection.save(@request).should == @instance end describe "when caching is enabled" do @@ -258,50 +272,37 @@ describe Puppet::Indirector::Indirection do end it "should save the object to the cache" do - @cache.expects(:save).with(@instance) + @cache.expects(:save).with(@request) @terminus.stubs(:save) - @indirection.save(@instance) + @indirection.save(@request) end end end describe "and removing a model instance" do - it "should create a request with the indirection name, the name of the instance being destroyed, the :destroy method, and any passed arguments" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).with(@indirection.name, "me", :destroy, {:one => :two}).returns request - - @indirection.stubs(:check_authorization) - @terminus.stubs(:destroy) - - @indirection.destroy("me", :one => :two) - end - it "should let the :select_terminus method choose the terminus if the method is defined" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).returns request - # Define the method, so our respond_to? hook matches. class << @indirection def select_terminus(request) end end - @indirection.expects(:select_terminus).with(request).returns :test_terminus + @indirection.expects(:select_terminus).with(@request).returns :test_terminus @indirection.stubs(:check_authorization) @terminus.expects(:destroy) - @indirection.destroy(@name) + @indirection.destroy(@request) end it "should delegate the instance removal to the appropriate terminus" do - @terminus.expects(:destroy).with(@name) - @indirection.destroy(@name) + @terminus.expects(:destroy).with(@request) + @indirection.destroy(@request) end it "should return nil" do @terminus.stubs(:destroy) - @indirection.destroy(@name).should be_nil + @indirection.destroy(@request).should be_nil end describe "when caching is enabled" do @@ -314,47 +315,34 @@ describe Puppet::Indirector::Indirection do it "should destroy any found object in the cache" do cached = mock 'cache' - @cache.expects(:find).with(@name).returns cached - @cache.expects(:destroy).with(@name) + @cache.expects(:find).with(@request).returns cached + @cache.expects(:destroy).with(@request) @terminus.stubs(:destroy) - @indirection.destroy(@name) + @indirection.destroy(@request) end end end describe "and searching for multiple model instances" do - it "should create a request with the indirection name, the search key, the :search method, and any passed arguments" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).with(@indirection.name, "me", :search, {:one => :two}).returns request - - @indirection.stubs(:check_authorization) - @terminus.stubs(:search) - - @indirection.search("me", :one => :two) - end - it "should let the :select_terminus method choose the terminus if the method is defined" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).returns request - # Define the method, so our respond_to? hook matches. class << @indirection - def select_terminus(request) + def select_terminus(req) end end - @indirection.expects(:select_terminus).with(request).returns :test_terminus + @indirection.expects(:select_terminus).with(@request).returns :test_terminus @indirection.stubs(:check_authorization) @terminus.expects(:search) - @indirection.search("me") + @indirection.search(@request) end it "should let the appropriate terminus find the matching instances" do - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name).should == @instance + @terminus.expects(:search).with(@request).returns(@instance) + @indirection.search(@request).should == @instance end end @@ -365,69 +353,72 @@ describe Puppet::Indirector::Indirection do def authorized? end end + + @request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {:node => "mynode"} end it "should not check authorization if a node name is not provided" do @terminus.expects(:authorized?).never @terminus.stubs(:find) - @indirection.find("/my/key") + + # The quotes are necessary here, else it looks like a block. + @request.stubs(:options).returns({}) + @indirection.find(@request) end it "should pass the request to the terminus's authorization method" do - request = stub 'request', :options => {:node => "yayhost"} - Puppet::Indirector::Request.expects(:new).returns(request) - @terminus.expects(:authorized?).with(request).returns(true) + @terminus.expects(:authorized?).with(@request).returns(true) @terminus.stubs(:find) - @indirection.find("/my/key", :node => "mynode") + @indirection.find(@request) end it "should fail while finding instances if authorization returns false" do @terminus.expects(:authorized?).returns(false) @terminus.stubs(:find) - proc { @indirection.find("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + proc { @indirection.find(@request) }.should raise_error(ArgumentError) end it "should continue finding instances if authorization returns true" do @terminus.expects(:authorized?).returns(true) @terminus.stubs(:find) - @indirection.find("/my/key", :node => "mynode") + @indirection.find(@request) end it "should fail while saving instances if authorization returns false" do @terminus.expects(:authorized?).returns(false) @terminus.stubs(:save) - proc { @indirection.save(@instance, :node => "mynode") }.should raise_error(ArgumentError) + proc { @indirection.save(@request) }.should raise_error(ArgumentError) end it "should continue saving instances if authorization returns true" do @terminus.expects(:authorized?).returns(true) @terminus.stubs(:save) - @indirection.save(@instance, :node => "mynode") + @indirection.save(@request) end it "should fail while destroying instances if authorization returns false" do @terminus.expects(:authorized?).returns(false) @terminus.stubs(:destroy) - proc { @indirection.destroy("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + proc { @indirection.destroy(@request) }.should raise_error(ArgumentError) end it "should continue destroying instances if authorization returns true" do @terminus.expects(:authorized?).returns(true) @terminus.stubs(:destroy) - @indirection.destroy(@instance, :node => "mynode") + @indirection.destroy(@request) end it "should fail while searching for instances if authorization returns false" do @terminus.expects(:authorized?).returns(false) @terminus.stubs(:search) - proc { @indirection.search("/my/key", :node => "mynode") }.should raise_error(ArgumentError) + proc { @indirection.search(@request) }.should raise_error(ArgumentError) end it "should continue searching for instances if authorization returns true" do @terminus.expects(:authorized?).returns(true) @terminus.stubs(:search) - @indirection.search("/my/key", :node => "mynode") + @indirection.search(@request) end end diff --git a/spec/unit/indirector/memory.rb b/spec/unit/indirector/memory.rb index 2e9a7f8f3..3b754a1eb 100755 --- a/spec/unit/indirector/memory.rb +++ b/spec/unit/indirector/memory.rb @@ -3,33 +3,7 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/memory' -describe "A Memory Terminus", :shared => true do - it "should find no instances by default" do - @searcher.find(@name).should be_nil - end - - it "should be able to find instances that were previously saved" do - @searcher.save(@instance) - @searcher.find(@name).should equal(@instance) - end - - it "should replace existing saved instances when a new instance with the same name is saved" do - @searcher.save(@instance) - two = stub 'second', :name => @name - @searcher.save(two) - @searcher.find(@name).should equal(two) - end - - it "should be able to remove previously saved instances" do - @searcher.save(@instance) - @searcher.destroy(@instance.name) - @searcher.find(@name).should be_nil - end - - it "should fail when asked to destroy an instance that does not exist" do - proc { @searcher.destroy(@instance) }.should raise_error(ArgumentError) - end -end +require 'shared_behaviours/memory_terminus' describe Puppet::Indirector::Memory do it_should_behave_like "A Memory Terminus" @@ -49,5 +23,7 @@ describe Puppet::Indirector::Memory do @searcher = @memory_class.new @name = "me" @instance = stub 'instance', :name => @name + + @request = stub 'request', :key => @name, :instance => @instance end end diff --git a/spec/unit/indirector/node/memory.rb b/spec/unit/indirector/node/memory.rb index a924c6209..71e01d4f3 100755 --- a/spec/unit/indirector/node/memory.rb +++ b/spec/unit/indirector/node/memory.rb @@ -4,14 +4,15 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/node/memory' -# All of our behaviour is described here, so we always have to include it. -require File.dirname(__FILE__) + '/../memory' +require 'shared_behaviours/memory_terminus' describe Puppet::Node::Memory do before do @name = "me" @searcher = Puppet::Node::Memory.new @instance = stub 'instance', :name => @name + + @request = stub 'request', :key => @name, :instance => @instance end it_should_behave_like "A Memory Terminus" diff --git a/spec/unit/indirector/plain.rb b/spec/unit/indirector/plain.rb index 1277739af..aca2816f2 100755 --- a/spec/unit/indirector/plain.rb +++ b/spec/unit/indirector/plain.rb @@ -17,11 +17,13 @@ describe Puppet::Indirector::Plain do end @searcher = @plain_class.new + + @request = stub 'request', :key => "yay" end it "should return return an instance of the indirected model" do object = mock 'object' - @model.expects(:new).with("yay").returns object - @searcher.find("yay").should equal(object) + @model.expects(:new).with(@request.key).returns object + @searcher.find(@request).should equal(object) end end diff --git a/spec/unit/indirector/request.rb b/spec/unit/indirector/request.rb index fd57c5297..cdb40b181 100755 --- a/spec/unit/indirector/request.rb +++ b/spec/unit/indirector/request.rb @@ -10,45 +10,45 @@ describe Puppet::Indirector::Request do end it "should use provided value as the key if it is a string" do - Puppet::Indirector::Request.new(:ind, "mykey", :method).key.should == "mykey" + Puppet::Indirector::Request.new(:ind, :method, "mykey").key.should == "mykey" end it "should use provided value as the key if it is a symbol" do - Puppet::Indirector::Request.new(:ind, :mykey, :method).key.should == :mykey + Puppet::Indirector::Request.new(:ind, :method, :mykey).key.should == :mykey end it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do instance = mock 'instance', :name => "mykey" - request = Puppet::Indirector::Request.new(:ind, instance, :method) + request = Puppet::Indirector::Request.new(:ind, :method, instance) request.key.should == "mykey" request.instance.should equal(instance) end it "should support options specified as a hash" do - lambda { Puppet::Indirector::Request.new(:ind, :key, :method, :one => :two) }.should_not raise_error(ArgumentError) + lambda { Puppet::Indirector::Request.new(:ind, :method, :key, :one => :two) }.should_not raise_error(ArgumentError) end it "should support nil options" do - lambda { Puppet::Indirector::Request.new(:ind, :key, :method, nil) }.should_not raise_error(ArgumentError) + lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.should_not raise_error(ArgumentError) end it "should support unspecified options" do - lambda { Puppet::Indirector::Request.new(:ind, :key, :method) }.should_not raise_error(ArgumentError) + lambda { Puppet::Indirector::Request.new(:ind, :method, :key) }.should_not raise_error(ArgumentError) end it "should fail if options are specified as anything other than nil or a hash" do - lambda { Puppet::Indirector::Request.new(:ind, :key, :method, [:one, :two]) }.should raise_error(ArgumentError) + lambda { Puppet::Indirector::Request.new(:ind, :method, :key, [:one, :two]) }.should raise_error(ArgumentError) end it "should use an empty options hash if nil was provided" do - Puppet::Indirector::Request.new(:ind, :key, :method, nil).options.should == {} + Puppet::Indirector::Request.new(:ind, :method, :key, nil).options.should == {} end end it "should look use the Indirection class to return the appropriate indirection" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind - request = Puppet::Indirector::Request.new(:myind, :key, :method) + request = Puppet::Indirector::Request.new(:myind, :method, :key) request.indirection.should equal(ind) end diff --git a/spec/unit/indirector/yaml.rb b/spec/unit/indirector/yaml.rb index 339529ab0..53d12f426 100755 --- a/spec/unit/indirector/yaml.rb +++ b/spec/unit/indirector/yaml.rb @@ -21,37 +21,28 @@ describe Puppet::Indirector::Yaml, " when choosing file location" do @dir = "/what/ever" Puppet.settings.stubs(:value).with(:yamldir).returns(@dir) - end - it "should use the mtime of the written file as the version" do - stat = mock 'stat' - FileTest.stubs(:exist?).returns true - File.expects(:stat).returns stat - time = Time.now - stat.expects(:mtime).returns time - - @store.version(:me).should equal(time) + @request = stub 'request', :key => :me, :instance => @subject end describe Puppet::Indirector::Yaml, " when choosing file location" do - it "should store all files in a single file root set in the Puppet defaults" do - @store.send(:path, :me).should =~ %r{^#{@dir}} + @store.path(:me).should =~ %r{^#{@dir}} end it "should use the terminus name for choosing the subdirectory" do - @store.send(:path, :me).should =~ %r{^#{@dir}/my_yaml} + @store.path(:me).should =~ %r{^#{@dir}/my_yaml} end it "should use the object's name to determine the file name" do - @store.send(:path, :me).should =~ %r{me.yaml$} + @store.path(:me).should =~ %r{me.yaml$} end end describe Puppet::Indirector::Yaml, " when storing objects as YAML" do - it "should only store objects that respond to :name" do - proc { @store.save(Object.new) }.should raise_error(ArgumentError) + @request.stubs(:instance).returns Object.new + proc { @store.save(@request) }.should raise_error(ArgumentError) end it "should convert Ruby objects to YAML and write them to disk" do @@ -62,7 +53,7 @@ describe Puppet::Indirector::Yaml, " when choosing file location" do File.expects(:open).with(path, "w", 0660).yields(file) file.expects(:print).with(yaml) - @store.save(@subject) + @store.save(@request) end it "should create the indirection subdirectory if it does not exist" do @@ -75,16 +66,11 @@ describe Puppet::Indirector::Yaml, " when choosing file location" do File.expects(:open).with(path, "w", 0660).yields(file) file.expects(:print).with(yaml) - @store.save(@subject) + @store.save(@request) end end describe Puppet::Indirector::Yaml, " when retrieving YAML" do - - it "should require the name of the object to retrieve" do - proc { @store.find(nil) }.should raise_error(ArgumentError) - end - it "should read YAML in from disk and convert it to Ruby objects" do path = @store.send(:path, @subject.name) @@ -92,7 +78,7 @@ describe Puppet::Indirector::Yaml, " when choosing file location" do FileTest.expects(:exist?).with(path).returns(true) File.expects(:read).with(path).returns(yaml) - @store.find(@subject.name).instance_variable_get("@name").should == :me + @store.find(@request).instance_variable_get("@name").should == :me end it "should fail coherently when the stored YAML is invalid" do @@ -104,7 +90,7 @@ describe Puppet::Indirector::Yaml, " when choosing file location" do FileTest.expects(:exist?).with(path).returns(true) File.expects(:read).with(path).returns(yaml) - proc { @store.find(@subject.name) }.should raise_error(Puppet::Error) + proc { @store.find(@request) }.should raise_error(Puppet::Error) end end end diff --git a/spec/unit/node.rb b/spec/unit/node.rb index e62bd5d07..421fcd635 100755 --- a/spec/unit/node.rb +++ b/spec/unit/node.rb @@ -117,9 +117,9 @@ end describe Puppet::Node, " when indirecting" do it "should redirect to the indirection" do - @indirection = mock 'indirection' + @indirection = stub 'indirection', :name => :node Puppet::Node.stubs(:indirection).returns(@indirection) - @indirection.expects(:find).with(:my_node.to_s) + @indirection.expects(:find) Puppet::Node.find(:my_node.to_s) end diff --git a/spec/unit/node/catalog.rb b/spec/unit/node/catalog.rb index 360dd87f2..cd27b925b 100755 --- a/spec/unit/node/catalog.rb +++ b/spec/unit/node/catalog.rb @@ -777,14 +777,14 @@ end describe Puppet::Node::Catalog, " when indirecting" do before do - @indirection = mock 'indirection' + @indirection = stub 'indirection', :name => :catalog Puppet::Indirector::Indirection.clear_cache end it "should redirect to the indirection for retrieval" do Puppet::Node::Catalog.stubs(:indirection).returns(@indirection) - @indirection.expects(:find).with(:myconfig) + @indirection.expects(:find) Puppet::Node::Catalog.find(:myconfig) end diff --git a/spec/unit/node/facts.rb b/spec/unit/node/facts.rb index 743a7082e..1bfccd32e 100755 --- a/spec/unit/node/facts.rb +++ b/spec/unit/node/facts.rb @@ -6,7 +6,7 @@ require 'puppet/node/facts' describe Puppet::Node::Facts, " when indirecting" do before do - @indirection = mock 'indirection' + @indirection = stub 'indirection', :request => mock('request'), :name => :facts # We have to clear the cache so that the facts ask for our indirection stub, # instead of anything that might be cached. @@ -16,13 +16,13 @@ describe Puppet::Node::Facts, " when indirecting" do it "should redirect to the specified fact store for retrieval" do Puppet::Node::Facts.stubs(:indirection).returns(@indirection) - @indirection.expects(:find).with(:my_facts) + @indirection.expects(:find) Puppet::Node::Facts.find(:my_facts) end it "should redirect to the specified fact store for storage" do Puppet::Node::Facts.stubs(:indirection).returns(@indirection) - @indirection.expects(:save).with(@facts) + @indirection.expects(:save) @facts.save end diff --git a/spec/unit/transaction/report.rb b/spec/unit/transaction/report.rb index 8fc3f0794..644f8d709 100755 --- a/spec/unit/transaction/report.rb +++ b/spec/unit/transaction/report.rb @@ -9,18 +9,18 @@ require 'puppet/transaction/report' describe Puppet::Transaction::Report, " when being indirect" do it "should redirect :find to the indirection" do - @indirection = mock 'indirection' + @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) - @indirection.expects(:find).with(:report) + @indirection.expects(:find) Puppet::Transaction::Report.find(:report) end it "should redirect :save to the indirection" do Facter.stubs(:value).returns("eh") - @indirection = mock 'indirection' + @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) report = Puppet::Transaction::Report.new - @indirection.expects(:save).with(report) + @indirection.expects(:save) report.save end @@ -28,6 +28,12 @@ describe Puppet::Transaction::Report, " when being indirect" do Puppet::Transaction::Report.indirection.terminus_class.should == :processor end + it "should delegate its name attribute to its host method" do + report = Puppet::Transaction::Report.new + report.expects(:host).returns "me" + report.name.should == "me" + end + after do Puppet::Indirector::Indirection.clear_cache end From 7774d9c443f19d44a1e2dab459fc4bfb94e75244 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 21:21:59 -0500 Subject: [PATCH 38/48] Ported the rest of the indirection terminuses over to expecting requests instead of having a random interface. --- lib/puppet/indirector/catalog/compiler.rb | 16 ++-- lib/puppet/indirector/exec.rb | 4 +- lib/puppet/indirector/facts/facter.rb | 4 +- lib/puppet/indirector/ldap.rb | 4 +- lib/puppet/indirector/node/exec.rb | 13 +-- lib/puppet/indirector/node/ldap.rb | 9 +- lib/puppet/indirector/node/plain.rb | 9 +- lib/puppet/indirector/report/processor.rb | 4 +- lib/puppet/indirector/terminus.rb | 27 ------ spec/integration/node.rb | 104 ++++++++++++++++------ spec/integration/node/catalog.rb | 10 ++- spec/integration/node/facts.rb | 34 +++++++ spec/integration/transaction/report.rb | 26 ++++++ spec/unit/indirector/catalog/compiler.rb | 37 ++++---- spec/unit/indirector/exec.rb | 18 ++-- spec/unit/indirector/facts/facter.rb | 8 +- spec/unit/indirector/ldap.rb | 18 ++-- spec/unit/indirector/node/exec.rb | 20 ++--- spec/unit/indirector/node/ldap.rb | 43 ++++----- spec/unit/indirector/node/plain.rb | 9 +- spec/unit/indirector/report/processor.rb | 16 ++-- spec/unit/indirector/terminus.rb | 54 ----------- 22 files changed, 246 insertions(+), 241 deletions(-) create mode 100755 spec/integration/node/facts.rb create mode 100755 spec/integration/transaction/report.rb diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index 6d769b97d..ecc340f75 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -13,11 +13,9 @@ class Puppet::Node::Catalog::Compiler < Puppet::Indirector::Code attr_accessor :code # Compile a node's catalog. - def find(key, client = nil, clientip = nil) - if key.is_a?(Puppet::Node) - node = key - else - node = find_node(key) + def find(request) + unless node = request.options[:node] || find_node(request.key) + raise ArgumentError, "Could not find node '%s'; cannot compile" % request.key end if catalog = compile(node) @@ -102,16 +100,12 @@ class Puppet::Node::Catalog::Compiler < Puppet::Indirector::Code def find_node(key) # If we want to use the cert name as our key # LAK:FIXME This needs to be figured out somehow, but it requires the routing. + # This should be able to use the request, yay. #if Puppet[:node_name] == 'cert' and client # key = client #end - # Note that this is reasonable, because either their node source should actually - # know about the node, or they should be using the ``null`` node source, which - # will always return data. - unless node = Puppet::Node.find_by_any_name(key) - raise Puppet::Error, "Could not find node '%s'" % key - end + return nil unless node = Puppet::Node.find_by_any_name(key) # Add any external data to the node. add_node_data(node) diff --git a/lib/puppet/indirector/exec.rb b/lib/puppet/indirector/exec.rb index 7e4ac8d18..2462e31da 100644 --- a/lib/puppet/indirector/exec.rb +++ b/lib/puppet/indirector/exec.rb @@ -3,9 +3,9 @@ require 'puppet/util' class Puppet::Indirector::Exec < Puppet::Indirector::Terminus # Look for external node definitions. - def find(name) + def find(request) # Run the command. - unless output = query(name) + unless output = query(request.key) return nil end diff --git a/lib/puppet/indirector/facts/facter.rb b/lib/puppet/indirector/facts/facter.rb index a8c47e3bf..465d90c13 100644 --- a/lib/puppet/indirector/facts/facter.rb +++ b/lib/puppet/indirector/facts/facter.rb @@ -56,8 +56,8 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code end # Look a host's facts up in Facter. - def find(key) - Puppet::Node::Facts.new(key, Facter.to_hash) + def find(request) + Puppet::Node::Facts.new(request.key, Facter.to_hash) end def save(facts) diff --git a/lib/puppet/indirector/ldap.rb b/lib/puppet/indirector/ldap.rb index fb883def6..07ad38933 100644 --- a/lib/puppet/indirector/ldap.rb +++ b/lib/puppet/indirector/ldap.rb @@ -2,10 +2,10 @@ require 'puppet/indirector/terminus' class Puppet::Indirector::Ldap < Puppet::Indirector::Terminus # Perform our ldap search and process the result. - def find(name) + def find(request) # We have to use 'yield' here because the LDAP::Entry objects # get destroyed outside the scope of the search, strangely. - ldapsearch(name) { |entry| return process(name, entry) } + ldapsearch(request.key) { |entry| return process(request.key, entry) } # Return nil if we haven't found something. return nil diff --git a/lib/puppet/indirector/node/exec.rb b/lib/puppet/indirector/node/exec.rb index dcfc625b2..52cbc370c 100644 --- a/lib/puppet/indirector/node/exec.rb +++ b/lib/puppet/indirector/node/exec.rb @@ -15,20 +15,13 @@ class Puppet::Node::Exec < Puppet::Indirector::Exec end # Look for external node definitions. - def find(name) + def find(request) output = super or return nil # Translate the output to ruby. - result = translate(name, output) + result = translate(request.key, output) - return create_node(name, result) - end - - # Use the version of the facts, since we assume that's the main thing - # that changes. If someone wants their own way of defining version, - # they can easily provide their own, um, version of this class. - def version(name) - Puppet::Node::Facts.version(name) + return create_node(request.key, result) end private diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb index 73b5cdd70..6c41c18d4 100644 --- a/lib/puppet/indirector/node/ldap.rb +++ b/lib/puppet/indirector/node/ldap.rb @@ -12,8 +12,11 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap end # Look for our node in ldap. - def find(name) + def find(request) return nil unless information = super + + name = request.key + node = Puppet::Node.new(name) parent_info = nil @@ -123,8 +126,4 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap end filter end - - def version(name) - Puppet::Node::Facts.version(name) - end end diff --git a/lib/puppet/indirector/node/plain.rb b/lib/puppet/indirector/node/plain.rb index 8058563e6..37ceb064d 100644 --- a/lib/puppet/indirector/node/plain.rb +++ b/lib/puppet/indirector/node/plain.rb @@ -11,16 +11,9 @@ class Puppet::Node::Plain < Puppet::Indirector::Plain node instance before it is returned." # Just return an empty node. - def find(name) + def find(request) node = super node.fact_merge node end - - # Use the version of the facts, since we assume that's the main thing - # that changes. If someone wants their own way of defining version, - # they can easily provide their own, um, version of this class. - def version(name) - Puppet::Node::Facts.version(name) - end end diff --git a/lib/puppet/indirector/report/processor.rb b/lib/puppet/indirector/report/processor.rb index fa2b7f36b..135f1649d 100644 --- a/lib/puppet/indirector/report/processor.rb +++ b/lib/puppet/indirector/report/processor.rb @@ -10,8 +10,8 @@ class Puppet::Transaction::Report::Processor < Puppet::Indirector::Code Puppet.settings.use(:main, :reporting, :metrics) end - def save(report) - process(report) + def save(request) + process(request.instance) end private diff --git a/lib/puppet/indirector/terminus.rb b/lib/puppet/indirector/terminus.rb index 3015c8a37..22c56a4d2 100644 --- a/lib/puppet/indirector/terminus.rb +++ b/lib/puppet/indirector/terminus.rb @@ -128,20 +128,6 @@ class Puppet::Indirector::Terminus end end - # Do we have an update for this object? This compares the provided version - # to our version, and returns true if our version is at least as high - # as the asked-about version. - def has_most_recent?(key, vers) - raise Puppet::DevError.new("Cannot check update status when no 'version' method is defined") unless respond_to?(:version) - - if existing_version = version(key) - #puts "%s fresh: %s (%s vs %s)" % [self.name, (existing_version.to_f >= vers.to_f).inspect, existing_version.to_f, vers.to_f] - existing_version.to_f >= vers.to_f - else - false - end - end - def indirection self.class.indirection end @@ -163,17 +149,4 @@ class Puppet::Indirector::Terminus def terminus_type self.class.terminus_type end - - # Provide a default method for retrieving an instance's version. - # By default, just find the resource and get its version. Individual - # terminus types can override this method to provide custom definitions of - # 'versions'. - def version(name) - raise Puppet::DevError.new("Cannot retrieve an instance's version without a :find method") unless respond_to?(:find) - if instance = find(name) - instance.version - else - nil - end - end end diff --git a/spec/integration/node.rb b/spec/integration/node.rb index d9c2618c9..b0375e743 100755 --- a/spec/integration/node.rb +++ b/spec/integration/node.rb @@ -7,38 +7,86 @@ require File.dirname(__FILE__) + '/../spec_helper' require 'puppet/node' -describe Puppet::Node, " when using the memory terminus" do - before do - @name = "me" - @old_terminus = Puppet::Node.indirection.terminus_class - @terminus = Puppet::Node.indirection.terminus(:memory) - Puppet::Node.indirection.stubs(:terminus).returns @terminus - @node = Puppet::Node.new(@name) - end +describe Puppet::Node do + describe "when delegating indirection calls" do + before do + @name = "me" + @node = Puppet::Node.new(@name) + end - it "should find no nodes by default" do - Puppet::Node.find(@name).should be_nil - end + it "should be able to use the exec terminus" do + Puppet::Node.indirection.stubs(:terminus_class).returns :exec - it "should be able to find nodes that were previously saved" do - @node.save - Puppet::Node.find(@name).should equal(@node) - end + # Load now so we can stub + terminus = Puppet::Node.indirection.terminus(:exec) - it "should replace existing saved nodes when a new node with the same name is saved" do - @node.save - two = Puppet::Node.new(@name) - two.save - Puppet::Node.find(@name).should equal(two) - end + terminus.expects(:query).with(@name).returns "myresults" + terminus.expects(:translate).with(@name, "myresults").returns "translated_results" + terminus.expects(:create_node).with(@name, "translated_results").returns @node - it "should be able to remove previously saved nodes" do - @node.save - Puppet::Node.destroy(@node.name) - Puppet::Node.find(@name).should be_nil - end + Puppet::Node.find(@name).should equal(@node) + end - it "should fail when asked to destroy a node that does not exist" do - proc { Puppet::Node.destroy(@node) }.should raise_error(ArgumentError) + it "should be able to use the yaml terminus" do + Puppet::Node.indirection.stubs(:terminus_class).returns :yaml + + # Load now, before we stub the exists? method. + Puppet::Node.indirection.terminus(:yaml) + + file = File.join(Puppet[:yamldir], "node", "me.yaml") + FileTest.expects(:exist?).with(file).returns false + Puppet::Node.find(@name).should be_nil + end + + it "should have an ldap terminus" do + Puppet::Node.indirection.terminus(:ldap).should_not be_nil + end + + it "should be able to use the plain terminus" do + Puppet::Node.indirection.stubs(:terminus_class).returns :plain + + # Load now, before we stub the exists? method. + Puppet::Node.indirection.terminus(:plain) + + Puppet::Node.expects(:new).with(@name).returns @node + + Puppet::Node.find(@name).should equal(@node) + end + + describe "and using the memory terminus" do + before do + @name = "me" + @old_terminus = Puppet::Node.indirection.terminus_class + @terminus = Puppet::Node.indirection.terminus(:memory) + Puppet::Node.indirection.stubs(:terminus).returns @terminus + @node = Puppet::Node.new(@name) + end + + it "should find no nodes by default" do + Puppet::Node.find(@name).should be_nil + end + + it "should be able to find nodes that were previously saved" do + @node.save + Puppet::Node.find(@name).should equal(@node) + end + + it "should replace existing saved nodes when a new node with the same name is saved" do + @node.save + two = Puppet::Node.new(@name) + two.save + Puppet::Node.find(@name).should equal(two) + end + + it "should be able to remove previously saved nodes" do + @node.save + Puppet::Node.destroy(@node.name) + Puppet::Node.find(@name).should be_nil + end + + it "should fail when asked to destroy a node that does not exist" do + proc { Puppet::Node.destroy(@node) }.should raise_error(ArgumentError) + end + end end end diff --git a/spec/integration/node/catalog.rb b/spec/integration/node/catalog.rb index d0ddfd8aa..ca14c2ea8 100755 --- a/spec/integration/node/catalog.rb +++ b/spec/integration/node/catalog.rb @@ -1,7 +1,7 @@ #!/usr/bin/env ruby # -# Created by Luke Kanies on 2007-10-18. -# Copyright (c) 2007. All rights reserved. +# Created by Luke Kanies on 2007-4-8. +# Copyright (c) 2008. All rights reserved. require File.dirname(__FILE__) + '/../../spec_helper' @@ -26,7 +26,11 @@ describe Puppet::Node::Catalog do # Load now, before we stub the exists? method. compiler = Puppet::Node::Catalog.indirection.terminus(:compiler) - compiler.expects(:compile).with("me").returns nil + node = mock 'node' + node.stub_everything + + Puppet::Node.expects(:find).returns(node) + compiler.expects(:compile).with(node).returns nil Puppet::Node::Catalog.find("me").should be_nil end diff --git a/spec/integration/node/facts.rb b/spec/integration/node/facts.rb new file mode 100755 index 000000000..977a1b6c9 --- /dev/null +++ b/spec/integration/node/facts.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-4-8. +# Copyright (c) 2008. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Node::Facts do + describe "when using the indirector" do + after { Puppet::Node::Facts.indirection.clear_cache } + + it "should be able to delegate to the :yaml terminus" do + Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml + + # Load now, before we stub the exists? method. + Puppet::Node::Facts.indirection.terminus(:yaml) + + file = File.join(Puppet[:yamldir], "facts", "me.yaml") + FileTest.expects(:exist?).with(file).returns false + + Puppet::Node::Facts.find("me").should be_nil + end + + it "should be able to delegate to the :facter terminus" do + Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :facter + + Facter.expects(:to_hash).returns "facter_hash" + facts = Puppet::Node::Facts.new("me") + Puppet::Node::Facts.expects(:new).with("me", "facter_hash").returns facts + + Puppet::Node::Facts.find("me").should equal(facts) + end + end +end diff --git a/spec/integration/transaction/report.rb b/spec/integration/transaction/report.rb new file mode 100755 index 000000000..48e59f203 --- /dev/null +++ b/spec/integration/transaction/report.rb @@ -0,0 +1,26 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2008-4-8. +# Copyright (c) 2008. All rights reserved. + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Transaction::Report do + describe "when using the indirector" do + after { Puppet::Transaction::Report.indirection.clear_cache } + + it "should be able to delegate to the :processor terminus" do + Puppet::Transaction::Report.indirection.stubs(:terminus_class).returns :processor + + terminus = Puppet::Transaction::Report.indirection.terminus(:processor) + + Facter.stubs(:value).returns "host.domain.com" + + report = Puppet::Transaction::Report.new + + terminus.expects(:process).with(report) + + report.save + end + end +end diff --git a/spec/unit/indirector/catalog/compiler.rb b/spec/unit/indirector/catalog/compiler.rb index 5a26302d1..57bbcdbd0 100755 --- a/spec/unit/indirector/catalog/compiler.rb +++ b/spec/unit/indirector/catalog/compiler.rb @@ -26,8 +26,8 @@ describe Puppet::Node::Catalog::Compiler do Puppet::Node.stubs(:find_by_any_name).with('node1').returns(node1) Puppet::Node.stubs(:find_by_any_name).with('node2').returns(node2) - compiler.find('node1') - compiler.find('node2') + compiler.find(stub('request', :key => 'node1', :options => {})) + compiler.find(stub('node2request', :key => 'node2', :options => {})) end it "should provide a method for determining if the catalog is networked" do @@ -63,21 +63,14 @@ describe Puppet::Node::Catalog::Compiler, " when finding nodes" do @compiler = Puppet::Node::Catalog::Compiler.new @name = "me" @node = mock 'node' + @request = stub 'request', :key => @name, :options => {} @compiler.stubs(:compile) end it "should look node information up via the Node class with the provided key" do @node.stubs :merge Puppet::Node.expects(:find_by_any_name).with(@name).returns(@node) - @compiler.find(@name) - end - - it "should fail if it cannot find the node" do - @node.stubs :merge - Puppet::Node.expects(:find_by_any_name).with(@name).returns(nil) - request = stub 'request', :key => @name - @compiler.find(request) - proc { @compiler.find(request) }.should raise_error(Puppet::Error) + @compiler.find(@request) end end @@ -90,23 +83,24 @@ describe Puppet::Node::Catalog::Compiler, " after finding nodes" do @compiler = Puppet::Node::Catalog::Compiler.new @name = "me" @node = mock 'node' + @request = stub 'request', :key => @name, :options => {} @compiler.stubs(:compile) Puppet::Node.stubs(:find_by_any_name).with(@name).returns(@node) end it "should add the server's Puppet version to the node's parameters as 'serverversion'" do @node.expects(:merge).with { |args| args["serverversion"] == "1" } - @compiler.find(@name) + @compiler.find(@request) end it "should add the server's fqdn to the node's parameters as 'servername'" do @node.expects(:merge).with { |args| args["servername"] == "my.server.com" } - @compiler.find(@name) + @compiler.find(@request) end it "should add the server's IP address to the node's parameters as 'serverip'" do @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" } - @compiler.find(@name) + @compiler.find(@request) end # LAK:TODO This is going to be difficult, because this whole process is so @@ -127,19 +121,26 @@ describe Puppet::Node::Catalog::Compiler, " when creating catalogs" do @name = "me" @node = Puppet::Node.new @name @node.stubs(:merge) + @request = stub 'request', :key => @name, :options => {} Puppet::Node.stubs(:find_by_any_name).with(@name).returns(@node) end it "should directly use provided nodes" do Puppet::Node.expects(:find_by_any_name).never @compiler.interpreter.expects(:compile).with(@node) - @compiler.find(@node) + @request.stubs(:options).returns(:node => @node) + @compiler.find(@request) + end + + it "should fail if no node is passed and none can be found" do + Puppet::Node.stubs(:find_by_any_name).with(@name).returns(nil) + proc { @compiler.find(@request) }.should raise_error(ArgumentError) end it "should pass the found node to the interpreter for compiling" do config = mock 'config' @compiler.interpreter.expects(:compile).with(@node) - @compiler.find(@name) + @compiler.find(@request) end it "should return the results of compiling as the catalog" do @@ -147,7 +148,7 @@ describe Puppet::Node::Catalog::Compiler, " when creating catalogs" do result = mock 'result', :to_transportable => :catalog @compiler.interpreter.expects(:compile).with(@node).returns(result) - @compiler.find(@name).should == :catalog + @compiler.find(@request).should == :catalog end it "should benchmark the compile process" do @@ -156,7 +157,7 @@ describe Puppet::Node::Catalog::Compiler, " when creating catalogs" do level == :notice and message =~ /^Compiled catalog/ end @compiler.interpreter.stubs(:compile).with(@node) - @compiler.find(@name) + @compiler.find(@request) end end diff --git a/spec/unit/indirector/exec.rb b/spec/unit/indirector/exec.rb index 3baf06629..e474de8b9 100755 --- a/spec/unit/indirector/exec.rb +++ b/spec/unit/indirector/exec.rb @@ -18,31 +18,33 @@ describe Puppet::Indirector::Exec do @searcher = @exec_class.new @searcher.command = ["/echo"] + + @request = stub 'request', :key => "foo" end it "should throw an exception if the command is not an array" do @searcher.command = "/usr/bin/echo" - proc { @searcher.find("foo") }.should raise_error(Puppet::DevError) + proc { @searcher.find(@request) }.should raise_error(Puppet::DevError) end it "should throw an exception if the command is not fully qualified" do @searcher.command = ["mycommand"] - proc { @searcher.find("foo") }.should raise_error(ArgumentError) + proc { @searcher.find(@request) }.should raise_error(ArgumentError) end it "should execute the command with the object name as the only argument" do - @searcher.expects(:execute).with(%w{/echo yay}) - @searcher.find("yay") + @searcher.expects(:execute).with(%w{/echo foo}) + @searcher.find(@request) end it "should return the output of the script" do - @searcher.expects(:execute).with(%w{/echo yay}).returns("whatever") - @searcher.find("yay").should == "whatever" + @searcher.expects(:execute).with(%w{/echo foo}).returns("whatever") + @searcher.find(@request).should == "whatever" end it "should return nil when the command produces no output" do - @searcher.expects(:execute).with(%w{/echo yay}).returns(nil) - @searcher.find("yay").should be_nil + @searcher.expects(:execute).with(%w{/echo foo}).returns(nil) + @searcher.find(@request).should be_nil end it "should be able to execute commands with multiple arguments" diff --git a/spec/unit/indirector/facts/facter.rb b/spec/unit/indirector/facts/facter.rb index 5ec66555d..225eb153b 100755 --- a/spec/unit/indirector/facts/facter.rb +++ b/spec/unit/indirector/facts/facter.rb @@ -36,22 +36,22 @@ describe Puppet::Node::Facts::Facter do @facter = Puppet::Node::Facts::Facter.new Facter.stubs(:to_hash).returns({}) @name = "me" - @facts = @facter.find(@name) + @request = stub 'request', :key => @name end describe Puppet::Node::Facts::Facter, " when finding facts" do it "should return a Facts instance" do - @facts.should be_instance_of(Puppet::Node::Facts) + @facter.find(@request).should be_instance_of(Puppet::Node::Facts) end it "should return a Facts instance with the provided key as the name" do - @facts.name.should == @name + @facter.find(@request).name.should == @name end it "should return the Facter facts as the values in the Facts instance" do Facter.expects(:to_hash).returns("one" => "two") - facts = @facter.find(@name) + facts = @facter.find(@request) facts.values["one"].should == "two" end end diff --git a/spec/unit/indirector/ldap.rb b/spec/unit/indirector/ldap.rb index 6712ccb4f..2599bcecf 100755 --- a/spec/unit/indirector/ldap.rb +++ b/spec/unit/indirector/ldap.rb @@ -24,11 +24,13 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do @searcher.stubs(:search_filter).returns(:filter) @searcher.stubs(:search_base).returns(:base) @searcher.stubs(:process) + + @request = stub 'request', :key => "yay" end it "should call the ldapsearch method with the name being searched for" do @searcher.expects(:ldapsearch).with("yay") - @searcher.find "yay" + @searcher.find @request end it "should fail if no block is passed to the ldapsearch method" do @@ -41,7 +43,7 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do args[0].should == "mybase" true end - @searcher.find "yay" + @searcher.find @request end it "should default to the value of the :search_base setting as the result of the ldapbase method" do @@ -56,7 +58,7 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do args[3].should == :myattrs true end - @searcher.find "yay" + @searcher.find @request end it "should use the results of the :search_filter method as the search filter" do @@ -65,7 +67,7 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do args[2].should == "yay's filter" true end - @searcher.find "yay" + @searcher.find @request end it "should use depth 2 when searching" do @@ -73,13 +75,13 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do args[1].should == 2 true end - @searcher.find "yay" + @searcher.find @request end it "should call process() on the first found entry" do @connection.expects(:search).yields("myresult") @searcher.expects(:process).with("yay", "myresult") - @searcher.find "yay" + @searcher.find @request end it "should reconnect and retry the search if there is a failure" do @@ -94,7 +96,7 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do end.yields("myresult") @searcher.expects(:process).with("yay", "myresult") - @searcher.find "yay" + @searcher.find @request end it "should not reconnect on failure more than once" do @@ -103,7 +105,7 @@ describe Puppet::Indirector::Ldap, " when searching ldap" do count += 1 raise ArgumentError, "yay" end - proc { @searcher.find("whatever") }.should raise_error(Puppet::Error) + proc { @searcher.find(@request) }.should raise_error(Puppet::Error) count.should == 2 end diff --git a/spec/unit/indirector/node/exec.rb b/spec/unit/indirector/node/exec.rb index b67e0fe97..09f13ab90 100755 --- a/spec/unit/indirector/node/exec.rb +++ b/spec/unit/indirector/node/exec.rb @@ -11,12 +11,6 @@ describe Puppet::Node::Exec do @searcher = Puppet::Node::Exec.new end - it "should use the version of the facts as its version" do - version = mock 'version' - Puppet::Node::Facts.expects(:version).with("me").returns version - @searcher.version("me").should equal(version) - end - describe "when constructing the command to run" do it "should use the external_node script as the command" do Puppet.expects(:[]).with(:external_nodes).returns("/bin/echo") @@ -25,7 +19,7 @@ describe Puppet::Node::Exec do it "should throw an exception if no external node command is set" do Puppet.expects(:[]).with(:external_nodes).returns("none") - proc { @searcher.find("foo") }.should raise_error(ArgumentError) + proc { @searcher.find(stub('request', :key => "foo")) }.should raise_error(ArgumentError) end end @@ -40,34 +34,36 @@ describe Puppet::Node::Exec do @searcher.meta_def(:execute) do |command| return YAML.dump(result) end + + @request = stub 'request', :key => @name end it "should translate the YAML into a Node instance" do # Use an empty hash - @searcher.find(@name).should equal(@node) + @searcher.find(@request).should equal(@node) end it "should set the resulting parameters as the node parameters" do @result[:parameters] = {"a" => "b", "c" => "d"} @node.expects(:parameters=).with "a" => "b", "c" => "d" - @searcher.find(@name) + @searcher.find(@request) end it "should set the resulting classes as the node classes" do @result[:classes] = %w{one two} @node.expects(:classes=).with %w{one two} - @searcher.find(@name) + @searcher.find(@request) end it "should merge the node's facts with its parameters" do @node.expects(:fact_merge) - @searcher.find(@name) + @searcher.find(@request) end it "should set the node's environment if one is provided" do @result[:environment] = "yay" @node.expects(:environment=).with "yay" - @searcher.find(@name) + @searcher.find(@request) end end end diff --git a/spec/unit/indirector/node/ldap.rb b/spec/unit/indirector/node/ldap.rb index 34456703d..a40698662 100755 --- a/spec/unit/indirector/node/ldap.rb +++ b/spec/unit/indirector/node/ldap.rb @@ -5,13 +5,6 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do - it "should use the version of the facts as its version" do - @searcher = Puppet::Node::Ldap.new - version = mock 'version' - Puppet::Node::Facts.expects(:version).with("me").returns version - @searcher.version("me").should equal(version) - end - describe "when searching for nodes" do before :each do @searcher = Puppet::Node::Ldap.new @@ -31,16 +24,18 @@ describe Puppet::Node::Ldap do @node.stubs(:fact_merge) @name = "mynode" Puppet::Node.stubs(:new).with(@name).returns(@node) + + @request = stub 'request', :key => @name end it "should return nil if no results are found in ldap" do @connection.stubs(:search) - @searcher.find("mynode").should be_nil + @searcher.find(@request).should be_nil end it "should return a node object if results are found in ldap" do @entry.stubs(:to_hash).returns({}) - @searcher.find("mynode").should equal(@node) + @searcher.find(@request).should equal(@node) end it "should deduplicate class values" do @@ -49,7 +44,7 @@ describe Puppet::Node::Ldap do @entry.stubs(:vals).with("one").returns(%w{a b}) @entry.stubs(:vals).with("two").returns(%w{b c}) @node.expects(:classes=).with(%w{a b c}) - @searcher.find("mynode") + @searcher.find(@request) end it "should add any values stored in the class_attributes attributes to the node classes" do @@ -58,38 +53,38 @@ describe Puppet::Node::Ldap do @entry.stubs(:vals).with("one").returns(%w{a b}) @entry.stubs(:vals).with("two").returns(%w{c d}) @node.expects(:classes=).with(%w{a b c d}) - @searcher.find("mynode") + @searcher.find(@request) end it "should add all entry attributes as node parameters" do @entry.stubs(:to_hash).returns("one" => ["two"], "three" => ["four"]) @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find("mynode") + @searcher.find(@request) end it "should set the node's environment to the environment of the results" do @entry.stubs(:to_hash).returns("environment" => ["test"]) @node.stubs(:parameters=) @node.expects(:environment=).with("test") - @searcher.find("mynode") + @searcher.find(@request) end it "should retain false parameter values" do @entry.stubs(:to_hash).returns("one" => [false]) @node.expects(:parameters=).with("one" => false) - @searcher.find("mynode") + @searcher.find(@request) end it "should turn single-value parameter value arrays into single non-arrays" do @entry.stubs(:to_hash).returns("one" => ["a"]) @node.expects(:parameters=).with("one" => "a") - @searcher.find("mynode") + @searcher.find(@request) end it "should keep multi-valued parametes as arrays" do @entry.stubs(:to_hash).returns("one" => ["a", "b"]) @node.expects(:parameters=).with("one" => ["a", "b"]) - @searcher.find("mynode") + @searcher.find(@request) end describe "and a parent node is specified" do @@ -113,7 +108,7 @@ describe Puppet::Node::Ldap do @entry.stubs(:to_hash).returns({}) @entry.stubs(:vals).with(:parent).returns(%w{parent}) - proc { @searcher.find("mynode") }.should raise_error(Puppet::Error) + proc { @searcher.find(@request) }.should raise_error(Puppet::Error) end it "should add any parent classes to the node's classes" do @@ -127,7 +122,7 @@ describe Puppet::Node::Ldap do @searcher.stubs(:class_attributes).returns(%w{classes}) @node.expects(:classes=).with(%w{a b c d}) - @searcher.find("mynode") + @searcher.find(@request) end it "should add any parent parameters to the node's parameters" do @@ -138,7 +133,7 @@ describe Puppet::Node::Ldap do @parent.stubs(:vals).with(:parent).returns(nil) @node.expects(:parameters=).with("one" => "two", "three" => "four") - @searcher.find("mynode") + @searcher.find(@request) end it "should prefer node parameters over parent parameters" do @@ -149,7 +144,7 @@ describe Puppet::Node::Ldap do @parent.stubs(:vals).with(:parent).returns(nil) @node.expects(:parameters=).with("one" => "two") - @searcher.find("mynode") + @searcher.find(@request) end it "should use the parent's environment if the node has none" do @@ -161,7 +156,7 @@ describe Puppet::Node::Ldap do @node.stubs(:parameters=) @node.expects(:environment=).with("parent") - @searcher.find("mynode") + @searcher.find(@request) end it "should prefer the node's environment to the parent's" do @@ -173,7 +168,7 @@ describe Puppet::Node::Ldap do @node.stubs(:parameters=) @node.expects(:environment=).with("child") - @searcher.find("mynode") + @searcher.find(@request) end it "should recursively look up parent information" do @@ -188,7 +183,7 @@ describe Puppet::Node::Ldap do @parent_parent.stubs(:vals).with(:parent).returns(nil) @node.expects(:parameters=).with("one" => "two", "three" => "four", "five" => "six") - @searcher.find("mynode") + @searcher.find(@request) end it "should not allow loops in parent declarations" do @@ -197,7 +192,7 @@ describe Puppet::Node::Ldap do @parent.stubs(:to_hash).returns("three" => "four") @parent.stubs(:vals).with(:parent).returns([@name]) - proc { @searcher.find("mynode") }.should raise_error(ArgumentError) + proc { @searcher.find(@request) }.should raise_error(ArgumentError) end end end diff --git a/spec/unit/indirector/node/plain.rb b/spec/unit/indirector/node/plain.rb index 943af52b4..a595998a1 100755 --- a/spec/unit/indirector/node/plain.rb +++ b/spec/unit/indirector/node/plain.rb @@ -13,12 +13,7 @@ describe Puppet::Node::Plain do node = mock 'node' Puppet::Node.expects(:new).with("mynode").returns(node) node.expects(:fact_merge) - @searcher.find("mynode") - end - - it "should use the version of the facts as its version" do - version = mock 'version' - Puppet::Node::Facts.expects(:version).with("me").returns version - @searcher.version("me").should equal(version) + request = stub 'request', :key => "mynode" + @searcher.find(request) end end diff --git a/spec/unit/indirector/report/processor.rb b/spec/unit/indirector/report/processor.rb index 587f512ee..bcb400bda 100755 --- a/spec/unit/indirector/report/processor.rb +++ b/spec/unit/indirector/report/processor.rb @@ -13,7 +13,6 @@ describe Puppet::Transaction::Report::Processor do end end - describe Puppet::Transaction::Report::Processor, " when saving a report" do before do Puppet.settings.stubs(:use) @@ -24,7 +23,9 @@ describe Puppet::Transaction::Report::Processor, " when saving a report" do Puppet::Reports.expects(:report).never Puppet.settings.expects(:value).with(:reports).returns("none") - @reporter.save(:whatever) + request = stub 'request', :instance => mock("report") + + @reporter.save(request) end it "should process the report with each configured report type" do @@ -44,6 +45,9 @@ describe Puppet::Transaction::Report::Processor, " when processing a report" do @dup_report.stubs(:process) @report = mock 'report' @report.expects(:dup).returns(@dup_report) + + @request = stub 'request', :instance => @report + Puppet::Reports.expects(:report).with("one").returns(@report_type) @dup_report.expects(:extend).with(@report_type) @@ -53,21 +57,21 @@ describe Puppet::Transaction::Report::Processor, " when processing a report" do # make sense to split it out, which means I just do the same test # three times so the spec looks right. it "should process a duplicate of the report, not the original" do - @reporter.save(@report) + @reporter.save(@request) end it "should extend the report with the report type's module" do - @reporter.save(@report) + @reporter.save(@request) end it "should call the report type's :process method" do @dup_report.expects(:process) - @reporter.save(@report) + @reporter.save(@request) end it "should not raise exceptions" do Puppet.settings.stubs(:value).with(:trace).returns(false) @dup_report.expects(:process).raises(ArgumentError) - proc { @reporter.save(@report) }.should_not raise_error + proc { @reporter.save(@request) }.should_not raise_error end end diff --git a/spec/unit/indirector/terminus.rb b/spec/unit/indirector/terminus.rb index 86813e4e5..3fcbf9d0c 100755 --- a/spec/unit/indirector/terminus.rb +++ b/spec/unit/indirector/terminus.rb @@ -106,60 +106,6 @@ describe Puppet::Indirector::Terminus do @terminus.model.should == :yay end end - - describe Puppet::Indirector::Terminus, " when managing indirected instances" do - - it "should support comparing an instance's version with the terminus's version using just the instance's key" do - @terminus.should respond_to(:has_most_recent?) - end - - it "should fail if the :version method has not been overridden and no :find method is available" do - proc { @terminus.version('yay') }.should raise_error(Puppet::DevError) - end - - it "should use a found instance's version by default" do - name = 'instance' - instance = stub name, :version => 2 - @terminus.expects(:find).with(name).returns(instance) - @terminus.version(name).should == 2 - end - - it "should return nil as the version if no instance can be found" do - name = 'instance' - @terminus.expects(:find).with(name).returns(nil) - @terminus.version(name).should be_nil - end - - it "should consider an instance fresh if its version is more recent than the version provided" do - name = "yay" - @terminus.expects(:version).with(name).returns(5) - @terminus.has_most_recent?(name, 4).should be_true - end - - it "should consider an instance fresh if its version is equal to the version provided" do - name = "yay" - @terminus.expects(:version).with(name).returns(5) - @terminus.has_most_recent?(name, 5).should be_true - end - - it "should consider an instance not fresh if the provided version is more recent than its version" do - name = "yay" - @terminus.expects(:version).with(name).returns(4) - @terminus.has_most_recent?(name, 5).should be_false - end - - # Times annoyingly can't be compared directly to numbers, and our - # default version is 0. - it "should convert versions to floats when checking for freshness" do - existing = mock 'existing version' - new = mock 'new version' - existing.expects(:to_f).returns(1.0) - new.expects(:to_f).returns(1.0) - name = "yay" - @terminus.expects(:version).with(name).returns(existing) - @terminus.has_most_recent?(name, new) - end - end end # LAK: This could reasonably be in the Indirection instances, too. It doesn't make From d82ac984cdbafb9fd43b2566b911b3ba33c0e38c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 21:36:29 -0500 Subject: [PATCH 39/48] Fixing the executables to use the new indirection api. --- bin/puppet | 2 +- ext/module_puppet | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/puppet b/bin/puppet index 9e1102a3d..bb7308997 100755 --- a/bin/puppet +++ b/bin/puppet @@ -206,7 +206,7 @@ end begin # Compile our catalog - catalog = Puppet::Node::Catalog.find(node) + catalog = Puppet::Node::Catalog.find(node.name, :node => node) # Translate it to a RAL catalog catalog = catalog.to_ral diff --git a/ext/module_puppet b/ext/module_puppet index 6a3f33fe6..e4a680e1b 100755 --- a/ext/module_puppet +++ b/ext/module_puppet @@ -179,7 +179,7 @@ node.classes = classes begin # Compile our configuration - catalog = Puppet::Node::Catalog.find(node) + catalog = Puppet::Node::Catalog.find(node.name, :node => node) rescue => detail if Puppet[:trace] puts detail.backtrace From d420701522990dd7c55bda7ea1e070254e9dcd13 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 8 Apr 2008 23:48:48 -0500 Subject: [PATCH 40/48] Making the log messages around caching better. --- lib/puppet/indirector/indirection.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 56cd687af..536735c21 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -178,9 +178,9 @@ class Puppet::Indirector::Indirection # See if our instance is in the cache and up to date. if cache? and cached = cache.find(request) if cached.expired? - Puppet.info "Cached %s %s expired at %s; not using" % [self.name, request.key, cached.expiration] + Puppet.info "Not using expired %s for %s from cache; expired at %s" % [self.name, request.key, cached.expiration] else - Puppet.debug "Using cached %s %s" % [self.name, request.key] + Puppet.debug "Using cached %s for %s" % [self.name, request.key] return cached end end @@ -189,7 +189,7 @@ class Puppet::Indirector::Indirection if result = terminus.find(request) result.expiration ||= self.expiration if cache? - Puppet.info "Caching %s %s" % [self.name, request.key] + Puppet.info "Caching %s for %s" % [self.name, request.key] cached_request = request.clone cached_request.instance = result cache.save(cached_request) From f285f1aab525a2585532fda0b15b7fd28e994491 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 10 Apr 2008 13:14:58 -0500 Subject: [PATCH 41/48] Moved the request creation into the Indirection class instead of the Indirector module. Also, added an 'expire' method to the indirector, so there's an easy way to expire cached instances. --- lib/puppet/indirector.rb | 13 +- lib/puppet/indirector/indirection.rb | 55 +++- spec/integration/node/facts.rb | 4 + spec/unit/indirector.rb | 33 +-- spec/unit/indirector/indirection.rb | 372 +++++++++++++++------------ 5 files changed, 276 insertions(+), 201 deletions(-) diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index a8a7a84d1..2402b9cbe 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -40,22 +40,27 @@ module Puppet::Indirector indirection.terminus_class = klass end + # Expire any cached instance. + def expire(*args) + indirection.expire *args + end + def find(*args) - indirection.find Puppet::Indirector::Request.new(indirection.name, :find, *args) + indirection.find *args end def destroy(*args) - indirection.destroy Puppet::Indirector::Request.new(indirection.name, :destroy, *args) + indirection.destroy *args end def search(*args) - indirection.search Puppet::Indirector::Request.new(indirection.name, :search, *args) + indirection.search *args end end module InstanceMethods def save(*args) - self.class.indirection.save Puppet::Indirector::Request.new(self.class.indirection.name, :save, self, *args) + self.class.indirection.save self, *args end end end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 536735c21..ccb7ef6e7 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -127,8 +127,8 @@ class Puppet::Indirector::Indirection end # Set up our request object. - def request(key, method, arguments = nil) - Puppet::Indirector::Request.new(self.name, key, method, arguments) + def request(method, key, arguments = nil) + Puppet::Indirector::Request.new(self.name, method, key, arguments) end # Return the singleton terminus for this indirection. @@ -172,7 +172,26 @@ class Puppet::Indirector::Indirection end end - def find(request) + # Expire a cached object, if one is cached. Note that we don't actually + # remove it, we expire it and write it back out to disk. This way people + # can still use the expired object if they want. + def expire(key, *args) + request = request(:expire, key, *args) + + return nil unless cache? + + return nil unless instance = cache.find(key, *args) + + # Set an expiration date in the past + instance.expiration = Time.now - 60 + + cache.save(instance, *args) + end + + # Search for an instance in the appropriate terminus, caching the + # results if caching is configured.. + def find(key, *args) + request = request(:find, key, *args) terminus = prepare(request) # See if our instance is in the cache and up to date. @@ -190,9 +209,7 @@ class Puppet::Indirector::Indirection result.expiration ||= self.expiration if cache? Puppet.info "Caching %s for %s" % [self.name, request.key] - cached_request = request.clone - cached_request.instance = result - cache.save(cached_request) + cache.save request(:save, result, *args) end return result @@ -202,12 +219,14 @@ class Puppet::Indirector::Indirection end # Remove something via the terminus. - def destroy(request) + def destroy(key, *args) + request = request(:destroy, key, *args) terminus = prepare(request) terminus.destroy(request) - if cache? and cached = cache.find(request) + if cache? and cached = cache.find(request(:find, key, *args)) + # Reuse the existing request, since it's equivalent. cache.destroy(request) end @@ -215,17 +234,24 @@ class Puppet::Indirector::Indirection end # Search for more than one instance. Should always return an array. - def search(request) + def search(key, *args) + request = request(:search, key, *args) terminus = prepare(request) - result = terminus.search(request) + if result = terminus.search(request) + raise Puppet::DevError, "Search results from terminus %s are not an array" % terminus.name unless result.is_a?(Array) - result + result.each do |instance| + instance.expiration ||= self.expiration + end + return result + end end # Save the instance in the appropriate terminus. This method is # normally an instance method on the indirected class. - def save(request) + def save(instance, *args) + request = request(:save, instance, *args) terminus = prepare(request) # If caching is enabled, save our document there @@ -238,12 +264,15 @@ class Puppet::Indirector::Indirection # Check authorization if there's a hook available; fail if there is one # and it returns false. def check_authorization(request, terminus) + # At this point, we're assuming authorization makes no sense without + # client information. return unless request.options[:node] + # This is only to authorize via a terminus-specific authorization hook. return unless terminus.respond_to?(:authorized?) unless terminus.authorized?(request) - raise ArgumentError, "Not authorized to call %s with %s" % [request.method, request.options.inspect] + raise ArgumentError, "Not authorized to call %s on %s with %s" % [request.method, request.key, request.options.inspect] end end diff --git a/spec/integration/node/facts.rb b/spec/integration/node/facts.rb index 977a1b6c9..d065918be 100755 --- a/spec/integration/node/facts.rb +++ b/spec/integration/node/facts.rb @@ -9,6 +9,10 @@ describe Puppet::Node::Facts do describe "when using the indirector" do after { Puppet::Node::Facts.indirection.clear_cache } + it "should expire any cached node instances when it is saved" do + raise "This test fails" + end + it "should be able to delegate to the :yaml terminus" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml diff --git a/spec/unit/indirector.rb b/spec/unit/indirector.rb index 892e70c1f..1efa7b2e5 100755 --- a/spec/unit/indirector.rb +++ b/spec/unit/indirector.rb @@ -60,22 +60,14 @@ describe Puppet::Indirector, "when registering an indirection" do end describe "Delegated Indirection Method", :shared => true do - it "should create an indirection request with the indirection name, the method being delegated, and all of the arguments to the method call" do - Puppet::Indirector::Request.expects(:new).with(@indirection.name, @method, "me", :one => :two) - @indirection.stubs(@method) - @thingie.send(@method, "me", :one => :two) - end - it "should delegate to the indirection" do @indirection.expects(@method) @thingie.send(@method, "me") end - it "should pass the indirection's request instance to the indirection's method" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).returns request - @indirection.expects(@method).with(request) - @thingie.send(@method, "me") + it "should pass all of the passed arguments directly to the indirection instance" do + @indirection.expects(@method).with("me", :one => :two) + @thingie.send(@method, "me", :one => :two) end it "should return the results of the delegation as its result" do @@ -116,28 +108,25 @@ describe Puppet::Indirector, "when redirecting a model" do it_should_behave_like "Delegated Indirection Method" end + describe "when expiring instances via the model" do + before { @method = :expire } + it_should_behave_like "Delegated Indirection Method" + end + # This is an instance method, so it behaves a bit differently. describe "when saving instances via the model" do before do @instance = @thingie.new("me") end - it "should pass the method name, the instance, plus all passed arguments to the indirection's request method" do - Puppet::Indirector::Request.expects(:new).with(@indirection.name, :save, @instance, :one => :two) - @indirection.stubs(:save) - @instance.save(:one => :two) - end - it "should delegate to the indirection" do @indirection.expects(:save) @instance.save end - it "should pass the indirection's request instance to the indirection's method" do - request = mock 'request' - Puppet::Indirector::Request.expects(:new).returns request - @indirection.expects(:save).with(request) - @instance.save + it "should pass the instance and all arguments to the indirection's :save method" do + @indirection.expects(:save).with(@instance, :one => :two) + @instance.save :one => :two end it "should return the results of the delegation as its result" do diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index f33444bbf..06d9fcb6d 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -4,6 +4,88 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/indirector/indirection' +describe "Indirection Delegator", :shared => true do + it "should create a request object with the appropriate method name and all of the passed arguments" do + request = stub 'request', :options => {} + + @indirection.expects(:request).with(@method, "mystuff", :one => :two).returns request + + @terminus.stubs(@method) + + @indirection.send(@method, "mystuff", :one => :two) + end + + it "should let the :select_terminus method choose the terminus using the created request if the :select_terminus method is available" do + # Define the method, so our respond_to? hook matches. + class << @indirection + def select_terminus(request) + end + end + + request = stub 'request', :key => "me", :options => {} + + @indirection.stubs(:request).returns request + + @indirection.expects(:select_terminus).with(request).returns :test_terminus + + @indirection.stubs(:check_authorization) + @terminus.expects(@method) + + @indirection.send(@method, "me") + end + + it "should choose the terminus returned by the :terminus_class method if no :select_terminus method is available" do + @indirection.expects(:terminus_class).returns :test_terminus + + @terminus.expects(@method) + + @indirection.send(@method, "me") + end + + it "should let the appropriate terminus perform the lookup" do + @terminus.expects(@method).with { |r| r.is_a?(Puppet::Indirector::Request) } + @indirection.send(@method, "me") + end +end + +describe "Delegation Authorizer", :shared => true do + before do + # So the :respond_to? turns out correctly. + class << @terminus + def authorized? + end + end + end + + it "should not check authorization if a node name is not provided" do + @terminus.expects(:authorized?).never + @terminus.stubs(@method) + + # The quotes are necessary here, else it looks like a block. + @request.stubs(:options).returns({}) + @indirection.send(@method, "/my/key") + end + + it "should pass the request to the terminus's authorization method" do + @terminus.expects(:authorized?).with { |r| r.is_a?(Puppet::Indirector::Request) }.returns(true) + @terminus.stubs(@method) + + @indirection.send(@method, "/my/key", :node => "mynode") + end + + it "should fail if authorization returns false" do + @terminus.expects(:authorized?).returns(false) + @terminus.stubs(@method) + proc { @indirection.send(@method, "/my/key", :node => "mynode") }.should raise_error(ArgumentError) + end + + it "should continue if authorization returns true" do + @terminus.expects(:authorized?).returns(true) + @terminus.stubs(@method) + @indirection.send(@method, "/my/key", :node => "mynode") + end +end + describe Puppet::Indirector::Indirection do describe "when initializing" do # (LAK) I've no idea how to test this, really. @@ -56,7 +138,8 @@ describe Puppet::Indirector::Indirection do @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" @name = :mything - @request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} + #@request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} + @request = mock 'instance' end it "should allow setting the ttl" do @@ -109,30 +192,14 @@ describe Puppet::Indirector::Indirection do end describe "and looking for a model instance" do - it "should let the :select_terminus method choose the terminus if the method is defined" do - # Define the method, so our respond_to? hook matches. - class << @indirection - def select_terminus(request) - end - end + before { @method = :find } - @indirection.expects(:select_terminus).with(@request).returns :test_terminus + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - @indirection.stubs(:check_authorization) - @terminus.expects(:find) - - @indirection.find(@request) - - end - - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:find).with(@request).returns(@instance) - @indirection.find(@request).should == @instance - end - - it "should return nil if nothing is returned by the terminus" do - @terminus.expects(:find).with(@request).returns(nil) - @indirection.find(@request).should be_nil + it "should return the results of the delegation" do + @terminus.expects(:find).returns(@instance) + @indirection.find("me").should equal(@instance) end it "should set the expiration date on any instances without one set" do @@ -143,7 +210,7 @@ describe Puppet::Indirector::Indirection do @instance.expects(:expiration).returns(nil) @instance.expects(:expiration=).with(:yay) - @indirection.find(@request) + @indirection.find("/my/key") end it "should not override an already-set expiration date on returned instances" do @@ -154,7 +221,7 @@ describe Puppet::Indirector::Indirection do @instance.expects(:expiration).returns(:yay) @instance.expects(:expiration=).never - @indirection.find(@request) + @indirection.find("/my/key") end describe "when caching is enabled" do @@ -166,24 +233,32 @@ describe Puppet::Indirector::Indirection do end it "should first look in the cache for an instance" do - @terminus.expects(:find).never - @cache.expects(:find).with(@request).returns @instance + @terminus.stubs(:find).never + @cache.expects(:find).returns @instance - @indirection.find(@request) + @indirection.find("/my/key") + end + + it "should use a request to look in the cache for cached objects" do + @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns @instance + + @cache.stubs(:save) + + @indirection.find("/my/key") end it "should return the cached object if it is not expired" do @instance.stubs(:expired?).returns false @cache.stubs(:find).returns @instance - @indirection.find(@request).should equal(@instance) + @indirection.find("/my/key").should equal(@instance) end it "should send a debug log if it is using the cached object" do Puppet.expects(:debug) @cache.stubs(:find).returns @instance - @indirection.find(@request) + @indirection.find("/my/key") end it "should not return the cached object if it is expired" do @@ -191,7 +266,7 @@ describe Puppet::Indirector::Indirection do @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil - @indirection.find(@request).should be_nil + @indirection.find("/my/key").should be_nil end it "should send an info log if it is using the cached object" do @@ -200,67 +275,59 @@ describe Puppet::Indirector::Indirection do @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil - @indirection.find(@request) + @indirection.find("/my/key") end it "should cache any objects not retrieved from the cache" do - @cache.expects(:find).with(@request).returns nil + @cache.expects(:find).returns nil - @terminus.expects(:find).with(@request).returns(@instance) + @terminus.expects(:find).returns(@instance) @cache.expects(:save) - @request.expects(:clone).returns(stub('newreq', :instance= => nil)) - - @indirection.find(@request) + @indirection.find("/my/key") end - it "should cache a clone of the request with the instance set to the cached object" do - @cache.expects(:find).with(@request).returns nil + it "should use a request to look in the cache for cached objects" do + @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns nil - newreq = mock 'request' - @request.expects(:clone).returns newreq - newreq.expects(:instance=).with(@instance) + @terminus.stubs(:find).returns(@instance) + @cache.stubs(:save) - @terminus.expects(:find).with(@request).returns(@instance) - @cache.expects(:save).with(newreq) + @indirection.find("/my/key") + end - @indirection.find(@request) + it "should cache the instance using a request with the instance set to the cached object" do + @cache.stubs(:find).returns nil + + @terminus.stubs(:find).returns(@instance) + + @cache.expects(:save).with { |r| r.method == :save and r.instance == @instance } + + @indirection.find("/my/key") end it "should send an info log that the object is being cached" do @cache.stubs(:find).returns nil - @request.expects(:clone).returns(stub('newreq', :instance= => nil)) - @terminus.stubs(:find).returns(@instance) @cache.stubs(:save) Puppet.expects(:info) - @indirection.find(@request) + @indirection.find("/my/key") end end end describe "and storing a model instance" do - it "should let the :select_terminus method choose the terminus if the method is defined" do - # Define the method, so our respond_to? hook matches. - class << @indirection - def select_terminus(req) - end - end + before { @method = :save } - @indirection.expects(:select_terminus).with(@request).returns :test_terminus + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - @indirection.stubs(:check_authorization) - @terminus.expects(:save) - - @indirection.save(@request) - end - - it "should let the appropriate terminus store the instance" do - @terminus.expects(:save).with(@request).returns(@instance) - @indirection.save(@request).should == @instance + it "should return nil" do + @terminus.stubs(:save) + @indirection.save(@instance).should be_nil end describe "when caching is enabled" do @@ -271,38 +338,27 @@ describe Puppet::Indirector::Indirection do @instance.stubs(:expired?).returns false end - it "should save the object to the cache" do - @cache.expects(:save).with(@request) + it "should use a request to save the object to the cache" do + request = stub 'request', :instance => @instance, :options => {} + + @indirection.expects(:request).returns request + + @cache.expects(:save).with(request) @terminus.stubs(:save) - @indirection.save(@request) + @indirection.save(@instance) end end end describe "and removing a model instance" do - it "should let the :select_terminus method choose the terminus if the method is defined" do - # Define the method, so our respond_to? hook matches. - class << @indirection - def select_terminus(request) - end - end + before { @method = :destroy } - @indirection.expects(:select_terminus).with(@request).returns :test_terminus - - @indirection.stubs(:check_authorization) - @terminus.expects(:destroy) - - @indirection.destroy(@request) - end - - it "should delegate the instance removal to the appropriate terminus" do - @terminus.expects(:destroy).with(@request) - @indirection.destroy(@request) - end + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" it "should return nil" do @terminus.stubs(:destroy) - @indirection.destroy(@request).should be_nil + @indirection.destroy("/my/key").should be_nil end describe "when caching is enabled" do @@ -313,112 +369,104 @@ describe Puppet::Indirector::Indirection do @instance.stubs(:expired?).returns false end - it "should destroy any found object in the cache" do + it "should use a request instance to search in and remove objects from the cache" do + destroy = stub 'destroy_request', :key => "/my/key", :options => {} + find = stub 'destroy_request', :key => "/my/key", :options => {} + + @indirection.expects(:request).with(:destroy, "/my/key").returns destroy + @indirection.expects(:request).with(:find, "/my/key").returns find + cached = mock 'cache' - @cache.expects(:find).with(@request).returns cached - @cache.expects(:destroy).with(@request) + + @cache.expects(:find).with(find).returns cached + @cache.expects(:destroy).with(destroy) + @terminus.stubs(:destroy) - @indirection.destroy(@request) + @indirection.destroy("/my/key") end end end describe "and searching for multiple model instances" do - it "should let the :select_terminus method choose the terminus if the method is defined" do - # Define the method, so our respond_to? hook matches. - class << @indirection - def select_terminus(req) - end - end + before { @method = :search } - @indirection.expects(:select_terminus).with(@request).returns :test_terminus + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - @indirection.stubs(:check_authorization) - @terminus.expects(:search) + it "should set the expiration date on any instances without one set" do + @terminus.stubs(:search).returns([@instance]) - @indirection.search(@request) + @indirection.expects(:expiration).returns :yay + + @instance.expects(:expiration).returns(nil) + @instance.expects(:expiration=).with(:yay) + + @indirection.search("/my/key") end - it "should let the appropriate terminus find the matching instances" do - @terminus.expects(:search).with(@request).returns(@instance) - @indirection.search(@request).should == @instance + it "should not override an already-set expiration date on returned instances" do + @terminus.stubs(:search).returns([@instance]) + + @indirection.expects(:expiration).never + + @instance.expects(:expiration).returns(:yay) + @instance.expects(:expiration=).never + + @indirection.search("/my/key") + end + + it "should return the results of searching in the terminus" do + @terminus.expects(:search).returns([@instance]) + @indirection.search("/my/key").should == [@instance] end end - describe "and an authorization hook is present" do - before do - # So the :respond_to? turns out correctly. - class << @terminus - def authorized? - end + describe "and expiring a model instance" do + describe "when caching is not enabled" do + it "should do nothing" do + @cache_class.expects(:new).never + + @indirection.expire("/my/key") end - - @request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {:node => "mynode"} end - it "should not check authorization if a node name is not provided" do - @terminus.expects(:authorized?).never - @terminus.stubs(:find) + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) - # The quotes are necessary here, else it looks like a block. - @request.stubs(:options).returns({}) - @indirection.find(@request) - end + @instance.stubs(:expired?).returns false + end - it "should pass the request to the terminus's authorization method" do - @terminus.expects(:authorized?).with(@request).returns(true) - @terminus.stubs(:find) + it "should do nothing if no such instance is cached" do + @cache.expects(:find).returns nil - @indirection.find(@request) - end + @indirection.expire("/my/key") + end - it "should fail while finding instances if authorization returns false" do - @terminus.expects(:authorized?).returns(false) - @terminus.stubs(:find) - proc { @indirection.find(@request) }.should raise_error(ArgumentError) - end + it "should set the cached instance's expiration to a time in the past" do + cached = mock 'cached' - it "should continue finding instances if authorization returns true" do - @terminus.expects(:authorized?).returns(true) - @terminus.stubs(:find) - @indirection.find(@request) - end + @cache.expects(:find).returns cached + @cache.stubs(:save) - it "should fail while saving instances if authorization returns false" do - @terminus.expects(:authorized?).returns(false) - @terminus.stubs(:save) - proc { @indirection.save(@request) }.should raise_error(ArgumentError) - end + cached.expects(:expiration=).with { |t| t < Time.now } - it "should continue saving instances if authorization returns true" do - @terminus.expects(:authorized?).returns(true) - @terminus.stubs(:save) - @indirection.save(@request) - end + @indirection.expire("/my/key") + end - it "should fail while destroying instances if authorization returns false" do - @terminus.expects(:authorized?).returns(false) - @terminus.stubs(:destroy) - proc { @indirection.destroy(@request) }.should raise_error(ArgumentError) - end + it "should save the now expired instance back into the cache" do + cached = stub 'cached', :expiration= => nil - it "should continue destroying instances if authorization returns true" do - @terminus.expects(:authorized?).returns(true) - @terminus.stubs(:destroy) - @indirection.destroy(@request) - end + @cache.expects(:find).returns cached - it "should fail while searching for instances if authorization returns false" do - @terminus.expects(:authorized?).returns(false) - @terminus.stubs(:search) - proc { @indirection.search(@request) }.should raise_error(ArgumentError) - end + cached.expects(:expiration=).with { |t| t < Time.now } - it "should continue searching for instances if authorization returns true" do - @terminus.expects(:authorized?).returns(true) - @terminus.stubs(:search) - @indirection.search(@request) + @cache.expects(:save).with(cached) + + @indirection.expire("/my/key") + end end end From 738889ba027b894867209d5175c716f9a2cc54c6 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 10 Apr 2008 14:44:14 -0500 Subject: [PATCH 42/48] Fixing the expire method (it wasn't using a request internally), and fixing the Facts class so it auto-expires any associated cached nodes when facts are saved. --- lib/puppet/indirector/indirection.rb | 6 +++-- lib/puppet/node/facts.rb | 10 ++++++- spec/integration/node/facts.rb | 9 ++++++- spec/unit/indirector/indirection.rb | 40 +++++++++++++++++++++------- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index ccb7ef6e7..15358a801 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -180,12 +180,14 @@ class Puppet::Indirector::Indirection return nil unless cache? - return nil unless instance = cache.find(key, *args) + return nil unless instance = cache.find(request(:find, key, *args)) + + Puppet.info "Expiring the %s cache of %s" % [self.name, instance.name] # Set an expiration date in the past instance.expiration = Time.now - 60 - cache.save(instance, *args) + cache.save(request(:save, instance, *args)) end # Search for an instance in the appropriate terminus, caching the diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb index c60be3fcf..8ee90b4ac 100755 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -8,8 +8,16 @@ class Puppet::Node::Facts # the node sources. extend Puppet::Indirector + # We want to expire any cached nodes if the facts are saved. + module NodeExpirer + def save(instance, *args) + Puppet::Node.expire(instance.name) + super + end + end + # Use the node source as the indirection terminus. - indirects :facts, :terminus_class => :facter + indirects :facts, :terminus_class => :facter, :extend => NodeExpirer attr_accessor :name, :values diff --git a/spec/integration/node/facts.rb b/spec/integration/node/facts.rb index d065918be..c2f876578 100755 --- a/spec/integration/node/facts.rb +++ b/spec/integration/node/facts.rb @@ -10,7 +10,14 @@ describe Puppet::Node::Facts do after { Puppet::Node::Facts.indirection.clear_cache } it "should expire any cached node instances when it is saved" do - raise "This test fails" + Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml + terminus = Puppet::Node::Facts.indirection.terminus(:yaml) + + terminus.expects(:save) + Puppet::Node.expects(:expire).with("me") + + facts = Puppet::Node::Facts.new("me") + facts.save end it "should be able to delegate to the :yaml terminus" do diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index 06d9fcb6d..e8ab9633b 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -437,6 +437,13 @@ describe Puppet::Indirector::Indirection do @cache_class.expects(:new).returns(@cache) @instance.stubs(:expired?).returns false + + @cached = stub 'cached', :expiration= => nil, :name => "/my/key" + end + + it "should use a request to find within the cache" do + @cache.expects(:find).with { |r| r.is_a?(Puppet::Indirector::Request) and r.method == :find } + @indirection.expire("/my/key") end it "should do nothing if no such instance is cached" do @@ -445,25 +452,40 @@ describe Puppet::Indirector::Indirection do @indirection.expire("/my/key") end - it "should set the cached instance's expiration to a time in the past" do - cached = mock 'cached' - - @cache.expects(:find).returns cached + it "should log that it is expiring any found instance" do + @cache.expects(:find).returns @cached @cache.stubs(:save) - cached.expects(:expiration=).with { |t| t < Time.now } + Puppet.expects(:info) + + @indirection.expire("/my/key") + end + + it "should set the cached instance's expiration to a time in the past" do + @cache.expects(:find).returns @cached + @cache.stubs(:save) + + @cached.expects(:expiration=).with { |t| t < Time.now } @indirection.expire("/my/key") end it "should save the now expired instance back into the cache" do - cached = stub 'cached', :expiration= => nil + @cache.expects(:find).returns @cached - @cache.expects(:find).returns cached + @cached.expects(:expiration=).with { |t| t < Time.now } - cached.expects(:expiration=).with { |t| t < Time.now } + @cache.expects(:save) - @cache.expects(:save).with(cached) + @indirection.expire("/my/key") + end + + it "should use a request to save the expired resource to the cache" do + @cache.expects(:find).returns @cached + + @cached.expects(:expiration=).with { |t| t < Time.now } + + @cache.expects(:save).with { |r| r.is_a?(Puppet::Indirector::Request) and r.instance == @cached and r.method == :save }.returns(@cached) @indirection.expire("/my/key") end From d91b6d8ac081179c467c7c4e9ea7ac067aa22baf Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 10 Apr 2008 15:48:45 -0500 Subject: [PATCH 43/48] Fixing #1173 -- classes and definitions can now have the same name as a directory with no failures. --- spec/unit/{other/modules.rb => module.rb} | 9 +++++++++ 1 file changed, 9 insertions(+) rename spec/unit/{other/modules.rb => module.rb} (94%) diff --git a/spec/unit/other/modules.rb b/spec/unit/module.rb similarity index 94% rename from spec/unit/other/modules.rb rename to spec/unit/module.rb index 26ca3907d..4115ee776 100755 --- a/spec/unit/other/modules.rb +++ b/spec/unit/module.rb @@ -171,6 +171,15 @@ describe Puppet::Module, " when searching for manifests in a found module" do Puppet::Module.find_manifests("mymod/yay/*.pp").should == %w{/one /two} end + it "should not return directories" do + Puppet.settings.expects(:value).with(:modulepath, nil).returns("/my/modules") + File.stubs(:directory?).returns(true) + Dir.expects(:glob).with("/my/modules/mymod/manifests/yay/*.pp").returns(%w{/one /two}) + FileTest.expects(:directory?).with("/one").returns false + FileTest.expects(:directory?).with("/two").returns true + Puppet::Module.find_manifests("mymod/yay/*.pp").should == %w{/one} + end + it "should default to the 'init.pp' file in the manifests directory" do Puppet.settings.expects(:value).with(:modulepath, nil).returns("/my/modules") File.stubs(:directory?).returns(true) From 3718b6410b6d0312027308c809ca5bf623538498 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 10 Apr 2008 15:49:12 -0500 Subject: [PATCH 44/48] Fixing #1173 -- classes and definitions can now have the same name as a directory with no failures. --- CHANGELOG | 10 ++++++++++ lib/puppet/module.rb | 2 +- spec/unit/module.rb | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 44299970b..80fa83c6f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,13 @@ + Fixing #1173 -- classes and definitions can now have the same + name as a directory with no failures. + + Saving new facts now expires any cached node information. + + Switching how caching is handled, so that objects now all + have an expiration date associated with them. This makes it + much easier to know whether a given cached object should be used + or if it should be regenerated. + Changing the default environment to production. 0.24.4 diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb index 54212710d..b86931664 100644 --- a/lib/puppet/module.rb +++ b/lib/puppet/module.rb @@ -132,7 +132,7 @@ class Puppet::Module def manifests(rest) rest ||= "init.pp" p = File::join(path, MANIFESTS, rest) - files = Dir.glob(p) + files = Dir.glob(p).reject { |f| FileTest.directory?(f) } if files.size == 0 files = Dir.glob(p + ".pp") end diff --git a/spec/unit/module.rb b/spec/unit/module.rb index 4115ee776..06b2d016d 100755 --- a/spec/unit/module.rb +++ b/spec/unit/module.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/../spec_helper' describe Puppet::Module, " when building its search path" do include PuppetTest From 376628d117f43d3319bfa1cc65d54c2d7af10b42 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 10 Apr 2008 23:17:36 -0500 Subject: [PATCH 45/48] Removed the code from the client that tries to avoid recompiling the catalog. The client will now always recompile, assuming it can reach the server. It will still use the cached config if there's a failure. --- CHANGELOG | 5 ++ lib/puppet/network/client/master.rb | 58 +-------------- spec/unit/network/client/master.rb | 25 ------- test/network/client/master.rb | 110 ---------------------------- 4 files changed, 7 insertions(+), 191 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 80fa83c6f..2e1a3d709 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ + Removed the code from the client that tries to avoid recompiling + the catalog. The client will now always recompile, assuming it + can reach the server. It will still use the cached config if + there's a failure. + Fixing #1173 -- classes and definitions can now have the same name as a directory with no failures. diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index 955acbfb5..22bb3fa4e 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -49,6 +49,8 @@ class Puppet::Network::Client::Master < Puppet::Network::Client end # Return the list of dynamic facts as an array of symbols + # NOTE:LAK(2008/04/10): This code is currently unused, since we now always + # recompile. def self.dynamic_facts # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] x = Puppet.settings[:dynamicfacts].split(/\s*,\s*/).collect { |fact| fact.downcase } @@ -97,31 +99,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client end end - # Check whether our catalog is up to date - def fresh?(facts) - if Puppet[:ignorecache] - Puppet.notice "Ignoring cache" - return false - end - unless self.compile_time - Puppet.debug "No cached compile time" - return false - end - if facts_changed?(facts) - Puppet.info "Facts have changed; recompiling" unless local? - return false - end - - newcompile = @driver.freshness - # We're willing to give a 2 second drift - if newcompile - @compile_time.to_i < 1 - return true - else - Puppet.debug "Server compile time is %s vs %s" % [newcompile, @compile_time.to_i] - return false - end - end - # Let the daemon run again, freely in the filesystem. Frolick, little # daemon! def enable @@ -148,11 +125,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client # Retrieve the plugins. getplugins() if Puppet[:pluginsync] - if (self.catalog or FileTest.exist?(self.cachefile)) and self.fresh?(facts) - Puppet.info "Configuration is up to date" - return if use_cached_config - end - Puppet.debug("Retrieving catalog") # If we can't retrieve the catalog, just return, which will either @@ -451,32 +423,6 @@ class Puppet::Network::Client::Master < Puppet::Network::Client loadfacts() - # Have the facts changed since we last compiled? - def facts_changed?(facts) - oldfacts = (Puppet::Util::Storage.cache(:configuration)[:facts] || {}).dup - newfacts = facts.dup - self.class.dynamic_facts.each do |fact| - [oldfacts, newfacts].each do |facthash| - facthash.delete(fact) if facthash.include?(fact) - end - end - - if oldfacts == newfacts - return false - else -# unless oldfacts -# puts "no old facts" -# return true -# end -# newfacts.keys.each do |k| -# unless newfacts[k] == oldfacts[k] -# puts "%s: %s vs %s" % [k, newfacts[k], oldfacts[k]] -# end -# end - return true - end - end - # Actually retrieve the catalog, either from the server or from a # local master. def get_actual_config(facts) diff --git a/spec/unit/network/client/master.rb b/spec/unit/network/client/master.rb index 7bf755d88..c0ad7562f 100755 --- a/spec/unit/network/client/master.rb +++ b/spec/unit/network/client/master.rb @@ -36,30 +36,6 @@ describe Puppet::Network::Client::Master, " when retrieving the catalog" do proc { @client.getconfig }.should raise_error(Puppet::Network::ClientError) end - it "should use the cached catalog if it is up to date" do - file = "/path/to/cachefile" - @client.stubs(:cachefile).returns(file) - FileTest.expects(:exist?).with(file).returns(true) - @client.expects(:fresh?).with(@facts).returns true - @client.class.stubs(:facts).returns(@facts) - @client.expects(:use_cached_config).returns(true) - Puppet.stubs(:info) - - @client.getconfig - end - - it "should log that the catalog does not need a recompile" do - file = "/path/to/cachefile" - @client.stubs(:cachefile).returns(file) - FileTest.stubs(:exist?).with(file).returns(true) - @client.stubs(:fresh?).with(@facts).returns true - @client.stubs(:use_cached_config).returns(true) - @client.class.stubs(:facts).returns(@facts) - Puppet.expects(:info).with { |m| m.include?("up to date") } - - @client.getconfig - end - it "should retrieve plugins if :pluginsync is enabled" do file = "/path/to/cachefile" @client.stubs(:cachefile).returns(file) @@ -69,7 +45,6 @@ describe Puppet::Network::Client::Master, " when retrieving the catalog" do @client.expects(:getplugins) @client.stubs(:get_actual_config).returns(nil) FileTest.stubs(:exist?).with(file).returns(true) - @client.stubs(:fresh?).with(@facts).returns true @client.stubs(:use_cached_config).returns(true) @client.class.stubs(:facts).returns(@facts) @client.getconfig diff --git a/test/network/client/master.rb b/test/network/client/master.rb index 14c7dd852..dc2140e62 100755 --- a/test/network/client/master.rb +++ b/test/network/client/master.rb @@ -305,44 +305,6 @@ end assert_equal(RUBY_VERSION, facts["rubyversion"], "ruby version did not get added") end - # #424 - def test_caching_of_compile_time - file = tempfile() - manifest = tempfile() - File.open(manifest, "w") { |f| f.puts "file { '#{file}': content => yay }" } - - Puppet::Node::Facts.indirection.stubs(:save) - - driver = mkmaster(:Manifest => manifest) - driver.local = false - master = mkclient(driver) - - # We have to make everything thinks it's remote, because there's no local caching info - master.local = false - - assert(! master.fresh?(master.class.facts), - "Considered fresh with no compile at all") - - assert_nothing_raised { master.run } - assert(master.fresh?(master.class.facts), - "not considered fresh after compile") - - # Now make sure the config time is cached - assert(master.compile_time, "No stored config time") - assert_equal(master.compile_time, Puppet::Util::Storage.cache(:configuration)[:compile_time], "times did not match") - time = master.compile_time - master.clear - File.unlink(file) - Puppet::Util::Storage.store - - # Now make a new master - Puppet::Util::Storage.clear - master = mkclient(driver) - master.run - assert_equal(time, master.compile_time, "time was not retrieved from cache") - assert(FileTest.exists?(file), "file was not created on second run") - end - # #540 - make sure downloads aren't affected by noop def test_download_in_noop source = tempfile @@ -384,44 +346,6 @@ end "Tried to load cache when it is non-existent") end - # #519 - cache the facts so that we notice if they change. - def test_factchanges_cause_recompile - $value = "one" - Facter.add(:testfact) do - setcode { $value } - end - assert_equal("one", Facter.value(:testfact), "fact was not set correctly") - master = mkclient - master.local = false - driver = master.send(:instance_variable_get, "@driver") - driver.local = false - - Puppet::Node::Facts.indirection.stubs(:save) - - assert_nothing_raised("Could not compile config") do - master.getconfig - end - - $value = "two" - Facter.clear - Facter.loadfacts - Facter.add(:testfact) do - setcode { $value } - end - facts = master.class.facts - assert_equal("two", Facter.value(:testfact), "fact did not change") - - assert(master.send(:facts_changed?, facts), - "master does not think facts changed") - assert(! master.fresh?(facts), - "master is considered fresh after facts changed") - - assert_nothing_raised("Could not recompile when facts changed") do - master.getconfig - end - - end - def test_locking master = mkclient @@ -455,40 +379,6 @@ end assert_equal(100, master.timeout, "Did not get changed integer default value for timeout on second run") end - # #569 -- Make sure we can ignore dynamic facts. - def test_dynamic_facts - client = mkclient - - assert_equal(%w{memorysize memoryfree swapsize swapfree}, client.class.dynamic_facts, - "Did not get correct defaults for dynamic facts") - - # Cache some values for comparison - cached = {"one" => "yep", "two" => "nope"} - Puppet::Util::Storage.cache(:configuration)[:facts] = cached - - assert(! client.send(:facts_changed?, cached), "Facts incorrectly considered to be changed") - - # Now add some values to the passed result and make sure we get a positive - newfacts = cached.dup - newfacts["changed"] = "something" - - assert(client.send(:facts_changed?, newfacts), "Did not catch changed fact") - - # Now add a dynamic fact and make sure it's ignored - newfacts = cached.dup - newfacts["memorysize"] = "something" - - assert(! client.send(:facts_changed?, newfacts), "Dynamic facts resulted in a false positive") - - # And try it with both - cached["memorysize"] = "something else" - assert(! client.send(:facts_changed?, newfacts), "Dynamic facts resulted in a false positive") - - # And finally, with only in the cache - newfacts.delete("memorysize") - assert(! client.send(:facts_changed?, newfacts), "Dynamic facts resulted in a false positive") - end - def test_splay client = mkclient From 2925ad1cb9aa820785afca58c4fb6e34274dadd4 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 10 Apr 2008 23:18:20 -0500 Subject: [PATCH 46/48] Fixed #1184 -- definitions now autoload correctly all of the time. --- CHANGELOG | 2 ++ lib/puppet/parser/parser_support.rb | 8 ++++++-- test/language/parser.rb | 9 ++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2e1a3d709..2e57e5f32 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ + Fixed #1184 -- definitions now autoload correctly all of the time. + Removed the code from the client that tries to avoid recompiling the catalog. The client will now always recompile, assuming it can reach the server. It will still use the cached config if diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index b86a4792b..d70722fdd 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -237,7 +237,9 @@ class Puppet::Parser::Parser end end - return true if classes.include?(classname) + # We don't know whether we're looking for a class or definition, so we have + # to test for both. + return true if classes.include?(classname) || definitions.include?(classname) unless @loaded.include?(filename) @loaded << filename @@ -249,7 +251,9 @@ class Puppet::Parser::Parser # We couldn't load the file end end - return classes.include?(classname) + # We don't know whether we're looking for a class or definition, so we have + # to test for both. + return classes.include?(classname) || definitions.include?(classname) end # Split an fq name into a namespace and name diff --git a/test/language/parser.rb b/test/language/parser.rb index 2161a33f5..cdd13297a 100755 --- a/test/language/parser.rb +++ b/test/language/parser.rb @@ -1141,7 +1141,6 @@ file { "/tmp/yayness": name = "sub" mk_module(modname, :init => %w{separate}, :sub => %w{separate::sub}) - Puppet.err :yay # First try it with a namespace klass = parser.findclass("separate", name) assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from separate file with a namespace") @@ -1171,6 +1170,14 @@ file { "/tmp/yayness": klass = parser.findclass("", "alone::sub") assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from alone file with no namespace") assert_equal("alone::sub", klass.classname, "Incorrect class was returned") + + # and with the definition in its own file + name = "mymod" + mk_module(name, :define => true, :mydefine => ["mymod::mydefine"]) + + klass = parser.finddefine("", "mymod::mydefine") + assert_instance_of(AST::Definition, klass, "Did not autoload definition from its own file") + assert_equal("mymod::mydefine", klass.classname, "Incorrect definition was returned") end # Make sure class, node, and define methods are case-insensitive From 4aaad26509b2dc9bd45782ec45231e6ea53a4a37 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 11 Apr 2008 11:34:51 -0500 Subject: [PATCH 47/48] Modified the 'master' handler to use the Catalog class to compile node configurations, rather than using the Configuration handler, which was never used directly. I removed the Configuration handler as a result. Modified the 'master' handler (responsible for sending configurations to clients) to always return Time.now as its compile date, so configurations will always get recompiled. --- CHANGELOG | 9 + lib/puppet/indirector/catalog/compiler.rb | 18 +- lib/puppet/network/handler/configuration.rb | 184 -------------------- lib/puppet/network/handler/master.rb | 21 +-- lib/puppet/parser/resource.rb | 6 + spec/unit/indirector/catalog/compiler.rb | 54 +----- spec/unit/parser/resource.rb | 7 + test/network/handler/configuration.rb | 160 ----------------- test/network/handler/master.rb | 53 +----- 9 files changed, 38 insertions(+), 474 deletions(-) delete mode 100644 lib/puppet/network/handler/configuration.rb delete mode 100755 test/network/handler/configuration.rb diff --git a/CHANGELOG b/CHANGELOG index 2e57e5f32..8dad0d977 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ + Modified the 'master' handler to use the Catalog class to + compile node configurations, rather than using the Configuration + handler, which was never used directly. I removed the Configuration + handler as a result. + + Modified the 'master' handler (responsible for sending configurations + to clients) to always return Time.now as its compile date, so + configurations will always get recompiled. + Fixed #1184 -- definitions now autoload correctly all of the time. Removed the code from the client that tries to avoid recompiling diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index ecc340f75..2b5e8d912 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -19,7 +19,7 @@ class Puppet::Node::Catalog::Compiler < Puppet::Indirector::Code end if catalog = compile(node) - return catalog.to_transportable + return catalog else # This shouldn't actually happen; we should either return # a config or raise an exception. @@ -44,22 +44,6 @@ class Puppet::Node::Catalog::Compiler < Puppet::Indirector::Code $0 =~ /puppetmasterd/ end - # Return the catalog version. Here we're returning the - # latest of the node, fact, or parse date. These are the - # three things that go into compiling a client catalog, - # so changes in any of them result in changes. - # LAK:FIXME Note that this only works when all three sources - # use timestamps; once one of them moves to using real versions, - # the comparison stops working. - def version(key) - if node = Puppet::Node.find_by_any_name(key) - return [Puppet::Node.version(key).to_f, Puppet::Node::Facts.version(key).to_f, interpreter.catalog_version(node).to_f].sort[-1] - else - # This is the standard for "got nothing for ya". - 0 - end - end - private # Add any extra data necessary to the node. diff --git a/lib/puppet/network/handler/configuration.rb b/lib/puppet/network/handler/configuration.rb deleted file mode 100644 index 8168ce1d5..000000000 --- a/lib/puppet/network/handler/configuration.rb +++ /dev/null @@ -1,184 +0,0 @@ -require 'openssl' -require 'puppet' -require 'puppet/parser/interpreter' -require 'puppet/sslcertificates' -require 'xmlrpc/server' -require 'yaml' - -class Puppet::Network::Handler - class Configuration < Handler - desc "Puppet's configuration compilation interface. Passed a node name - or other key, retrieves information about the node (using the ``node_source``) - and returns a compiled configuration." - - include Puppet::Util - - attr_accessor :local, :classes - - @interface = XMLRPC::Service::Interface.new("configuration") { |iface| - iface.add_method("string configuration(string)") - iface.add_method("string version()") - } - - # Compile a node's configuration. - def configuration(key, client = nil, clientip = nil) - # If we want to use the cert name as our key - if Puppet[:node_name] == 'cert' and client - key = client - end - - # Note that this is reasonable, because either their node source should actually - # know about the node, or they should be using the ``none`` node source, which - # will always return data. - unless node = Puppet::Node.find_by_any_name(key) - raise Puppet::Error, "Could not find node '%s'" % key - end - - # Add any external data to the node. - add_node_data(node) - - configuration = compile(node) - - return translate(configuration) - end - - def initialize(options = {}) - options.each do |param, value| - case param - when :Classes: @classes = value - when :Local: self.local = value - else - raise ArgumentError, "Configuration handler does not accept %s" % param - end - end - - set_server_facts - end - - # Are we running locally, or are our clients networked? - def local? - self.local - end - - # Return the configuration version. - def version(client = nil, clientip = nil) - if client and node = Puppet::Node.find_by_any_name(client) - update_node_check(node) - return interpreter.configuration_version(node) - else - # Just return something that will always result in a recompile, because - # this is local. - return (Time.now + 1000).to_i - end - end - - private - - # Add any extra data necessary to the node. - def add_node_data(node) - # Merge in our server-side facts, so they can be used during compilation. - node.merge(@server_facts) - - # Add any specified classes to the node's class list. - if @classes - @classes.each do |klass| - node.classes << klass - end - end - end - - # Compile the actual configuration. - def compile(node) - # Pick the benchmark level. - if local? - level = :none - else - level = :notice - end - - # Ask the interpreter to compile the configuration. - str = "Compiled configuration for %s" % node.name - if node.environment - str += " in environment %s" % node.environment - end - config = nil - benchmark(level, "Compiled configuration for %s" % node.name) do - begin - config = interpreter.compile(node) - rescue => detail - # If we're local, then we leave it to the local system - # to handle error reporting, but otherwise we do it here - # so the interpreter doesn't need to know if the parser - # is local or not. - Puppet.err(detail.to_s) unless local? - raise - end - end - - return config - end - - # Create our interpreter object. - def create_interpreter - return Puppet::Parser::Interpreter.new - end - - # Create/return our interpreter. - def interpreter - unless defined?(@interpreter) and @interpreter - @interpreter = create_interpreter - end - @interpreter - end - - # Initialize our server fact hash; we add these to each client, and they - # won't change while we're running, so it's safe to cache the values. - def set_server_facts - @server_facts = {} - - # Add our server version to the fact list - @server_facts["serverversion"] = Puppet.version.to_s - - # And then add the server name and IP - {"servername" => "fqdn", - "serverip" => "ipaddress" - }.each do |var, fact| - if value = Facter.value(fact) - @server_facts[var] = value - else - Puppet.warning "Could not retrieve fact %s" % fact - end - end - - if @server_facts["servername"].nil? - host = Facter.value(:hostname) - if domain = Facter.value(:domain) - @server_facts["servername"] = [host, domain].join(".") - else - @server_facts["servername"] = host - end - end - end - - # Translate our configuration appropriately for sending back to a client. - def translate(config) - if local? - config - else - CGI.escape(config.to_yaml(:UseBlock => true)) - end - end - - # Mark that the node has checked in. FIXME this needs to be moved into - # the Node class, or somewhere that's got abstract backends. - def update_node_check(node) - if Puppet.features.rails? and Puppet[:storeconfigs] - Puppet::Rails.connect - - host = Puppet::Rails::Host.find_or_create_by_name(node.name) - host.last_freshcheck = Time.now - host.save - end - end - end -end diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index dabfaca50..851ccc7b2 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -23,8 +23,8 @@ class Puppet::Network::Handler # Tell a client whether there's a fresh config for it def freshness(client = nil, clientip = nil) - client ||= Facter.value("hostname") - config_handler.version(client, clientip) + # Always force a recompile. Newer clients shouldn't do this (as of April 2008). + Time.now end def initialize(hash = {}) @@ -51,8 +51,6 @@ class Puppet::Network::Handler if hash.include?(:Classes) args[:Classes] = hash[:Classes] end - - @config_handler = Puppet::Network::Handler.handler(:configuration).new(args) end # Call our various handlers; this handler is getting deprecated. @@ -63,13 +61,9 @@ class Puppet::Network::Handler # Pass the facts to the fact handler Puppet::Node::Facts.new(client, facts).save unless local? - # And get the configuration from the config handler - config = nil - benchmark(:notice, "Compiled configuration for %s" % client) do - config = config_handler.configuration(client) - end + catalog = Puppet::Node::Catalog.find(client) - return translate(config.extract) + return translate(catalog.extract) end private @@ -93,13 +87,6 @@ class Puppet::Network::Handler return client, clientip end - def config_handler - unless defined? @config_handler - @config_handler = Puppet::Network::Handler.handler(:config).new :local => local? - end - @config_handler - end - # def decode_facts(facts) if @local diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 4b48ff6cf..d214a60ee 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -303,6 +303,12 @@ class Puppet::Parser::Resource return bucket end + # Convert this resource to a RAL resource. We hackishly go via the + # transportable stuff. + def to_type + to_trans.to_type + end + def to_transobject # Now convert to a transobject obj = Puppet::TransObject.new(@ref.title, @ref.type) diff --git a/spec/unit/indirector/catalog/compiler.rb b/spec/unit/indirector/catalog/compiler.rb index 57bbcdbd0..083a9ced5 100755 --- a/spec/unit/indirector/catalog/compiler.rb +++ b/spec/unit/indirector/catalog/compiler.rb @@ -145,10 +145,10 @@ describe Puppet::Node::Catalog::Compiler, " when creating catalogs" do it "should return the results of compiling as the catalog" do config = mock 'config' - result = mock 'result', :to_transportable => :catalog + result = mock 'result' @compiler.interpreter.expects(:compile).with(@node).returns(result) - @compiler.find(@request).should == :catalog + @compiler.find(@request).should equal(result) end it "should benchmark the compile process" do @@ -160,53 +160,3 @@ describe Puppet::Node::Catalog::Compiler, " when creating catalogs" do @compiler.find(@request) end end - -describe Puppet::Node::Catalog::Compiler, " when determining a client's available catalog version" do - before do - Puppet::Node::Facts.stubs(:find).returns(nil) - Facter.stubs(:value).returns("whatever") - @catalog = Puppet::Node::Catalog::Compiler.new - @name = "johnny" - end - - it "should provide a mechanism for providing the version of a given client's catalog" do - @catalog.should respond_to(:version) - end - - it "should use the client's Facts version as the available catalog version if it is the most recent" do - Puppet::Node::Facts.stubs(:version).with(@name).returns(5) - Puppet::Node.expects(:version).with(@name).returns(3) - @catalog.interpreter.stubs(:catalog_version).returns(4) - - @catalog.version(@name).should == 5 - end - - it "should use the client's Node version as the available catalog version if it is the most recent" do - Puppet::Node::Facts.stubs(:version).with(@name).returns(3) - Puppet::Node.expects(:version).with(@name).returns(5) - @catalog.interpreter.stubs(:catalog_version).returns(4) - - @catalog.version(@name).should == 5 - end - - it "should use the last parse date as the available catalog version if it is the most recent" do - Puppet::Node::Facts.stubs(:version).with(@name).returns(3) - Puppet::Node.expects(:version).with(@name).returns(4) - @catalog.interpreter.stubs(:catalog_version).returns(5) - - @catalog.version(@name).should == 5 - end - - it "should return a version of 0 if no information on the node can be found" do - Puppet::Node.stubs(:find_by_any_name).returns(nil) - @catalog.version(@name).should == 0 - end - - it "should indicate when an update is available even if an input has clock skew" do - pending "Unclear how to implement this" - end - - it "should not indicate an available update when apparent updates are a result of clock skew" do - pending "Unclear how to implement this" - end -end diff --git a/spec/unit/parser/resource.rb b/spec/unit/parser/resource.rb index 776e9c742..6b2021916 100755 --- a/spec/unit/parser/resource.rb +++ b/spec/unit/parser/resource.rb @@ -64,6 +64,13 @@ describe Puppet::Parser::Resource do @resource[:one].should == "yay" end + it "should have a method for converting to a ral resource" do + trans = mock 'trans', :to_type => "yay" + @resource = mkresource + @resource.expects(:to_trans).returns trans + @resource.to_type.should == "yay" + end + describe "when initializing" do before do @arguments = {:type => "resource", :title => "testing", :scope => stub('scope', :source => mock('source'))} diff --git a/test/network/handler/configuration.rb b/test/network/handler/configuration.rb deleted file mode 100755 index 36c5d9e54..000000000 --- a/test/network/handler/configuration.rb +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../lib/puppettest' - -require 'puppettest' -require 'puppet/network/handler/configuration' - -class TestHandlerConfiguration < Test::Unit::TestCase - include PuppetTest - - Config = Puppet::Network::Handler.handler(:configuration) - - # Check all of the setup stuff. - def test_initialize - config = nil - assert_nothing_raised("Could not create local config") do - config = Config.new(:Local => true) - end - - assert(config.local?, "Config is not considered local after being started that way") - end - - # Test creation/returning of the interpreter - def test_interpreter - config = Config.new - - # First test the defaults - config.expects(:create_interpreter).returns(:interp) - assert_equal(:interp, config.send(:interpreter), "Did not return the interpreter") - - # Now run it again and make sure we get the same thing - assert_equal(:interp, config.send(:interpreter), "Did not cache the interpreter") - end - - def test_create_interpreter - config = Config.new(:Local => false) - args = {} - - # Try it first with defaults. - Puppet::Parser::Interpreter.expects(:new).returns(:interp) - assert_equal(:interp, config.send(:create_interpreter), "Did not return the interpreter") - end - - # Make sure node objects get appropriate data added to them. - def test_add_node_data - # First with no classes - config = Config.new - - fakenode = Object.new - # Set the server facts to something - config.instance_variable_set("@server_facts", :facts) - fakenode.expects(:merge).with(:facts) - config.send(:add_node_data, fakenode) - - # Now try it with classes. - config.classes = %w{a b} - list = [] - fakenode = Object.new - fakenode.expects(:merge).with(:facts) - fakenode.expects(:classes).returns(list).times(2) - config.send(:add_node_data, fakenode) - assert_equal(%w{a b}, list, "Did not add classes to node") - end - - def test_compile - config = Config.new - - # First do a local - node = mock 'node' - node.stubs(:name).returns(:mynode) - node.stubs(:environment).returns(:myenv) - - interp = mock 'interpreter' - interp.stubs(:environment) - interp.expects(:compile).with(node).returns(:config) - config.expects(:interpreter).returns(interp) - - Puppet.expects(:notice) # The log message from benchmarking - - assert_equal(:config, config.send(:compile, node), "Did not return config") - - # Now try it non-local - node = mock 'node' - node.stubs(:name).returns(:mynode) - node.stubs(:environment).returns(:myenv) - - interp = mock 'interpreter' - interp.stubs(:environment) - interp.expects(:compile).with(node).returns(:config) - - config = Config.new(:Local => true) - config.expects(:interpreter).returns(interp) - - assert_equal(:config, config.send(:compile, node), "Did not return config") - end - - def test_set_server_facts - config = Config.new - assert_nothing_raised("Could not call :set_server_facts") do - config.send(:set_server_facts) - end - facts = config.instance_variable_get("@server_facts") - %w{servername serverversion serverip}.each do |fact| - assert(facts.include?(fact), "Config did not set %s fact" % fact) - end - end - - def test_translate - # First do a local config - config = Config.new(:Local => true) - assert_equal(:plain, config.send(:translate, :plain), "Attempted to translate local config") - - # Now a non-local - config = Config.new(:Local => false) - assert(! config.local?, "Config wrongly thinks it's local") - obj = mock 'dumpee' - yamld = mock 'yaml' - obj.expects(:to_yaml).with(:UseBlock => true).returns(yamld) - CGI.expects(:escape).with(yamld).returns(:translated) - assert_equal(:translated, config.send(:translate, obj), "Did not return translated config") - end - - # Check that we're storing the node freshness into the rails db. Hackilicious. - def test_update_node_check - # This is stupid. - config = Config.new - node = Object.new - node.expects(:name).returns(:hostname) - now = Object.new - Time.expects(:now).returns(now) - host = Object.new - host.expects(:last_freshcheck=).with(now) - host.expects(:save) - - # Only test the case where rails is there - Puppet[:storeconfigs] = true - Puppet.features.expects(:rails?).returns(true) - Puppet::Rails.expects(:connect) - Puppet::Rails::Host.expects(:find_or_create_by_name).with(:hostname).returns(host) - - config.send(:update_node_check, node) - end - - def test_version - # First try the case where we can't look up the node - config = Config.new - node = Object.new - Puppet::Node.stubs(:find_by_any_name).with(:client).returns(false, node) - interp = Object.new - assert_instance_of(Bignum, config.version(:client), "Did not return configuration version") - - # And then when we find the node. - config = Config.new - config.expects(:update_node_check).with(node) - interp = Object.new - interp.expects(:configuration_version).returns(:version) - config.expects(:interpreter).returns(interp) - assert_equal(:version, config.version(:client), "Did not return configuration version") - end -end diff --git a/test/network/handler/master.rb b/test/network/handler/master.rb index 55522237b..e91ec2f47 100755 --- a/test/network/handler/master.rb +++ b/test/network/handler/master.rb @@ -13,52 +13,17 @@ class TestMaster < Test::Unit::TestCase Puppet::Indirector::Indirection.clear_cache end - # Make sure that files are reread when they change. - def test_filereread - # Start with a normal setting - Puppet[:filetimeout] = 15 + def test_freshness_is_always_now + master = Puppet::Network::Handler.master.new( + :Manifest => tempfile, + :UseNodes => true, + :Local => true + ) - manifest = mktestmanifest() + now1 = mock 'now1' + Time.expects(:now).returns(now1) - facts = Puppet::Network::Client.master.facts - # Store them, so we don't determine frshness based on facts. - Puppet::Util::Storage.cache(:configuration)[:facts] = facts - - file2 = @createdfile + "2" - @@tmpfiles << file2 - - client = master = nil - Puppet[:manifest] = manifest - assert_nothing_raised() { - # this is the default server setup - master = Puppet::Network::Handler.master.new( - :Local => true - ) - } - - config = master.getconfig({"hostname" => "blah"}) - - # Cache this value for later - parse1 = master.freshness("mynode") - - sleep 1.5 - # Create a new manifest - File.open(manifest, "w") { |f| - f.puts "file { \"%s\": ensure => file }\n" % file2 - } - - # Verify that the master doesn't immediately reparse the file; we - # want to wait through the timeout - assert_equal(parse1, master.freshness("mynode"), "Master did not wait through timeout") - - # Then eliminate it - Puppet[:filetimeout] = 0 - - # Now make sure the master does reparse - #Puppet.notice "%s vs %s" % [parse1, master.freshness] - assert(parse1 != master.freshness("mynode"), "Master did not reparse file") - - assert(master.getconfig({"hostname" => "blah"}) != config, "Did not use reloaded config") + assert_equal(master.freshness, now1, "Did not return current time as freshness") end # Make sure we're correctly doing clientname manipulations. From b49fd495622b15f96faf944db1e70cbe9e7fe7c4 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 11 Apr 2008 13:01:17 -0500 Subject: [PATCH 48/48] Resources now return the 'should' value for properties from the [] accessor method (they previously threw an exception when this method was used with properties). This shouldn't have any affect functionally; it just makes the method equivalent to 'should' for properties, but it works for all attribute types now. --- CHANGELOG | 6 +++++ lib/puppet/metatype/attributes.rb | 10 +++------ test/rails/host.rb | 37 ------------------------------- 3 files changed, 9 insertions(+), 44 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8dad0d977..b5326a12f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ + Resources now return the 'should' value for properties from + the [] accessor method (they previously threw an exception when + this method was used with properties). This shouldn't have any + affect functionally; it just makes the method equivalent to 'should' + for properties, but it works for all attribute types now. + Modified the 'master' handler to use the Catalog class to compile node configurations, rather than using the Configuration handler, which was never used directly. I removed the Configuration diff --git a/lib/puppet/metatype/attributes.rb b/lib/puppet/metatype/attributes.rb index b83fcdd78..3f48f22ff 100644 --- a/lib/puppet/metatype/attributes.rb +++ b/lib/puppet/metatype/attributes.rb @@ -477,13 +477,9 @@ class Puppet::Type end if obj = @parameters[name] - # We throw a failure here, because this method is too - # ambiguous when used with properties. - if obj.is_a?(Puppet::Property) - fail "[] called on a property" - else - return obj.value - end + # Note that if this is a property, then the value is the "should" value, + # not the current value. + obj.value else return nil end diff --git a/test/rails/host.rb b/test/rails/host.rb index 582bebcb2..79f0ae398 100755 --- a/test/rails/host.rb +++ b/test/rails/host.rb @@ -151,41 +151,4 @@ class TestRailsHost < PuppetTest::TestCase "loglevel was not added") end end - - def test_freshness_connect_update - Puppet::Rails.init - Puppet[:storeconfigs] = true - - Puppet[:code] = " " - # this is the default server setup - master = Puppet::Network::Handler.configuration.new( - :Local => true - ) - - # Create a host - Puppet::Rails::Host.new(:name => "test", :ip => "192.168.0.3").save - - assert_nothing_raised("Failed to update last_connect for unknown host") do - master.version("created",'192.168.0.1') - end - - # Make sure it created the host - created = Puppet::Rails::Host.find_by_name("created") - assert(created, "Freshness did not create host") - assert(created.last_freshcheck, - "Did not set last_freshcheck on created host") - - # Now check on the existing host - assert_nothing_raised("Failed to update last_connect for unknown host") do - master.version("test",'192.168.0.2') - end - - # Recreate it, so we're not using the cached object. - host = Puppet::Rails::Host.find_by_name("test") - - # Make sure it created the host - assert(host.last_freshcheck, - "Did not set last_freshcheck on existing host") - end end -