diff options
author | Luke Kanies <luke@madstop.com> | 2008-04-11 13:01:42 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-04-11 13:01:42 -0500 |
commit | fb05ef3c96038d67a46eb142202af186ad6cb0b3 (patch) | |
tree | 148e8b882e5c8104c4f3fa8ecc4288e8608f812d | |
parent | b49fb68f768e8b98c555ef0ae08a7bd22f5d36bd (diff) | |
parent | b49fd495622b15f96faf944db1e70cbe9e7fe7c4 (diff) | |
download | puppet-fb05ef3c96038d67a46eb142202af186ad6cb0b3.tar.gz puppet-fb05ef3c96038d67a46eb142202af186ad6cb0b3.tar.xz puppet-fb05ef3c96038d67a46eb142202af186ad6cb0b3.zip |
Merge branch '0.24.x'
105 files changed, 2091 insertions, 1973 deletions
@@ -1,3 +1,38 @@ + 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 + 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 + 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. + + 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 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 @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # # = Synopsis 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 @@ -1,4 +1,4 @@ -#!/usr/bin/ruby +#!/usr/bin/env ruby # vim: softtabstop=4 shiftwidth=4 expandtab # # = Synopsis 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 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/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index f331474a9..0a7ee1a5f 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-syntax-table nil - "Syntax table in use in puppet-mode buffers.") +(defvar puppet-mode-map + (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.") -(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) - ) +(defvar puppet-mode-syntax-table + (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) + "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." @@ -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\\)\\)" 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 ) 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 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 + 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 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/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 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 :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 + 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 + :dependent_service_description, :execution_failure_criteria, + :notification_failure_criteria, :hostgroup_name, + :dependent_hostgroup_name setnamevar :host_name - end + end newtype :serviceescalation do setparameters :host_name, :service_description, :first_notification, - :last_notification, :contact_groups, :notification_interval + :last_notification, :contact_groups, :notification_interval, :hostgroup_name setnamevar :host_name end - newtype :serviceextinfo do + 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$ 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/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 diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index c30c097b2..2402b9cbe 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,37 @@ 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 destroy(*args) - indirection.destroy(*args) - end + # Expire any cached instance. + def expire(*args) + indirection.expire *args + end + + def find(*args) + indirection.find *args + end - def search(*args) - indirection.search(*args) - end + def destroy(*args) + indirection.destroy *args + end - def version(*args) - indirection.version(*args) - end + def search(*args) + indirection.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 self, *args + end end end diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index 6d769b97d..2b5e8d912 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -13,15 +13,13 @@ 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) - return catalog.to_transportable + return catalog else # This shouldn't actually happen; we should either return # a config or raise an exception. @@ -46,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. @@ -102,16 +84,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/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/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/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/file.rb b/lib/puppet/indirector/file.rb index c2d36c46b..e5382155f 100644 --- a/lib/puppet/indirector/file.rb +++ b/lib/puppet/indirector/file.rb @@ -2,26 +2,28 @@ 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(request) if respond_to?(:path) - path = path(file.name) + path = path(request.key) else - path = file.path + path = request.key 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" % [request.key]) 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" % [request.key, detail] end end - def find(name) + # Return a model instance for a given file on disk. + 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) @@ -35,20 +37,21 @@ class Puppet::Indirector::File < Puppet::Indirector::Terminus return model.new(content) end - def save(file) + # Save a new file to disk. + 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 d47433c60..15358a801 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -1,4 +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, @@ -28,8 +30,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 +66,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 +126,11 @@ class Puppet::Indirector::Indirection end end + # Set up our request object. + def request(method, key, arguments = nil) + Puppet::Indirector::Request.new(self.name, method, key, arguments) + end + # Return the singleton terminus for this indirection. def terminus(terminus_name = nil) # Get the name of the terminus. @@ -147,83 +172,124 @@ class Puppet::Indirector::Indirection end 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 + # 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(request(:find, key, *args)) + + Puppet.info "Expiring the %s cache of %s" % [self.name, instance.name] - check_authorization(:find, terminus_name, ([key] + args)) + # Set an expiration date in the past + instance.expiration = Time.now - 60 + + cache.save(request(: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. - 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(request) + if cached.expired? + 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 for %s" % [self.name, request.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 + 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 for %s" % [self.name, request.key] + cache.save request(:save, result, *args) end - terminus(terminus_name).post_find(result) if terminus(terminus_name).respond_to?(:post_find) - return result end - end - def destroy(*args) - check_authorization(:destroy, terminus_class, args) - - terminus.destroy(*args) + return nil end - def search(*args) - check_authorization(:search, terminus_class, args) + # Remove something via the terminus. + def destroy(key, *args) + request = request(:destroy, key, *args) + terminus = prepare(request) - result = terminus.search(*args) + terminus.destroy(request) - terminus().post_search(result) if terminus().respond_to?(:post_search) + if cache? and cached = cache.find(request(:find, key, *args)) + # Reuse the existing request, since it's equivalent. + cache.destroy(request) + end - result + nil end - # these become instance methods - def save(instance, *args) - check_authorization(:save, terminus_class, ([instance] + args)) + # Search for more than one instance. Should always return an array. + def search(key, *args) + request = request(:search, key, *args) + terminus = prepare(request) - 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? - cache.save(instance, *args) if cache? - terminus.save(instance, *args) + 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.each do |instance| + instance.expiration ||= self.expiration + end + return result + end end - def version(*args) - terminus.version(*args) + # Save the instance in the appropriate terminus. This method is + # normally an instance method on the indirected class. + def save(instance, *args) + request = request(:save, instance, *args) + terminus = prepare(request) + + # If caching is enabled, save our document there + cache.save(request) if cache? + terminus.save(request) 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) + # 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 on %s with %s" % [request.method, request.key, request.options.inspect] + end + end - 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]] + # 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 + + check_authorization(request, terminus(terminus_name)) + + return terminus(terminus_name) end # Create a new terminus instance. 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/memory.rb b/lib/puppet/indirector/memory.rb index 5bfcec95d..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(instance) - raise ArgumentError.new("Could not find %s to destroy" % instance) unless @instances.include?(instance.name) - @instances.delete(instance.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/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/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/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/request.rb b/lib/puppet/indirector/request.rb new file mode 100644 index 000000000..68b7ee160 --- /dev/null +++ b/lib/puppet/indirector/request.rb @@ -0,0 +1,25 @@ +require 'puppet/indirector' + +# Provide any attributes or functionality needed for indirected +# instances. +class Puppet::Indirector::Request + attr_accessor :indirection_name, :key, :method, :options, :instance + + 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) + @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 + + # Look up the indirection based on the name provided. + def indirection + Puppet::Indirector::Indirection.instance(@indirection_name) + end +end 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/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/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/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/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index 4c7fa5f5b..a2b6499bb 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 } @@ -96,31 +98,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 @@ -147,11 +124,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 @@ -450,32 +422,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/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/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 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/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/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/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 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: +" 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/ 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/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/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." 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/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index 98b6a63de..0b668395d 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/lib/puppet/util/posix.rb b/lib/puppet/util/posix.rb index 0b1e1c2f2..8228734ef 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 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/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.rb b/spec/integration/node.rb index 631d4403e..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) - Puppet::Node.find(@name).should be_nil - end + Puppet::Node.find(@name).should equal(@node) + end + + 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) + 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 new file mode 100755 index 000000000..ca14c2ea8 --- /dev/null +++ b/spec/integration/node/catalog.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-4-8. +# Copyright (c) 2008. 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) + + 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 + end +end diff --git a/spec/integration/node/facts.rb b/spec/integration/node/facts.rb new file mode 100755 index 000000000..c2f876578 --- /dev/null +++ b/spec/integration/node/facts.rb @@ -0,0 +1,45 @@ +#!/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 expire any cached node instances when it is saved" do + 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 + 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/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/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/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 +describe Puppet::FileServing::IndirectionHooks 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 - before do - @object = Object.new - @object.extend(Puppet::FileServing::IndirectionHooks) - - @modules = mock 'modules' - @object.stubs(:terminus).with(:modules).returns(@modules) - 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") - 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 - 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 = stub 'request', :key => "http://myhost/blah", :options => {:node => "whatever"} + end + + 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 + + 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) + + @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 diff --git a/spec/unit/indirector.rb b/spec/unit/indirector.rb index 1a5867c51..1efa7b2e5 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,81 @@ 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 delegate to the indirection" do + @indirection.expects(@method) + @thingie.send(@method, "me") + end + + 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 + 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 + describe "when expiring instances via the model" do + before { @method = :expire } + it_should_behave_like "Delegated Indirection Method" 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 + # 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 delegate to the indirection" do + @indirection.expects(:save) + @instance.save + 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 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 + 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..083a9ced5 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,19 +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) - proc { @compiler.find(@name) }.should raise_error(Puppet::Error) + @compiler.find(@request) end end @@ -88,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 @@ -125,27 +121,34 @@ 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 config = mock 'config' - result = mock 'result', :to_transportable => :catalog + result = mock 'result' @compiler.interpreter.expects(:compile).with(@node).returns(result) - @compiler.find(@name).should == :catalog + @compiler.find(@request).should equal(result) end it "should benchmark the compile process" do @@ -154,56 +157,6 @@ 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) - 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" + @compiler.find(@request) end end diff --git a/spec/unit/indirector/checksum/file.rb b/spec/unit/indirector/checksum/file.rb index 4f8ee98b2..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,19 +124,16 @@ 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 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(@request) 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..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
\ No newline at end of file +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 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 0974a60ec..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 @@ -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..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) - - file = stub 'file', :content => content, :path => @path, :name => @path + filehandle.expects(:print).with(@content).raises(ArgumentError) - 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,48 +110,45 @@ 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 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(@request) 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(@request) }.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(@request) }.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(@request) end end -end
\ No newline at end of file +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 - end + 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 - it "should collect its attributes when a file is found" do - @data.expects(:collect_attributes) + @request = stub 'request', :key => @uri, :options => {} + end - Puppet::FileServing::Metadata.expects(:new).returns(@data) - @metadata.find(@uri).should == @data - end -end + it "should collect its attributes when a file is found" do + @data.expects(:collect_attributes) -describe Puppet::Indirector::FileMetadata::File, "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(@request).should == @data + end 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) + describe "when searching for multiple files" do + before do + @metadata = Puppet::Indirector::FileMetadata::File.new + @uri = "file:///my/local" + + @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/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/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index fe456b61e..e8ab9633b 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -2,579 +2,730 @@ require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/indirector' +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 -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 + @terminus.stubs(@method) + + @indirection.send(@method, "mystuff", :one => :two) 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) + 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 set the name" do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) - @indirection.name.should == :myind + 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 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) + 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 - 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) +describe "Delegation Authorizer", :shared => true do + before do + # So the :respond_to? turns out correctly. + class << @terminus + def authorized? + end + end end - after do - @indirection.delete if defined? @indirection + 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 -end -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 + 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 - - 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 - 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 - 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 +describe Puppet::Indirector::Indirection do + 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 pass the instance to the :post_find hook if there is one" do - class << @terminus - def post_find - end - end - @terminus.expects(:post_find).with(@instance) - @terminus.expects(:find).with(@name).returns(@instance) - @indirection.find(@name) + 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 - 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 + it "should set the name" do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) + @indirection.name.should == :myind end - end - describe Puppet::Indirector::Indirection, " when searching for multiple model instances" do + 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 let the appropriate terminus find the matching instances" do - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name).should == @instance + 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 pass the instances to the :post_search hook if there is one" do - class << @terminus - def post_search - end - end - @terminus.expects(:post_search).with(@instance) - @terminus.expects(:search).with(@name).returns(@instance) - @indirection.search(@name) + after do + @indirection.delete if defined? @indirection end end - describe Puppet::Indirector::Indirection, " when storing a model instance" do + 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 - it "should let the appropriate terminus store the instance" do - @terminus.expects(:save).with(@instance).returns(@instance) - @indirection.save(@instance).should == @instance + #@request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} + @request = mock 'instance' + end + + it "should allow setting the ttl" do + @indirection.ttl = 300 + @indirection.ttl.should == 300 end - end - - describe Puppet::Indirector::Indirection, " when handling instance versions" do - it "should let the appropriate terminus perform the lookup" do - @terminus.expects(:version).with(@name).returns(5) - @indirection.version(@name).should == 5 + 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 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) + 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 - 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) + it "should have a method for creating an indirection request instance" do + @indirection.should respond_to(:request) end - # 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? + 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 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 - @terminus.stubs(:save) - @indirection.save(@instance) end - end + + describe "and looking for a model instance" do + before { @method = :find } + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - describe Puppet::Indirector::Indirection, " when an authorization hook is present" do + it "should return the results of the delegation" do + @terminus.expects(:find).returns(@instance) + @indirection.find("me").should equal(@instance) + end - before do - # So the :respond_to? turns out right. - class << @terminus - def authorized? + it "should set the expiration date on any instances without one set" do + @terminus.stubs(:find).returns(@instance) + + @indirection.expects(:expiration).returns :yay + + @instance.expects(:expiration).returns(nil) + @instance.expects(:expiration=).with(:yay) + + @indirection.find("/my/key") + end + + it "should not override an already-set expiration date on returned instances" do + @terminus.stubs(:find).returns(@instance) + + @indirection.expects(:expiration).never + + @instance.expects(:expiration).returns(:yay) + @instance.expects(:expiration=).never + + @indirection.find("/my/key") + 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 first look in the cache for an instance" do + @terminus.stubs(:find).never + @cache.expects(:find).returns @instance + + @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("/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("/my/key") + end + + it "should not return the cached object if it is expired" do + @instance.stubs(:expired?).returns true + + @cache.stubs(:find).returns @instance + @terminus.stubs(:find).returns nil + @indirection.find("/my/key").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 + + @cache.stubs(:find).returns @instance + @terminus.stubs(:find).returns nil + @indirection.find("/my/key") + end + + it "should cache any objects not retrieved from the cache" do + @cache.expects(:find).returns nil + + @terminus.expects(:find).returns(@instance) + @cache.expects(:save) + + @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 nil + + @terminus.stubs(:find).returns(@instance) + @cache.stubs(:save) + + @indirection.find("/my/key") + end + + 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 + + @terminus.stubs(:find).returns(@instance) + @cache.stubs(:save) + + Puppet.expects(:info) + + @indirection.find("/my/key") 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 + describe "and storing a model instance" do + before { @method = :save } - 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_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - 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 return nil" do + @terminus.stubs(:save) + @indirection.save(@instance).should be_nil + 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 + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) - 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 + @instance.stubs(:expired?).returns false + 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 use a request to save the object to the cache" do + request = stub 'request', :instance => @instance, :options => {} - 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 + @indirection.expects(:request).returns request - 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) + @cache.expects(:save).with(request) + @terminus.stubs(:save) + @indirection.save(@instance) + end + end end + + describe "and removing a model instance" do + before { @method = :destroy } - 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_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - after :each do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end + it "should return nil" do + @terminus.stubs(:destroy) + @indirection.destroy("/my/key").should be_nil + end + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) -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 + @instance.stubs(:expired?).returns false + 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 + 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 => {} - 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 + @indirection.expects(:request).with(:destroy, "/my/key").returns destroy + @indirection.expects(:request).with(:find, "/my/key").returns find - 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 + cached = mock 'cache' - 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 + @cache.expects(:find).with(find).returns cached + @cache.expects(:destroy).with(destroy) - 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 + @terminus.stubs(:destroy) - after do - @indirection.delete if defined? @indirection - end -end + @indirection.destroy("/my/key") + end + 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 + describe "and searching for multiple model instances" do + before { @method = :search } - it "should allow specification of a terminus type" do - @indirection.should respond_to(:terminus_class=) - end + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" - 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 set the expiration date on any instances without one set" do + @terminus.stubs(:search).returns([@instance]) - it "should fail when the terminus class name is an empty string" do - proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) - end + @indirection.expects(:expiration).returns :yay - it "should fail when the terminus class name is nil" do - proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) - end + @instance.expects(:expiration).returns(nil) + @instance.expects(:expiration=).with(:yay) - 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 + @indirection.search("/my/key") + 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 not override an already-set expiration date on returned instances" do + @terminus.stubs(:search).returns([@instance]) - 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 + @indirection.expects(:expiration).never - after do - @indirection.delete if defined? @indirection - end -end + @instance.expects(:expiration).returns(:yay) + @instance.expects(:expiration=).never -describe Puppet::Indirector::Indirection, " when a select_terminus hook is available" do - before do - @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @indirection.search("/my/key") + end - # And provide a select_terminus hook - @indirection.meta_def(:select_terminus) do |uri| - :other + it "should return the results of searching in the terminus" do + @terminus.expects(:search).returns([@instance]) + @indirection.search("/my/key").should == [@instance] + end end - @terminus = mock 'terminus' - @terminus_class = stub 'terminus class', :new => @terminus + describe "and expiring a model instance" do + describe "when caching is not enabled" do + it "should do nothing" do + @cache_class.expects(:new).never - @other_terminus = mock 'other_terminus' - @other_terminus_class = stub 'other_terminus_class', :new => @other_terminus + @indirection.expire("/my/key") + end + end - @cache_terminus = mock 'cache_terminus' - @cache_terminus_class = stub 'cache_terminus_class', :new => @cache_terminus + describe "when caching is enabled" do + before do + @indirection.cache_class = :cache_terminus + @cache_class.expects(:new).returns(@cache) - 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) + @instance.stubs(:expired?).returns false - # Set it to a default type. - @indirection.terminus_class = :foo + @cached = stub 'cached', :expiration= => nil, :name => "/my/key" + end - @uri = "full://url/path" - @result = stub 'result', :version => 1.0 - 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 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) + it "should do nothing if no such instance is cached" do + @cache.expects(:find).returns nil - @indirection.find(@uri) - end + @indirection.expire("/my/key") + 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 + it "should log that it is expiring any found instance" do + @cache.expects(:find).returns @cached + @cache.stubs(:save) - @other_terminus.expects(:version).with(@uri).returns(2.0) + Puppet.expects(:info) - @cache_terminus.expects(:has_most_recent?).with(@uri, 2.0).returns(true) - @cache_terminus.expects(:find).returns(:whatever) + @indirection.expire("/my/key") + end - @indirection.find(@uri).should == :whatever - end + it "should set the cached instance's expiration to a time in the past" do + @cache.expects(:find).returns @cached + @cache.stubs(:save) - 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) + @cached.expects(:expiration=).with { |t| t < Time.now } - @indirection.find(@uri, :node => "johnny") - end + @indirection.expire("/my/key") + 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 + it "should save the now expired instance back into the cache" do + @cache.expects(:find).returns @cached - after do - @indirection.delete if defined? @indirection - end -end + @cached.expects(:expiration=).with { |t| t < Time.now } -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 + @cache.expects(:save) - it "should create an instance of the chosen terminus class" do - @terminus_class.stubs(:new).returns(@terminus) - @indirection.terminus(:foo).should equal(@terminus) - end + @indirection.expire("/my/key") + 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 + it "should use a request to save the expired resource to the cache" do + @cache.expects(:find).returns @cached - # 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 + @cached.expects(:expiration=).with { |t| t < Time.now } - 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 + @cache.expects(:save).with { |r| r.is_a?(Puppet::Indirector::Request) and r.instance == @cached and r.method == :save }.returns(@cached) - after do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end + @indirection.expire("/my/key") + end + 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 + after :each do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache + end 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 + 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 - 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 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 fail to set the cache class when the cache class name is nil" do - proc { @indirection.cache_class = nil }.should raise_error(ArgumentError) + after do + @indirection.delete if defined? @indirection + end 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 + describe "when routing to the correct 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 - after do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache - end -end + it "should fail if no terminus class can be picked" do + proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) + 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 + 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 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 managing the cache terminus" do + describe "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 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 fail to redirect if no terminus type has been specified" do + proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) 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) + it "should fail when the terminus class name is an empty string" do + proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) 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) + it "should fail when the terminus class name is nil" do + proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) end - end - describe Puppet::Indirector::Indirection, " when saving and using a cache" do + 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 - before do - @indirection.cache_class = :cache_terminus - @cache_class.expects(:new).returns(@cache) - @name = "testing" - @instance = stub 'instance', :version => 5, :name => @name + 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 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 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 - 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) + after do + @indirection.delete if defined? @indirection end end - - describe Puppet::Indirector::Indirection, " when finding and using a cache" do + describe "when managing terminus instances" 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 = 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 return the cached object if the cache is up to date" do - cached = mock 'cached object' + 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 - name = "myobject" + # 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 - @terminus.expects(:version).with(name).returns(1) - @cache.expects(:has_most_recent?).with(name, 1).returns(true) + 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 - @cache.expects(:find).with(name).returns(cached) + 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 - @indirection.find(name).should equal(cached) + it "should provide a method for setting the cache terminus class" do + @indirection.should respond_to(:cache_class=) end - it "should return the original object if the cache is not up to date" do - real = stub 'real object', :version => 1 + it "should fail to cache if no cache type has been specified" do + proc { @indirection.cache }.should raise_error(Puppet::DevError) + end - name = "myobject" + 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 - @cache.stubs(:save) - @cache.expects(:has_most_recent?).with(name, 1).returns(false) - @terminus.expects(:version).with(name).returns(1) + 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 - @terminus.expects(:find).with(name).returns(real) + 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 - @indirection.find(name).should equal(real) + after do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache end + end - it "should cache any newly returned objects" do - real = stub 'real object', :version => 1 + 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 - name = "myobject" + 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 - @terminus.expects(:version).with(name).returns(1) - @cache.expects(:has_most_recent?).with(name, 1).returns(false) + 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 - @terminus.expects(:find).with(name).returns(real) - @cache.expects(:save).with(real) + 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 - @indirection.find(name).should equal(real) + describe "and saving" do + end + + describe "and finding" do + end + + after :each do + @indirection.delete + Puppet::Indirector::Indirection.clear_cache end - end - - after :each do - @indirection.delete - Puppet::Indirector::Indirection.clear_cache 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/memory.rb b/spec/unit/indirector/memory.rb index c0fca6bd9..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) - @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/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/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/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/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/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/request.rb b/spec/unit/indirector/request.rb new file mode 100755 index 000000000..cdb40b181 --- /dev/null +++ b/spec/unit/indirector/request.rb @@ -0,0 +1,55 @@ +#!/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 use provided value as the key if it is a string" do + 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, :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, :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, :method, :key, :one => :two) }.should_not raise_error(ArgumentError) + end + + it "should support nil options" do + 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, :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, :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, :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, :method, :key) + + request.indirection.should equal(ind) + 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 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/other/modules.rb b/spec/unit/module.rb index 26ca3907d..06b2d016d 100755 --- a/spec/unit/other/modules.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 @@ -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) 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/spec/unit/node.rb b/spec/unit/node.rb index bb99378d9..348e160cf 100755 --- a/spec/unit/node.rb +++ b/spec/unit/node.rb @@ -133,9 +133,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 d607b3540..8d7692442 100755 --- a/spec/unit/node/catalog.rb +++ b/spec/unit/node/catalog.rb @@ -794,14 +794,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/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/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/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 + 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 parameter" % nagios_type.namevar do - puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil - end + it "should have documentation" do + puppet_type.instance_variable_get("@doc").should_not == "" + end - it "should have an ensure property" do - puppet_type.should be_validproperty(:ensure) - end + it "should have %s as its namevar" % nagios_type.namevar do + puppet_type.namevar.should == nagios_type.namevar + end - it "should have a target property" do - puppet_type.should be_validproperty(:target) - 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 documentation for its target property" do - puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil - end + it "should have an ensure property" do + puppet_type.should be_validproperty(:ensure) + 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) + it "should have a target property" do + puppet_type.should be_validproperty(:target) 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 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 - 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) + 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 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 diff --git a/spec/unit/util/loadedfile.rb b/spec/unit/util/loadedfile.rb new file mode 100755 index 000000000..083120e20 --- /dev/null +++ b/spec/unit/util/loadedfile.rb @@ -0,0 +1,65 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'tempfile' +require 'puppet/util/loadedfile' + +describe Puppet::Util::LoadedFile do + 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 + # 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 + # 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 + # 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) + # 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! + # 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 disable checking if Puppet[:filetimeout] is negative" do + Puppet[:filetimeout] = -1 + @loaded.changed?.should_not be(false) + end + + after(:each) do + @f.close + end +end diff --git a/test/language/parser.rb b/test/language/parser.rb index f7e7d8762..effb2d40c 100755 --- a/test/language/parser.rb +++ b/test/language/parser.rb @@ -1130,7 +1130,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") @@ -1160,6 +1159,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 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' diff --git a/test/network/client/master.rb b/test/network/client/master.rb index 682d48eb5..c0d14ccee 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 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 - - manifest = mktestmanifest() - - 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 + def test_freshness_is_always_now + master = Puppet::Network::Handler.master.new( + :Manifest => tempfile, + :UseNodes => true, + :Local => true + ) - # 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") + now1 = mock 'now1' + Time.expects(:now).returns(now1) - 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. 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 - diff --git a/test/ral/type/user.rb b/test/ral/type/user.rb index ec9b12923..1a2de2649 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 |