summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-05-20 10:19:10 -0500
committerLuke Kanies <luke@madstop.com>2008-05-20 10:19:10 -0500
commitfe157f239a301abb52f81c62719355c8e50c970c (patch)
tree2ff683d14dbf5c95a99410fe1d12300368cf497e
parent3cb0d60d3d0870f1d9ac83e5dbeaa06d2888231f (diff)
parent84a787a2a764a5035f7cbb8d30f94fc601bed154 (diff)
downloadpuppet-fe157f239a301abb52f81c62719355c8e50c970c.tar.gz
puppet-fe157f239a301abb52f81c62719355c8e50c970c.tar.xz
puppet-fe157f239a301abb52f81c62719355c8e50c970c.zip
Merge branch '0.24.x'
Conflicts: CHANGELOG spec/integration/defaults.rb spec/integration/node/catalog.rb spec/unit/rails.rb spec/unit/type/mount.rb
-rw-r--r--CHANGELOG18
-rwxr-xr-xbin/puppetd8
-rwxr-xr-xbin/ralsh2
-rw-r--r--ext/autotest/Rakefile8
-rw-r--r--ext/autotest/config43
-rw-r--r--ext/autotest/readme.rst16
-rw-r--r--ext/emacs/puppet-mode.el301
-rw-r--r--ext/ldap/puppet.schema7
-rwxr-xr-xinstall.rb11
-rw-r--r--lib/puppet/defaults.rb12
-rw-r--r--lib/puppet/indirector/node/ldap.rb29
-rw-r--r--lib/puppet/parser/compiler.rb2
-rw-r--r--lib/puppet/parser/functions.rb2
-rw-r--r--lib/puppet/parser/interpreter.rb2
-rw-r--r--lib/puppet/parser/templatewrapper.rb9
-rw-r--r--lib/puppet/provider.rb104
-rw-r--r--lib/puppet/provider/confine.rb77
-rw-r--r--lib/puppet/provider/confine/exists.rb22
-rw-r--r--lib/puppet/provider/confine/facter.rb37
-rw-r--r--lib/puppet/provider/confine/false.rb19
-rw-r--r--lib/puppet/provider/confine/feature.rb17
-rw-r--r--lib/puppet/provider/confine/true.rb20
-rw-r--r--lib/puppet/provider/confine_collection.rb47
-rw-r--r--lib/puppet/provider/confiner.rb20
-rwxr-xr-xlib/puppet/provider/cron/crontab.rb2
-rw-r--r--lib/puppet/provider/group/ldap.rb37
-rw-r--r--lib/puppet/provider/ldap.rb137
-rw-r--r--lib/puppet/provider/nameservice/objectadd.rb2
-rwxr-xr-xlib/puppet/provider/package/rpm.rb2
-rw-r--r--lib/puppet/provider/package/urpmi.rb6
-rwxr-xr-xlib/puppet/provider/service/base.rb8
-rwxr-xr-xlib/puppet/provider/service/init.rb6
-rwxr-xr-xlib/puppet/provider/service/redhat.rb29
-rw-r--r--lib/puppet/provider/ssh_authorized_key/parsed.rb49
-rw-r--r--lib/puppet/provider/user/ldap.rb115
-rw-r--r--lib/puppet/reference/providers.rb2
-rwxr-xr-xlib/puppet/type/mount.rb4
-rw-r--r--lib/puppet/type/ssh_authorized_key.rb44
-rw-r--r--lib/puppet/util/ldap.rb5
-rw-r--r--lib/puppet/util/ldap/connection.rb57
-rw-r--r--lib/puppet/util/ldap/generator.rb45
-rw-r--r--lib/puppet/util/ldap/manager.rb281
-rw-r--r--lib/puppet/util/settings.rb2
-rw-r--r--lib/puppet/util/storage.rb4
-rw-r--r--lib/puppet/util/variables.rb38
-rw-r--r--man/man8/filebucket.82
-rw-r--r--man/man8/pi.82
-rw-r--r--man/man8/puppet.82
-rw-r--r--man/man8/puppet.conf.88
-rw-r--r--man/man8/puppetca.810
-rw-r--r--man/man8/puppetd.82
-rw-r--r--man/man8/puppetdoc.82
-rw-r--r--man/man8/puppetmasterd.82
-rw-r--r--man/man8/puppetrun.82
-rw-r--r--man/man8/ralsh.833
-rwxr-xr-xspec/integration/defaults.rb10
-rwxr-xr-xspec/integration/node/catalog.rb6
-rwxr-xr-xspec/integration/type/package.rb (renamed from spec/integration/ral/types/package.rb)2
-rwxr-xr-xspec/unit/indirector/node/ldap.rb91
-rwxr-xr-xspec/unit/parser/templatewrapper.rb57
-rwxr-xr-xspec/unit/provider/confine.rb67
-rwxr-xr-xspec/unit/provider/confine/exists.rb80
-rwxr-xr-xspec/unit/provider/confine/facter.rb86
-rwxr-xr-xspec/unit/provider/confine/false.rb52
-rwxr-xr-xspec/unit/provider/confine/feature.rb59
-rwxr-xr-xspec/unit/provider/confine/true.rb50
-rwxr-xr-xspec/unit/provider/confine_collection.rb122
-rwxr-xr-xspec/unit/provider/confiner.rb62
-rwxr-xr-xspec/unit/provider/group/ldap.rb66
-rwxr-xr-xspec/unit/provider/interface/redhat.rb (renamed from spec/unit/ral/provider/interface/redhat.rb)6
-rwxr-xr-xspec/unit/provider/interface/sunos.rb (renamed from spec/unit/ral/provider/interface/sunos.rb)2
-rwxr-xr-xspec/unit/provider/ldap.rb248
-rwxr-xr-xspec/unit/provider/mount.rb (renamed from spec/unit/ral/provider/mount.rb)2
-rwxr-xr-xspec/unit/provider/mount/parsed.rb (renamed from spec/unit/ral/provider/mount/parsed.rb)2
-rwxr-xr-xspec/unit/provider/ssh_authorized_key/parsed.rb74
-rwxr-xr-xspec/unit/provider/user/ldap.rb252
-rwxr-xr-xspec/unit/rails.rb3
-rwxr-xr-xspec/unit/type.rb (renamed from spec/unit/ral/type.rb)2
-rwxr-xr-xspec/unit/type/exec.rb (renamed from spec/unit/ral/type/exec.rb)2
-rwxr-xr-xspec/unit/type/file.rb (renamed from spec/unit/ral/type/file.rb)2
-rwxr-xr-xspec/unit/type/interface.rb (renamed from spec/unit/ral/type/interface.rb)2
-rwxr-xr-xspec/unit/type/mount.rb (renamed from spec/unit/ral/type/mount.rb)46
-rwxr-xr-xspec/unit/type/nagios.rb (renamed from spec/unit/ral/type/nagios.rb)2
-rwxr-xr-xspec/unit/type/noop_metaparam.rb (renamed from spec/unit/ral/type/noop_metaparam.rb)2
-rwxr-xr-xspec/unit/type/package.rb (renamed from spec/unit/ral/type/package.rb)2
-rwxr-xr-xspec/unit/type/schedule.rb (renamed from spec/unit/ral/type/schedule.rb)2
-rwxr-xr-xspec/unit/type/service.rb (renamed from spec/unit/ral/type/service.rb)2
-rwxr-xr-xspec/unit/type/ssh_authorized_key.rb80
-rwxr-xr-xspec/unit/type/user.rb (renamed from spec/unit/ral/type/user.rb)12
-rwxr-xr-xspec/unit/util/ldap/connection.rb114
-rwxr-xr-xspec/unit/util/ldap/generator.rb54
-rwxr-xr-xspec/unit/util/ldap/manager.rb654
-rwxr-xr-xspec/unit/util/storage.rb237
-rw-r--r--test/data/providers/ssh_authorized_key/parsed/authorized_keys5
-rw-r--r--test/data/types/ssh_authorized_key/12
-rwxr-xr-xtest/ral/providers/cron/crontab.rb28
-rwxr-xr-xtest/ral/providers/provider.rb35
97 files changed, 4071 insertions, 386 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 0a5d9650d..f0819cefb 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,24 @@
whether we should even use a CRL. This way we aren't trying to
set file paths to 'false' to disable the CRL.
+ Moving all confine code out of the Provider class, and fixing #1197.
+ Created a Confiner module for the Provider class methods, enhanced
+ the interface between it and the Confine class to make sure binary
+ paths are searched for fresh each time.
+
+ Modified the 'factpath' setting to automatically configure
+ Facter to load facts there if a new enough version of
+ Facter is used.
+
+ Crontab provider: fix a parse error when a line begins with a space
+ character (fixes #1216)
+
+ Instead of deleting the init scripts (with --del) we should simply
+ disable it with chkconfig service off, and respectfully do the same
+ for enable => true;
+
+ Added ldap providers for users and groups.
+
Added support for the --all option to puppetca --clean. If
puppetca --clean --all is issued then all client certificates
are removed.
diff --git a/bin/puppetd b/bin/puppetd
index b92773c76..d408af7d3 100755
--- a/bin/puppetd
+++ b/bin/puppetd
@@ -10,7 +10,7 @@
#
# puppetd [-D|--daemonize|--no-daemonize] [-d|--debug] [--disable] [--enable]
# [-h|--help] [--fqdn <host name>] [-l|--logdest syslog|<file>|console]
-# [-o|--onetime] [--serve <handler>] [-t|--test]
+# [-o|--onetime] [--serve <handler>] [-t|--test] [--noop]
# [-V|--version] [-v|--verbose] [-w|--waitforcert <seconds>]
#
# = Description
@@ -57,7 +57,7 @@
# parameter, so you can specify '--server <servername>' as an argument.
#
# See the configuration file documentation at
-# http://reductivelabs.com/projects/puppet/reference/configref.html for
+# http://reductivelabs.com/trac/puppet/wiki/ConfigurationReference for
# the full list of acceptable parameters. A commented list of all
# configuration options can also be generated by running puppetd with
# '--genconfig'.
@@ -124,6 +124,10 @@
# Enable the most common options used for testing. These are +onetime+,
# +verbose+, +ignorecache, and +no-usecacheonfailure+.
#
+# noop::
+# Use +noop+ mode where the daemon runs in a no-op or dry-run mode. This is useful
+# for seeing what changes Puppet will make without actually executing the changes.
+#
# verbose::
# Turn on verbose reporting.
#
diff --git a/bin/ralsh b/bin/ralsh
index 1d0036717..3cbfcad41 100755
--- a/bin/ralsh
+++ b/bin/ralsh
@@ -65,7 +65,7 @@
#
# This example uses ``ralsh`` to return Puppet configuration for the user ``luke``::
#
-# $ ralsh user luke
+# $ ralsh user luke
# user { 'luke':
# home => '/home/luke',
# uid => '100',
diff --git a/ext/autotest/Rakefile b/ext/autotest/Rakefile
new file mode 100644
index 000000000..86327c04b
--- /dev/null
+++ b/ext/autotest/Rakefile
@@ -0,0 +1,8 @@
+dest = File.expand_path("~/.autotest")
+file dest => ["config", "Rakefile"] do
+ sh "cp config #{dest}"
+end
+
+task :install => dest
+
+task :default => :install
diff --git a/ext/autotest/config b/ext/autotest/config
new file mode 100644
index 000000000..d37c1b2c6
--- /dev/null
+++ b/ext/autotest/config
@@ -0,0 +1,43 @@
+# vim: syntax=ruby
+# From http://pastie.caboo.se/115692, linked from rickbradley
+
+require 'autotest/redgreen'
+require 'autotest/timestamp'
+
+Autotest.send(:alias_method, :real_find_files, :find_files)
+Autotest.send(:define_method, :find_files) do |*args|
+ real_find_files.reject do |k, v|
+ if (ENV['AUTOTEST'] and !ENV['AUTOTEST'].empty?)
+ !Regexp.new(ENV['AUTOTEST']).match(k)
+ end
+ end
+end
+
+module Autotest::Growl
+
+ def self.growl title, msg, img, pri=0, sticky=""
+ system "growlnotify -n autotest --image #{img} -p #{pri} -m #{msg.inspect} #{title} #{sticky}"
+ end
+
+ Autotest.add_hook :ran_command do |at|
+ image_root = "~/.autotest_images"
+ results = [at.results].flatten.join("\n")
+ output = results.slice(/(\d+)\stests,\s(\d+)\sassertions,\s(\d+)\sfailures,\s(\d+)\serrors/)
+ if output
+ if $~[3].to_i > 0 || $~[4].to_i > 0
+ growl "FAIL", "#{output}", "#{image_root}/fail.png", 2
+ else
+ growl "Pass", "#{output}", "#{image_root}/pass.png"
+ end
+ end
+
+ output = results.slice(/(\d+)\sexamples,\s(\d+)\sfailures?(,\s+\d+\s+pending)?/)
+ if output
+ if $~[2].to_i > 0 || $~[4].to_i > 0
+ growl "FAIL", "#{output}", "#{image_root}/fail.png", 2
+ else
+ growl "Pass", "#{output}", "#{image_root}/pass.png"
+ end
+ end
+ end
+end
diff --git a/ext/autotest/readme.rst b/ext/autotest/readme.rst
new file mode 100644
index 000000000..93d9ed2fc
--- /dev/null
+++ b/ext/autotest/readme.rst
@@ -0,0 +1,16 @@
+Autotest is a simple tool that automatically links tests with the files being
+tested, and runs tests automatically when either the test or code has changed.
+
+If you are running on a Mac and have growlnotify_ installed, install the
+ZenTest_ gem, then copy the ``config`` file to ``~/.autotest`` (or just
+run ``rake`` in this directory).
+
+Once you have ``autotest`` installed, change to the root of your Puppet
+git repository and run ``autotest`` with no arguments. To refresh the list
+of files to scan, hit ``^c`` (that is, control-c).
+
+It's recommended you leave this running in another terminal during all
+development, preferably on another monitor.
+
+.. _zentest: http://www.zenspider.com/ZSS/Products/ZenTest/
+.. _growlnotify: http://growl.info/extras.php
diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el
index 0a7ee1a5f..f3f4589aa 100644
--- a/ext/emacs/puppet-mode.el
+++ b/ext/emacs/puppet-mode.el
@@ -2,10 +2,11 @@
;;; puppet-mode.el
;;;
;;; Author: lutter
-;;; Description: A simple mode for editing puppet manifests
+;;; Author: Russ Allbery <rra@stanford.edu>
;;;
+;;; Description: A simple mode for editing puppet manifests
-(defconst puppet-mode-version "0.1")
+(defconst puppet-mode-version "0.2")
(defvar puppet-mode-abbrev-table nil
"Abbrev table in use in puppet-mode buffers.")
@@ -56,11 +57,43 @@
"*Indentation column of comments."
:type 'integer :group 'puppet)
+(defun puppet-count-matches (re start end)
+ "The same as Emacs 22 count-matches, for portability to other versions
+of Emacs."
+ (save-excursion
+ (let ((n 0))
+ (goto-char start)
+ (while (re-search-forward re end t) (setq n (1+ n)))
+ n)))
+
(defun puppet-comment-line-p ()
"Return non-nil iff this line is a comment."
(save-excursion
- (beginning-of-line)
- (looking-at (format "\\s-*%s" comment-start))))
+ (save-match-data
+ (beginning-of-line)
+ (looking-at (format "\\s-*%s" comment-start)))))
+
+(defun puppet-block-indent ()
+ "If point is in a block, return the indentation of the first line of that
+block (the line containing the opening brace). Used to set the indentation
+of the closing brace of a block."
+ (save-excursion
+ (save-match-data
+ (let ((opoint (point))
+ (apoint (search-backward "{" nil t)))
+ (when apoint
+ ;; This is a bit of a hack and doesn't allow for strings. We really
+ ;; want to parse by sexps at some point.
+ (let ((close-braces (puppet-count-matches "}" apoint opoint))
+ (open-braces 0))
+ (while (and apoint (> close-braces open-braces))
+ (setq apoint (search-backward "{" nil t))
+ (when apoint
+ (setq close-braces (puppet-count-matches "}" apoint opoint))
+ (setq open-braces (1+ open-braces)))))
+ (if apoint
+ (current-indentation)
+ nil))))))
(defun puppet-in-array ()
"If point is in an array, return the position of the opening '[' of
@@ -77,7 +110,7 @@ that array, else return nil."
;; ### steps, baby steps. A more robust strategy might be
;; ### to walk backwards by sexps, until hit a wall, then
;; ### inspect the nature of that wall.
- (if (= (count-matches "\\]" apoint opoint) 0)
+ (if (= (puppet-count-matches "\\]" apoint opoint) 0)
apoint))))))
(defun puppet-in-include ()
@@ -90,15 +123,14 @@ of the initial include plus puppet-include-indent."
(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))))
+ ((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 ()
@@ -110,6 +142,7 @@ of the initial include plus puppet-include-indent."
(let ((not-indented t)
(array-start (puppet-in-array))
(include-start (puppet-in-include))
+ (block-indent (puppet-block-indent))
cur-indent)
(cond
(array-start
@@ -146,18 +179,11 @@ of the initial include plus puppet-include-indent."
(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.
- (save-excursion
- (forward-line -1)
- (if (looking-at "^.*}")
- (progn
- (setq cur-indent (- (current-indentation) puppet-indent-level))
- (setq not-indented nil))
- (setq cur-indent (- (current-indentation) puppet-indent-level))))
- (if (< cur-indent 0) ; We can't indent past the left margin
- (setq cur-indent 0)))
+ ((and (looking-at "^\\s-*}\\s-*$") block-indent)
+ ;; This line contains only a closing brace and we're at the inner
+ ;; block, so we should indent it matching the indentation of the
+ ;; opening brace of the block.
+ (setq cur-indent block-indent))
(t
;; Otherwise, we did not start on a block-ending-only line.
(save-excursion
@@ -165,30 +191,136 @@ of the initial include plus puppet-include-indent."
(while not-indented
(forward-line -1)
(cond
+ ;; Comment lines are ignored unless we're at the start of the
+ ;; buffer.
((puppet-comment-line-p)
(if (bobp)
- (setq not-indented nil)
- ;; else ignore the line and continue iterating backwards
- ))
- ((looking-at "^.*}") ; indent at the level of the END_ token
+ (setq not-indented nil)))
+
+ ;; Brace or paren on a line by itself will already be indented to
+ ;; the right level, so we can cheat and stop there.
+ ((looking-at "^\\s-*[\)}]\\s-*")
(setq cur-indent (current-indentation))
(setq not-indented nil))
- ((looking-at "^.*{") ; indent an extra level
+
+ ;; Brace or paren not on a line by itself will be indented one
+ ;; level too much, but don't catch cases where the block is
+ ;; started and closed on the same line.
+ ((looking-at "^[^\({]*[\)}]\\s-*$")
+ (setq cur-indent (- (current-indentation) puppet-indent-level))
+ (setq not-indented nil))
+
+ ;; Indent by one level more than the start of our block. We lose
+ ;; if there is more than one block opened and closed on the same
+ ;; line but it's still unbalanced; hopefully people don't do that.
+ ((looking-at "^.*{[^}]*$")
+ (setq cur-indent (+ (current-indentation) puppet-indent-level))
+ (setq not-indented nil))
+
+ ;; Indent by one level if the line ends with an open paren.
+ ((looking-at "^.*\(\\s-*$")
(setq cur-indent (+ (current-indentation) puppet-indent-level))
(setq not-indented nil))
- ((looking-at "^.*;\\s-*$") ; Semicolon ends a nested resource
+
+ ;; Semicolon ends a block for a resource when multiple resources
+ ;; are defined in the same block, but try not to get the case of
+ ;; a complete resource on a single line wrong.
+ ((looking-at "^\\([^'\":\n]\\|\"[^\"]*\"\\|'[^']'\\)**;\\s-*$")
(setq cur-indent (- (current-indentation) puppet-indent-level))
(setq not-indented nil))
- ((looking-at "^.*:\\s-*$") ; indent an extra level after :
+
+ ;; Indent an extra level after : since it introduces a resource.
+ ((looking-at "^.*:\\s-*$")
(setq cur-indent (+ (current-indentation) puppet-indent-level))
(setq not-indented nil))
+
+ ;; Start of buffer.
((bobp)
- (setq not-indented nil))
- )))))
- (if cur-indent
+ (setq not-indented nil)))))
+
+ ;; If this line contains only a closing paren, we should lose one
+ ;; level of indentation.
+ (if (looking-at "^\\s-*\)\\s-*$")
+ (setq cur-indent (- cur-indent puppet-indent-level)))))
+
+ ;; We've figured out the indentation, so do it.
+ (if (and cur-indent (> cur-indent 0))
(indent-line-to cur-indent)
(indent-line-to 0)))))
+(defvar puppet-font-lock-syntax-table
+ (let* ((tbl (copy-syntax-table puppet-mode-syntax-table)))
+ (modify-syntax-entry ?_ "w" tbl)
+ tbl))
+
+(defvar puppet-font-lock-keywords
+ (list
+ ;; defines, classes, and nodes
+ '("^\\s *\\(class\\|define\\|node\\)\\s +\\([^( \t\n]+\\)"
+ 2 font-lock-function-name-face)
+ ;; inheritence
+ '("\\s +inherits\\s +\\([^( \t\n]+\\)"
+ 1 font-lock-function-name-face)
+ ;; include
+ '("\\(^\\|\\s +\\)include\\s +\\(\\([a-zA-Z0-9:_-]+\\(,[ \t\n]*\\)?\\)+\\)"
+ 2 font-lock-reference-face)
+ ;; keywords
+ (cons (concat
+ "\\b\\(\\("
+ (mapconcat
+ 'identity
+ '("alert"
+ "case"
+ "class"
+ "crit"
+ "debug"
+ "default"
+ "define"
+ "defined"
+ "else"
+ "emerg"
+ "err"
+ "fail"
+ "false"
+ "file"
+ "filebucket"
+ "generate"
+ "if"
+ "import"
+ "include"
+ "info"
+ "inherits"
+ "node"
+ "notice"
+ "realize"
+ "search"
+ "tag"
+ "tagged"
+ "template"
+ "true"
+ "warning"
+ )
+ "\\|")
+ "\\)\\>\\)")
+ 1)
+ ;; variables
+ '("\\(^\\|[^_:.@$]\\)\\b\\(true\\|false\\)\\>"
+ 2 font-lock-variable-name-face)
+ ;; variables
+ '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
+ 1 font-lock-variable-name-face)
+ '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\|:\\)+"
+ 0 font-lock-variable-name-face)
+ ;; usage of types
+ '("^\\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\\)\\)"
+ (2 font-lock-string-face)))
+ "*Additional expressions to highlight in puppet mode.")
;;;###autoload
(defun puppet-mode ()
@@ -213,97 +345,14 @@ The variable puppet-indent-level controls the amount of indentation.
(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)
(setq font-lock-variable-name-face font-lock-type-face))
-
- (setq puppet-font-lock-syntactic-keywords
- '(
- ("\\(^\\|[=(,~?:;]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
- (4 (7 . ?/))
- (6 (7 . ?/)))
- ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil))
- ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil))))
-
- (cond ((featurep 'xemacs)
- (put 'puppet-mode 'font-lock-defaults
- '((puppet-font-lock-keywords)
- nil nil nil
- beginning-of-line
- (font-lock-syntactic-keywords
- . puppet-font-lock-syntactic-keywords))))
- (t
- (add-hook 'puppet-mode-hook
- '(lambda ()
- (make-local-variable 'font-lock-defaults)
- (make-local-variable 'font-lock-keywords)
- (make-local-variable 'font-lock-syntax-table)
- (make-local-variable 'font-lock-syntactic-keywords)
- (setq font-lock-defaults '((puppet-font-lock-keywords) nil nil))
- (setq font-lock-keywords puppet-font-lock-keywords)
- (setq font-lock-syntax-table puppet-font-lock-syntax-table)
- (setq font-lock-syntactic-keywords puppet-font-lock-syntactic-keywords)))))
-
- (defvar puppet-font-lock-syntax-table
- (let* ((tbl (copy-syntax-table puppet-mode-syntax-table)))
- (modify-syntax-entry ?_ "w" tbl)
- tbl))
-
- (defvar puppet-font-lock-keywords
- (list
- ;; 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,]+\\)"
- 1 font-lock-reference-face)
- ;; hack to catch continued includes
- '("^\\s *\\([a-zA-Z0-9:_-]+\\),?\\s *$"
- 1 font-lock-reference-face)
- ;; keywords
- (cons (concat
- "\\b\\(\\("
- (mapconcat
- 'identity
- '("case"
- "class"
- "default"
- "define"
- "false"
- "import"
- "include"
- "inherits"
- "node"
- "realize"
- "true"
- )
- "\\|")
- "\\)\\>\\)")
- 1)
- ;; variables
- '("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(nil\\|self\\|true\\|false\\)\\>"
- 2 font-lock-variable-name-face)
- ;; variables
- '("\\(\\$\\([^a-zA-Z0-9 \n]\\|[0-9]\\)\\)\\W"
- 1 font-lock-variable-name-face)
- '("\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+"
- 0 font-lock-variable-name-face)
- ;; usage of types
- '("^\\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\\)\\)"
- (2 font-lock-string-face))
- )
- "*Additional expressions to highlight in puppet mode."))
- )
+ (set (make-local-variable 'font-lock-keywords) puppet-font-lock-keywords)
+ (set (make-local-variable 'font-lock-multiline) t)
+ (set (make-local-variable 'font-lock-defaults)
+ '((puppet-font-lock-keywords) nil nil))
+ (set (make-local-variable 'font-lock-syntax-table)
+ puppet-font-lock-syntax-table)
+ (run-hooks 'puppet-mode-hook))
(provide 'puppet-mode)
diff --git a/ext/ldap/puppet.schema b/ext/ldap/puppet.schema
index d8dc4260d..a7a5f46ff 100644
--- a/ext/ldap/puppet.schema
+++ b/ext/ldap/puppet.schema
@@ -17,6 +17,11 @@ attributetype ( 1.1.3.11 NAME 'environment'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+attributetype ( 1.1.3.12 NAME 'puppetvar'
+ DESC 'A variable setting for puppet'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
objectclass ( 1.1.1.2 NAME 'puppetClient' SUP top AUXILIARY
DESC 'Puppet Client objectclass'
- MAY ( puppetclass $ parentnode $ environment ))
+ MAY ( puppetclass $ parentnode $ environment $ puppetvar ))
diff --git a/install.rb b/install.rb
index d744f2d2d..c32c24245 100755
--- a/install.rb
+++ b/install.rb
@@ -106,7 +106,7 @@ def do_man(man, strip = 'man/')
File.install(mf, omf, 0644, true)
gzip = %x{which gzip}
gzip.chomp!
- %x{#{gzip} #{omf}}
+ %x{#{gzip} -f #{omf}}
end
end
@@ -210,6 +210,15 @@ def prepare_installation
end
end
+ # Mac OS X 10.5 declares bindir and sbindir as
+ # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin
+ # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/sbin
+ # which is not generally where people expect executables to be installed
+ if RUBY_PLATFORM == "universal-darwin9.0"
+ Config::CONFIG['bindir'] = "/usr/bin"
+ Config::CONFIG['sbindir'] = "/usr/sbin"
+ end
+
if (destdir = ENV['DESTDIR'])
bindir = "#{destdir}#{Config::CONFIG['bindir']}"
sbindir = "#{destdir}#{Config::CONFIG['sbindir']}"
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 78cf7c47d..d77ec0486 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -516,9 +516,11 @@ module Puppet
# Central fact information.
self.setdefaults(:main,
- :factpath => ["$vardir/facts",
- "Where Puppet should look for facts. Multiple directories should
- be colon-separated, like normal PATH variables."],
+ :factpath => {:default => "$vardir/facts",
+ :desc => "Where Puppet should look for facts. Multiple directories should
+ be colon-separated, like normal PATH variables.",
+ :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter.
+ :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }},
:factdest => ["$vardir/facts",
"Where Puppet should store facts that it pulls down from the central
server."],
@@ -633,6 +635,10 @@ module Puppet
:ldapclassattrs => ["puppetclass",
"The LDAP attributes to use to define Puppet classes. Values
should be comma-separated."],
+ :ldapstackedattrs => ["puppetvar",
+ "The LDAP attributes that should be stacked to arrays by adding
+ the values in all hierarchy elements of the tree. Values
+ should be comma-separated."],
:ldapattrs => ["all",
"The LDAP attributes to include when querying LDAP for nodes. All
returned attributes are set as variables in the top-level scope.
diff --git a/lib/puppet/indirector/node/ldap.rb b/lib/puppet/indirector/node/ldap.rb
index 6c41c18d4..bc58908fd 100644
--- a/lib/puppet/indirector/node/ldap.rb
+++ b/lib/puppet/indirector/node/ldap.rb
@@ -19,6 +19,8 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
node = Puppet::Node.new(name)
+ information[:stacked_parameters] = {}
+
parent_info = nil
parent = information[:parent]
parents = [name]
@@ -34,6 +36,10 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
raise Puppet::Error.new("Could not find parent node '%s'" % parent)
end
information[:classes] += parent_info[:classes]
+ parent_info[:stacked].each do |value|
+ param = value.split('=', 2)
+ information[:stacked_parameters][param[0]] = param[1]
+ end
parent_info[:parameters].each do |param, value|
# Specifically test for whether it's set, so false values are handled
# correctly.
@@ -45,6 +51,15 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
parent = parent_info[:parent]
end
+ information[:stacked].each do |value|
+ param = value.split('=', 2)
+ information[:stacked_parameters][param[0]] = param[1]
+ end
+
+ information[:stacked_parameters].each do |param, value|
+ information[:parameters][param] = value unless information[:parameters].include?(param)
+ end
+
node.classes = information[:classes].uniq unless information[:classes].empty?
node.parameters = information[:parameters] unless information[:parameters].empty?
node.environment = information[:environment] if information[:environment]
@@ -62,6 +77,12 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
end
end
+ # The attributes that Puppet will stack as array over the full
+ # hierarchy.
+ def stacked_attributes
+ Puppet[:ldapstackedattrs].split(/\s*,\s*/)
+ end
+
# Process the found entry. We assume that we don't just want the
# ldap object.
def process(name, entry)
@@ -85,6 +106,14 @@ class Puppet::Node::Ldap < Puppet::Indirector::Ldap
end
}
+ result[:stacked] = []
+ stacked_attributes.each { |attr|
+ if values = entry.vals(attr)
+ result[:stacked] = result[:stacked] + values
+ end
+ }
+
+
result[:parameters] = entry.to_hash.inject({}) do |hash, ary|
if ary[1].length == 1
hash[ary[0]] = ary[1].shift
diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index 8fba41121..d67b3d275 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -332,7 +332,7 @@ class Puppet::Parser::Compiler
unless remaining.empty?
fail Puppet::ParseError,
- "Could not find object(s) %s" % remaining.collect { |o|
+ "Could not find resource(s) %s for overriding" % remaining.collect { |o|
o.ref
}.join(", ")
end
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index e0b60e161..93991275c 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -165,7 +165,7 @@ module Functions
type is defined, either as a native type or a defined type, or whether a class is defined.
This is useful for checking whether a class is defined and only including it if it is.
This function can also test whether a resource has been defined, using resource references
- (e.g., ``if defined(File['/tmp/myfile'] { ... }``). This function is unfortunately
+ (e.g., ``if defined(File['/tmp/myfile']) { ... }``). This function is unfortunately
dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals|
result = false
vals.each do |val|
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index f27c1c5c8..04ca41494 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -62,7 +62,7 @@ class Puppet::Parser::Interpreter
# exception elsewhere and reuse the parser. If one doesn't
# exist, then reraise.
if @parsers[environment]
- Puppet.err detail
+ Puppet.err(detail.to_s + "; using previously parsed manifests")
else
raise detail
end
diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb
index 7a8f74156..4790cea30 100644
--- a/lib/puppet/parser/templatewrapper.rb
+++ b/lib/puppet/parser/templatewrapper.rb
@@ -20,6 +20,15 @@ class Puppet::Parser::TemplateWrapper
end
end
+ # Should return true if a variable is defined, false if it is not
+ def has_variable?(name)
+ if @scope.lookupvar(name.to_s, false) != :undefined
+ true
+ else
+ false
+ end
+ end
+
# Ruby treats variables like methods, so we can cheat here and
# trap missing vars like they were missing methods.
def method_missing(name, *args)
diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb
index e73bb0cb6..c02e15029 100644
--- a/lib/puppet/provider.rb
+++ b/lib/puppet/provider.rb
@@ -5,6 +5,10 @@ class Puppet::Provider
include Puppet::Util::Warnings
extend Puppet::Util::Warnings
+ require 'puppet/provider/confiner'
+
+ extend Puppet::Provider::Confiner
+
Puppet::Util.logmethods(self, true)
class << self
@@ -40,27 +44,13 @@ class Puppet::Provider
[name, self.name]
end
- if command == :missing
- return nil
- end
-
- command
+ return binary(command)
end
# Define commands that are not optional.
def self.commands(hash)
optional_commands(hash) do |name, path|
- confine :exists => path
- end
- end
-
- def self.confine(hash)
- hash.each do |p,v|
- if v.is_a? Array
- @confines[p] += v
- else
- @confines[p] << v
- end
+ confine :exists => path, :for_binary => true
end
end
@@ -108,10 +98,6 @@ class Puppet::Provider
def self.initvars
@defaults = {}
@commands = {}
- @origcommands = {}
- @confines = Hash.new do |hash, key|
- hash[key] = []
- end
end
# The method for returning a list of provider instances. Note that it returns providers, preferably with values already
@@ -180,16 +166,7 @@ class Puppet::Provider
def self.optional_commands(hash)
hash.each do |name, path|
name = symbolize(name)
- @origcommands[name] = path
-
- # Try to find the full path (or verify already-full paths); otherwise
- # store that the command is missing so we know it's defined but absent.
- if tmp = binary(path)
- path = tmp
- @commands[name] = path
- else
- @commands[name] = :missing
- end
+ @commands[name] = path
if block_given?
yield(name, path)
@@ -208,69 +185,6 @@ class Puppet::Provider
@source
end
- # Check whether this implementation is suitable for our platform.
- def self.suitable?(short = true)
- # A single false result is sufficient to turn the whole thing down.
- # We don't return 'true' until the very end, though, so that every
- # confine is tested.
- missing = {}
- @confines.each do |check, values|
- case check
- when :exists:
- values.each do |value|
- unless value and FileTest.exists? value
- debug "Not suitable: missing %s" % value
- return false if short
- missing[:exists] ||= []
- missing[:exists] << value
- end
- end
- when :true:
- values.each do |v|
- debug "Not suitable: false value"
- unless v
- return false if short
- missing[:true] ||= 0
- missing[:true] += 1
- end
- end
- when :false:
- values.each do |v|
- debug "Not suitable: true value"
- if v and short
- return false if short
- missing[:false] ||= 0
- missing[:false] += 1
- end
- end
- else # Just delegate everything else to facter
- if result = Facter.value(check)
- result = result.to_s.downcase.intern
-
- found = values.find do |v|
- result == v.to_s.downcase.intern
- end
- unless found
- debug "Not suitable: %s not in %s" % [check, values]
- return false if short
- missing[:facter] ||= {}
- missing[:facter][check] = values
- end
- else
- return false if short
- missing[:facter] ||= {}
- missing[:facter][check] = values
- end
- end
- end
-
- if short
- return true
- else
- return missing
- end
- end
-
# Does this provider support the specified parameter?
def self.supports_parameter?(param)
if param.is_a?(Class)
@@ -309,8 +223,8 @@ class Puppet::Provider
end
dochook(:commands) do
- if @origcommands.length > 0
- return " Required binaries: " + @origcommands.collect do |n, c|
+ if @commands.length > 0
+ return " Required binaries: " + @commands.collect do |n, c|
"``#{c}``"
end.join(", ") + "."
end
diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb
new file mode 100644
index 000000000..35b80fdcf
--- /dev/null
+++ b/lib/puppet/provider/confine.rb
@@ -0,0 +1,77 @@
+# The class that handles testing whether our providers
+# actually work or not.
+require 'puppet/util'
+
+class Puppet::Provider::Confine
+ include Puppet::Util
+
+ @tests = {}
+
+ class << self
+ attr_accessor :name
+ end
+
+ def self.inherited(klass)
+ name = klass.to_s.split("::").pop.downcase.to_sym
+ raise "Test %s is already defined" % name if @tests.include?(name)
+
+ klass.name = name
+
+ @tests[name] = klass
+ end
+
+ def self.test(name)
+ unless @tests[name]
+ begin
+ require "puppet/provider/confine/%s" % name
+ rescue LoadError => detail
+ unless detail.to_s.include?("no such file")
+ warn "Could not load confine test '%s': %s" % [name, detail]
+ end
+ # Could not find file
+ end
+ end
+ return @tests[name]
+ end
+
+ attr_reader :values
+
+ # Mark that this confine is used for testing binary existence.
+ attr_accessor :for_binary
+ def for_binary?
+ for_binary
+ end
+
+ def initialize(values)
+ values = [values] unless values.is_a?(Array)
+ @values = values
+ end
+
+ # Provide a hook for the message when there's a failure.
+ def message(value)
+ ""
+ end
+
+ # Collect the results of all of them.
+ def result
+ values.collect { |value| pass?(value) }
+ end
+
+ # Test whether our confine matches.
+ def valid?
+ values.each do |value|
+ unless pass?(value)
+ Puppet.debug message(value)
+ return false
+ end
+ end
+
+ return true
+ ensure
+ reset
+ end
+
+ # Provide a hook for subclasses.
+ def reset
+ end
+end
diff --git a/lib/puppet/provider/confine/exists.rb b/lib/puppet/provider/confine/exists.rb
new file mode 100644
index 000000000..1d1ed8c84
--- /dev/null
+++ b/lib/puppet/provider/confine/exists.rb
@@ -0,0 +1,22 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::Exists < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.inject([]) { |total, confine| total + confine.summary }
+ end
+
+ def pass?(value)
+ if for_binary?
+ return false unless value = binary(value)
+ end
+ value and FileTest.exist?(value)
+ end
+
+ def message(value)
+ "file %s does not exist" % value
+ end
+
+ def summary
+ result.zip(values).inject([]) { |array, args| val, f = args; array << f unless val; array }
+ end
+end
diff --git a/lib/puppet/provider/confine/facter.rb b/lib/puppet/provider/confine/facter.rb
new file mode 100644
index 000000000..9bb66c058
--- /dev/null
+++ b/lib/puppet/provider/confine/facter.rb
@@ -0,0 +1,37 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::Facter < Puppet::Provider::Confine
+ def self.summarize(confines)
+ result = Hash.new { |hash, key| hash[key] = [] }
+ confines.inject(result) { |total, confine| total[confine.fact] += confine.values unless confine.valid?; total }
+ end
+
+ attr_accessor :fact
+
+ # Are we a facter comparison?
+ def facter?
+ defined?(@facter)
+ end
+
+ # Retrieve the value from facter
+ def facter_value
+ unless defined?(@facter_value) and @facter_value
+ @facter_value = ::Facter.value(@fact).to_s.downcase
+ end
+ @facter_value
+ end
+
+ def message(value)
+ "facter value '%s' for '%s' not in required list '%s'" % [value, self.fact, values.join(",")]
+ end
+
+ def pass?(value)
+ facter_value == value.to_s.downcase
+ end
+
+ def reset
+ # Reset the cache. We want to cache it during a given
+ # run, but across runs.
+ @facter_value = nil
+ end
+end
diff --git a/lib/puppet/provider/confine/false.rb b/lib/puppet/provider/confine/false.rb
new file mode 100644
index 000000000..b5b2b51c8
--- /dev/null
+++ b/lib/puppet/provider/confine/false.rb
@@ -0,0 +1,19 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::False < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.inject(0) { |count, confine| count + confine.summary }
+ end
+
+ def pass?(value)
+ ! value
+ end
+
+ def message(value)
+ "true value when expecting false"
+ end
+
+ def summary
+ result.find_all { |v| v == false }.length
+ end
+end
diff --git a/lib/puppet/provider/confine/feature.rb b/lib/puppet/provider/confine/feature.rb
new file mode 100644
index 000000000..1d92b001a
--- /dev/null
+++ b/lib/puppet/provider/confine/feature.rb
@@ -0,0 +1,17 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::Feature < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.collect { |c| c.values }.flatten.uniq.find_all { |value| ! confines[0].pass?(value) }
+ end
+
+ # Is the named feature available?
+ def pass?(value)
+ Puppet.features.send(value.to_s + "?")
+ end
+
+ def message(value)
+ "feature %s is missing" % value
+ end
+end
+
diff --git a/lib/puppet/provider/confine/true.rb b/lib/puppet/provider/confine/true.rb
new file mode 100644
index 000000000..86b3b144f
--- /dev/null
+++ b/lib/puppet/provider/confine/true.rb
@@ -0,0 +1,20 @@
+require 'puppet/provider/confine'
+
+class Puppet::Provider::Confine::True < Puppet::Provider::Confine
+ def self.summarize(confines)
+ confines.inject(0) { |count, confine| count + confine.summary }
+ end
+
+ def pass?(value)
+ # Double negate, so we only get true or false.
+ ! ! value
+ end
+
+ def message(value)
+ "false value when expecting true"
+ end
+
+ def summary
+ result.find_all { |v| v == true }.length
+ end
+end
diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/provider/confine_collection.rb
new file mode 100644
index 000000000..0c80086c9
--- /dev/null
+++ b/lib/puppet/provider/confine_collection.rb
@@ -0,0 +1,47 @@
+# Manage a collection of confines, returning a boolean or
+# helpful information.
+require 'puppet/provider/confine'
+
+class Puppet::Provider::ConfineCollection
+ def confine(hash)
+ if hash.include?(:for_binary)
+ for_binary = true
+ hash.delete(:for_binary)
+ else
+ for_binary = false
+ end
+ hash.each do |test, values|
+ if klass = Puppet::Provider::Confine.test(test)
+ @confines << klass.new(values)
+ @confines[-1].for_binary = true if for_binary
+ else
+ confine = Puppet::Provider::Confine.test(:facter).new(values)
+ confine.fact = test
+ @confines << confine
+ end
+ end
+ end
+
+ def initialize
+ @confines = []
+ end
+
+ # Return a hash of the whole confine set, used for the Provider
+ # reference.
+ def summary
+ confines = Hash.new { |hash, key| hash[key] = [] }
+ @confines.each { |confine| confines[confine.class] << confine }
+ result = {}
+ confines.each do |klass, list|
+ value = klass.summarize(list)
+ next if (value.respond_to?(:length) and value.length == 0) or (value == 0)
+ result[klass.name] = value
+
+ end
+ result
+ end
+
+ def valid?
+ ! @confines.detect { |c| ! c.valid? }
+ end
+end
diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb
new file mode 100644
index 000000000..4605523e8
--- /dev/null
+++ b/lib/puppet/provider/confiner.rb
@@ -0,0 +1,20 @@
+require 'puppet/provider/confine_collection'
+
+module Puppet::Provider::Confiner
+ def confine(hash)
+ confine_collection.confine(hash)
+ end
+
+ def confine_collection
+ unless defined?(@confine_collection)
+ @confine_collection = Puppet::Provider::ConfineCollection.new
+ end
+ @confine_collection
+ end
+
+ # Check whether this implementation is suitable for our platform.
+ def suitable?(short = true)
+ return confine_collection.valid? if short
+ return confine_collection.summary
+ end
+end
diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb
index 7ddcc0505..e2228b15e 100755
--- a/lib/puppet/provider/cron/crontab.rb
+++ b/lib/puppet/provider/cron/crontab.rb
@@ -30,7 +30,7 @@ Puppet::Type.type(:cron).provide(:crontab,
}
crontab = record_line :crontab, :fields => %w{minute hour monthday month weekday command},
- :match => %r{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$},
+ :match => %r{^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$},
:optional => %w{minute hour weekday month monthday}, :absent => "*"
class << crontab
diff --git a/lib/puppet/provider/group/ldap.rb b/lib/puppet/provider/group/ldap.rb
new file mode 100644
index 000000000..632358ff1
--- /dev/null
+++ b/lib/puppet/provider/group/ldap.rb
@@ -0,0 +1,37 @@
+require 'puppet/provider/ldap'
+
+Puppet::Type.type(:group).provide :ldap, :parent => Puppet::Provider::Ldap do
+ desc "Group management via ``ldap``. This provider requires that you
+ have valid values for all of the ldap-related settings,
+ including ``ldapbase``. You will also almost definitely need settings
+ for ``ldapuser`` and ``ldappassword``, so that your clients can write
+ to ldap.
+
+ Note that this provider will automatically generate a GID for you if
+ you do not specify one, but it is a potentially expensive operation,
+ as it iterates across all existing groups to pick the appropriate next
+ one."
+
+ confine :true => Puppet.features.ldap?
+
+ # We're mapping 'members' here because we want to make it
+ # easy for the ldap user provider to manage groups. This
+ # way it can just use the 'update' method in the group manager,
+ # whereas otherwise it would need to replicate that code.
+ manages(:posixGroup).at("ou=Groups").and.maps :name => :cn, :gid => :gidNumber, :members => :memberUid
+
+ # Find the next gid after the current largest gid.
+ provider = self
+ manager.generates(:gidNumber).with do
+ largest = 0
+ provider.manager.search.each do |hash|
+ next unless value = hash[:gid]
+ num = value[0].to_i
+ if num > largest
+ largest = num
+ end
+ end
+ largest + 1
+ end
+
+end
diff --git a/lib/puppet/provider/ldap.rb b/lib/puppet/provider/ldap.rb
new file mode 100644
index 000000000..76834f94d
--- /dev/null
+++ b/lib/puppet/provider/ldap.rb
@@ -0,0 +1,137 @@
+require 'puppet/provider'
+
+# The base class for LDAP providers.
+class Puppet::Provider::Ldap < Puppet::Provider
+ require 'puppet/util/ldap/manager'
+
+ class << self
+ attr_reader :manager
+ end
+
+ # Look up all instances at our location. Yay.
+ def self.instances
+ return [] unless list = manager.search
+
+ list.collect { |entry| new(entry) }
+ end
+
+ # Specify the ldap manager for this provider, which is
+ # used to figure out how we actually interact with ldap.
+ def self.manages(*args)
+ @manager = Puppet::Util::Ldap::Manager.new
+ @manager.manages(*args)
+
+ # Set up our getter/setter methods.
+ mk_resource_methods
+ return @manager
+ end
+
+ # Query all of our resources from ldap.
+ def self.prefetch(resources)
+ resources.each do |name, resource|
+ if result = manager.find(name)
+ result[:ensure] = :present
+ resource.provider = new(result)
+ else
+ resource.provider = new(:ensure => :absent)
+ end
+ end
+ end
+
+ attr_reader :ldap_properties
+
+ def manager
+ self.class.manager
+ end
+
+ def create
+ @property_hash[:ensure] = :present
+ self.class.resource_type.validproperties.each do |property|
+ if val = resource.should(property)
+ @property_hash[property] = val
+ end
+ end
+ end
+
+ def delete
+ @property_hash[:ensure] = :absent
+ end
+
+ def exists?
+ @property_hash[:ensure] != :absent
+ end
+
+ # Apply our changes to ldap, yo.
+ def flush
+ # Just call the manager's update() method.
+ @property_hash.delete(:groups)
+ @ldap_properties.delete(:groups)
+ manager.update(name, ldap_properties, properties)
+ @property_hash.clear
+ @ldap_properties.clear
+ end
+
+ def initialize(*args)
+ raise(Puppet::DevError, "No LDAP Configuration defined for %s" % self.class) unless self.class.manager
+ raise(Puppet::DevError, "Invalid LDAP Configuration defined for %s" % self.class) unless self.class.manager.valid?
+ super
+
+ @property_hash = @property_hash.inject({}) do |result, ary|
+ param, values = ary
+
+ # Skip any attributes we don't manage.
+ next result unless self.class.resource_type.validattr?(param)
+
+ paramclass = self.class.resource_type.attrclass(param)
+
+ unless values.is_a?(Array)
+ result[param] = values
+ next result
+ end
+
+ # Only use the first value if the attribute class doesn't manage
+ # arrays of values.
+ if paramclass.superclass == Puppet::Parameter or paramclass.array_matching == :first
+ result[param] = values[0]
+ else
+ result[param] = values
+ end
+ result
+ end
+
+ # Make a duplicate, so that we have a copy for comparison
+ # at the end.
+ @ldap_properties = @property_hash.dup
+ end
+
+ # Return the current state of ldap.
+ def ldap_properties
+ @ldap_properties.dup
+ end
+
+ # Return (and look up if necessary) the desired state.
+ def properties
+ if @property_hash.empty?
+ @property_hash = query || {:ensure => :absent}
+ if @property_hash.empty?
+ @property_hash[:ensure] = :absent
+ end
+ end
+ @property_hash.dup
+ end
+
+ # Collect the current attributes from ldap. Returns
+ # the results, but also stores the attributes locally,
+ # so we have something to compare against when we update.
+ # LAK:NOTE This is normally not used, because we rely on prefetching.
+ def query
+ # Use the module function.
+ unless attributes = manager.find(name)
+ @ldap_properties = {}
+ return nil
+ end
+
+ @ldap_properties = attributes
+ return @ldap_properties.dup
+ end
+end
diff --git a/lib/puppet/provider/nameservice/objectadd.rb b/lib/puppet/provider/nameservice/objectadd.rb
index 8ebf8924b..4682b5169 100644
--- a/lib/puppet/provider/nameservice/objectadd.rb
+++ b/lib/puppet/provider/nameservice/objectadd.rb
@@ -25,7 +25,7 @@ class ObjectAdd < Puppet::Provider::NameService
cmd = [command(:modify),
flag(param),
value]
- if @resource[:allowdupe] == :true
+ if @resource[:allowdupe] == :true && param == :uid
cmd << "-o"
end
cmd << @resource[:name]
diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb
index 98ca1efa6..35684e11d 100755
--- a/lib/puppet/provider/package/rpm.rb
+++ b/lib/puppet/provider/package/rpm.rb
@@ -67,7 +67,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
end
cmd = [command(:rpm), "-q", "--qf", "#{NEVRAFORMAT}\n", "-p", "#{@resource[:source]}"]
- h = nevra_to_hash(execfail(cmd, Puppet::Error))
+ h = self.class.nevra_to_hash(execfail(cmd, Puppet::Error))
return h[:ensure]
end
diff --git a/lib/puppet/provider/package/urpmi.rb b/lib/puppet/provider/package/urpmi.rb
index 8adc62ab4..a95835284 100644
--- a/lib/puppet/provider/package/urpmi.rb
+++ b/lib/puppet/provider/package/urpmi.rb
@@ -1,6 +1,6 @@
Puppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do
desc "Support via ``urpmi``."
- commands :urpmi => "urpmi", :rpm => "rpm"
+ commands :urpmi => "urpmi", :urpmq => "urpmq", :rpm => "rpm"
if command('rpm')
confine :true => begin
@@ -41,9 +41,9 @@ Puppet::Type.type(:package).provide :urpmi, :parent => :rpm, :source => :rpm do
# What's the latest package version available?
def latest
- output = urpmi "-S", :available, @resource[:name]
+ output = urpmq "-S", @resource[:name]
- if output =~ /^#{@resource[:name]}\S+\s+(\S+)\s/
+ if output =~ /^#{@resource[:name]}\s+:\s+.*\(\s+(\S+)\s+\)/
return $1
else
# urpmi didn't find updates, pretend the current
diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb
index 254b4fe4c..8964322b6 100755
--- a/lib/puppet/provider/service/base.rb
+++ b/lib/puppet/provider/service/base.rb
@@ -1,10 +1,10 @@
Puppet::Type.type(:service).provide :base do
desc "The simplest form of service support. You have to specify
enough about your service for this to work; the minimum you can specify
- is a binary for starting the process, and this same binary will be searched
- for in the process table to stop the service. It is preferable to
- specify start, stop, and status commands, akin to how you would do
- so using ``init``."
+ is a binary for starting the process, and this same binary will be
+ searched for in the process table to stop the service. It is
+ preferable to specify start, stop, and status commands, akin to how you
+ would do so using ``init``."
commands :kill => "kill"
diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb
index 274c334a3..3081d0eb8 100755
--- a/lib/puppet/provider/service/init.rb
+++ b/lib/puppet/provider/service/init.rb
@@ -2,9 +2,9 @@
# customizations of this module.
Puppet::Type.type(:service).provide :init, :parent => :base do
desc "Standard init service management. This provider assumes that the
- init script has not ``status`` command, because so few scripts do,
- so you need to either provide a status command or specify via ``hasstatus``
- that one already exists in the init script."
+ init script has no ``status`` command, because so few scripts do,
+ so you need to either provide a status command or specify via
+ ``hasstatus`` that one already exists in the init script."
class << self
attr_accessor :defpath
diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb
index b013c34dc..e2d6ac947 100755
--- a/lib/puppet/provider/service/redhat.rb
+++ b/lib/puppet/provider/service/redhat.rb
@@ -1,11 +1,11 @@
-# Manage debian services. Start/stop is the same as InitSvc, but enable/disable
-# is special.
+# Manage Red Hat services. Start/stop uses /sbin/service and enable/disable uses chkconfig
+
Puppet::Type.type(:service).provide :redhat, :parent => :init do
desc "Red Hat's (and probably many others) form of ``init``-style service
management; uses ``chkconfig`` for service enabling and disabling."
- commands :chkconfig => "/sbin/chkconfig"
-
+ commands :chkconfig => "/sbin/chkconfig", :service => "/sbin/service"
+
defaultfor :operatingsystem => [:redhat, :fedora, :suse, :centos]
def self.defpath
@@ -16,7 +16,6 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do
def disable
begin
output = chkconfig(@resource[:name], :off)
- output += chkconfig("--del", @resource[:name])
rescue Puppet::ExecutionFailure
raise Puppet::Error, "Could not disable %s: %s" %
[self.name, output]
@@ -43,12 +42,28 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do
# in the init scripts.
def enable
begin
- output = chkconfig("--add", @resource[:name])
- output += chkconfig(@resource[:name], :on)
+ output = chkconfig(@resource[:name], :on)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not enable %s: %s" %
[self.name, detail]
end
end
+
+ def restart
+ if @resource[:hasrestart] == true
+ service(@resource[:name], "restart")
+ else
+ return false
+ end
+ end
+
+ def start
+ service(@resource[:name], "start")
+ end
+
+ def stop
+ service(@resource[:name], "stop")
+ end
+
end
diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb
new file mode 100644
index 000000000..7cb6626de
--- /dev/null
+++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb
@@ -0,0 +1,49 @@
+require 'puppet/provider/parsedfile'
+
+Puppet::Type.type(:ssh_authorized_key).provide(:parsed,
+ :parent => Puppet::Provider::ParsedFile,
+ :filetype => :flat,
+ :default_target => ''
+) do
+ desc "Parse and generate authorized_keys files for SSH."
+
+ text_line :comment, :match => /^#/
+ text_line :blank, :match => /^\s+/
+
+ record_line :parsed,
+ :fields => %w{options type key name},
+ :optional => %w{options},
+ :rts => /^\s+/,
+ :match => /^(?:([^ ]+) )?(ssh-dss|ssh-rsa) ([^ ]+)(?: (.+))?$/,
+ :post_parse => proc { |record|
+ if record[:options].nil?
+ record[:options] = [:absent]
+ else
+ record[:options] = record[:options].split(',')
+ end
+ },
+ :pre_gen => proc { |record|
+ if record[:options].include?(:absent)
+ record[:options] = ""
+ else
+ record[:options] = record[:options].join(',')
+ end
+ }
+
+ def prefetch
+ if not @resource.should(:target)
+ #
+ # Set default target when user is given
+ if val = @resource.should(:user)
+ target = File.expand_path("~%s/.ssh/authorized_keys" % val)
+ Puppet::debug("Setting target to %s" % target)
+ @resource[:target] = target
+ else
+ raise Puppet::Error, "Missing attribute 'user' or 'target'"
+ end
+ end
+
+ super
+ end
+end
+
diff --git a/lib/puppet/provider/user/ldap.rb b/lib/puppet/provider/user/ldap.rb
new file mode 100644
index 000000000..ba91a871e
--- /dev/null
+++ b/lib/puppet/provider/user/ldap.rb
@@ -0,0 +1,115 @@
+require 'puppet/provider/ldap'
+
+Puppet::Type.type(:user).provide :ldap, :parent => Puppet::Provider::Ldap do
+ desc "User management via ``ldap``. This provider requires that you
+ have valid values for all of the ldap-related settings,
+ including ``ldapbase``. You will also almost definitely need settings
+ for ``ldapuser`` and ``ldappassword``, so that your clients can write
+ to ldap.
+
+ Note that this provider will automatically generate a UID for you if
+ you do not specify one, but it is a potentially expensive operation,
+ as it iterates across all existing users to pick the appropriate next
+ one."
+
+ confine :true => Puppet.features.ldap?
+
+ manages(:posixAccount, :person).at("ou=People").named_by(:uid).and.maps :name => :uid,
+ :password => :userPassword,
+ :comment => :cn,
+ :uid => :uidNumber,
+ :gid => :gidNumber,
+ :home => :homeDirectory,
+ :shell => :loginShell
+
+ # Use the last field of a space-separated array as
+ # the sn. LDAP requires a surname, for some stupid reason.
+ manager.generates(:sn).from(:cn).with do |cn|
+ x = 1
+ cn[0].split(/\s+/)[-1]
+ end
+
+ # Find the next uid after the current largest uid.
+ provider = self
+ manager.generates(:uidNumber).with do
+ largest = 0
+ provider.manager.search.each do |hash|
+ next unless value = hash[:uid]
+ num = value[0].to_i
+ if num > largest
+ largest = num
+ end
+ end
+ largest + 1
+ end
+
+ # Find all groups this user is a member of in ldap.
+ def groups
+ # We want to cache the current result, so we know if we
+ # have to remove old values.
+ unless @property_hash[:groups]
+ unless result = group_manager.search("memberUid=%s" % name)
+ return @property_hash[:groups] = :absent
+ end
+
+ return @property_hash[:groups] = result.collect { |r| r[:name] }.join(",")
+ end
+ return @property_hash[:groups]
+ end
+
+ # Manage the list of groups this user is a member of.
+ def groups=(values)
+ should = values.split(",")
+
+ if groups() == :absent
+ is = []
+ else
+ is = groups().split(",")
+ end
+
+ modes = {}
+ [is, should].flatten.uniq.each do |group|
+ # Skip it when they're in both
+ next if is.include?(group) and should.include?(group)
+
+ # We're adding a group.
+ modes[group] = :add and next unless is.include?(group)
+
+ # We're removing a group.
+ modes[group] = :remove and next unless should.include?(group)
+ end
+
+ modes.each do |group, form|
+ self.fail "Could not find ldap group %s" % group unless ldap_group = group_manager.find(group)
+
+ current = ldap_group[:members]
+
+ if form == :add
+ if current.is_a?(Array) and ! current.empty?
+ new = current + [name]
+ else
+ new = [name]
+ end
+ else
+ new = current - [name]
+ new = :absent if new.empty?
+ end
+
+ group_manager.update(group, {:ensure => :present, :members => current}, {:ensure => :present, :members => new})
+ end
+ end
+
+ private
+
+ def group_manager
+ Puppet::Type.type(:group).provider(:ldap).manager
+ end
+
+ def group_properties(values)
+ if values.empty? or values == :absent
+ {:ensure => :present}
+ else
+ {:ensure => :present, :members => values}
+ end
+ end
+end
diff --git a/lib/puppet/reference/providers.rb b/lib/puppet/reference/providers.rb
index da815ddf1..610c7550d 100644
--- a/lib/puppet/reference/providers.rb
+++ b/lib/puppet/reference/providers.rb
@@ -71,6 +71,8 @@ providers = Puppet::Util::Reference.newreference :providers, :title => "Provider
details += " - Got %s true tests that should have been false\n" % values
when :false:
details += " - Got %s false tests that should have been true\n" % values
+ when :feature:
+ details += " - Missing features %s\n" % values.collect { |f| f.to_s }.join(",")
end
end
notes << details
diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb
index e18630cc8..1679b73a3 100755
--- a/lib/puppet/type/mount.rb
+++ b/lib/puppet/type/mount.rb
@@ -141,7 +141,9 @@ module Puppet
newproperty(:dump) do
desc "Whether to dump the mount. Not all platforms
- support this."
+ support this. Valid values are ``1`` or ``0``. Default is ``0``."
+
+ newvalue(%r{(0|1)}) { }
defaultto {
if @resource.managed?
diff --git a/lib/puppet/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb
new file mode 100644
index 000000000..e28fb7cda
--- /dev/null
+++ b/lib/puppet/type/ssh_authorized_key.rb
@@ -0,0 +1,44 @@
+module Puppet
+ newtype(:ssh_authorized_key) do
+ @doc = "Manages ssh authorized keys."
+
+ ensurable
+
+ newparam(:name) do
+ desc "The ssh key comment."
+
+ isnamevar
+ end
+
+ newproperty(:type) do
+ desc "The encryption type used. Probably ssh-dss or ssh-rsa for
+ ssh version 2. Not used for ssh version 1."
+
+ newvalue("ssh-dss")
+ newvalue("ssh-rsa")
+
+ aliasvalue(:dsa, "ssh-dss")
+ aliasvalue(:rsa, "ssh-rsa")
+ end
+
+ newproperty(:key) do
+ desc "The key itself; generally a long string of hex digits."
+ end
+
+ newproperty(:user) do
+ desc "The user account in which the ssh key should be installed."
+ end
+
+ newproperty(:target) do
+ desc "The file in which to store the ssh key."
+ end
+
+ newproperty(:options, :array_matching => :all) do
+ desc "Key options, see sshd(8) for possible values. Multiple values
+ should be specified as an array."
+
+ defaultto do :absent end
+ end
+ end
+end
+
diff --git a/lib/puppet/util/ldap.rb b/lib/puppet/util/ldap.rb
new file mode 100644
index 000000000..33f01f789
--- /dev/null
+++ b/lib/puppet/util/ldap.rb
@@ -0,0 +1,5 @@
+#
+# Created by Luke Kanies on 2008-3-23.
+# Copyright (c) 2008. All rights reserved.
+module Puppet::Util::Ldap
+end
diff --git a/lib/puppet/util/ldap/connection.rb b/lib/puppet/util/ldap/connection.rb
new file mode 100644
index 000000000..abcc07ecb
--- /dev/null
+++ b/lib/puppet/util/ldap/connection.rb
@@ -0,0 +1,57 @@
+#
+# Created by Luke Kanies on 2008-3-23.
+# Copyright (c) 2008. All rights reserved.
+require 'puppet/util/ldap'
+
+class Puppet::Util::Ldap::Connection
+ attr_accessor :host, :port, :user, :password, :reset, :ssl
+
+ attr_reader :connection
+
+ def close
+ connection.unbind if connection.bound?
+ end
+
+ def initialize(host, port, options = {})
+ raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries" unless Puppet.features.ldap?
+
+ @host, @port = host, port
+
+ options.each do |param, value|
+ begin
+ send(param.to_s + "=", value)
+ rescue
+ raise ArgumentError, "LDAP connections do not support %s parameters" % param
+ end
+ end
+ end
+
+ # Create a per-connection unique name.
+ def name
+ [host, port, user, password, ssl].collect { |p| p.to_s }.join("/")
+ end
+
+ # Should we reset the connection?
+ def reset?
+ reset
+ end
+
+ # Start our ldap connection.
+ def start
+ begin
+ case ssl
+ when :tls:
+ @connection = LDAP::SSLConn.new(host, port, true)
+ when true:
+ @connection = LDAP::SSLConn.new(host, port)
+ else
+ @connection = LDAP::Conn.new(host, port)
+ end
+ @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @connection.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @connection.simple_bind(user, password)
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+end
diff --git a/lib/puppet/util/ldap/generator.rb b/lib/puppet/util/ldap/generator.rb
new file mode 100644
index 000000000..2a868b0d9
--- /dev/null
+++ b/lib/puppet/util/ldap/generator.rb
@@ -0,0 +1,45 @@
+#
+# Created by Luke Kanies on 2008-3-28.
+# Copyright (c) 2008. All rights reserved.
+require 'puppet/util/ldap'
+
+class Puppet::Util::Ldap::Generator
+ # Declare the attribute we'll use to generate the value.
+ def from(source)
+ @source = source
+ return self
+ end
+
+ # Actually do the generation.
+ def generate(value = nil)
+ if value.nil?
+ @generator.call
+ else
+ @generator.call(value)
+ end
+ end
+
+ # Initialize our generator with the name of the parameter
+ # being generated.
+ def initialize(name)
+ @name = name
+ end
+
+ def name
+ @name.to_s
+ end
+
+ def source
+ if defined?(@source) and @source
+ @source.to_s
+ else
+ nil
+ end
+ end
+
+ # Provide the code that does the generation.
+ def with(&block)
+ @generator = block
+ return self
+ end
+end
diff --git a/lib/puppet/util/ldap/manager.rb b/lib/puppet/util/ldap/manager.rb
new file mode 100644
index 000000000..9761fc753
--- /dev/null
+++ b/lib/puppet/util/ldap/manager.rb
@@ -0,0 +1,281 @@
+require 'puppet/util/ldap'
+require 'puppet/util/ldap/connection'
+require 'puppet/util/ldap/generator'
+
+# The configuration class for LDAP providers, plus
+# connection handling for actually interacting with ldap.
+class Puppet::Util::Ldap::Manager
+ attr_reader :objectclasses, :puppet2ldap, :location, :rdn
+
+ # A null-op that just returns the config.
+ def and
+ return self
+ end
+
+ # Set the offset from the search base and return the config.
+ def at(location)
+ @location = location
+ return self
+ end
+
+ # The basic search base.
+ def base
+ [location, Puppet[:ldapbase]].join(",")
+ end
+
+ # Convert the name to a dn, then pass the args along to
+ # our connection.
+ def create(name, attributes)
+ attributes = attributes.dup
+
+ # Add the objectclasses
+ attributes["objectClass"] = objectclasses.collect { |o| o.to_s }
+ attributes["objectClass"] << "top" unless attributes["objectClass"].include?("top")
+
+ attributes[rdn.to_s] = [name]
+
+ # Generate any new values we might need.
+ generate(attributes)
+
+ # And create our resource.
+ connect { |conn| conn.add dn(name), attributes }
+ end
+
+ # Open, yield, and close the connection. Cannot be left
+ # open, at this point.
+ def connect
+ raise ArgumentError, "You must pass a block to #connect" unless block_given?
+
+ unless defined?(@connection) and @connection
+ if Puppet[:ldaptls]
+ ssl = :tls
+ elsif Puppet[:ldapssl]
+ ssl = true
+ else
+ ssl = false
+ end
+ options = {:ssl => ssl}
+ if user = Puppet[:ldapuser] and user != ""
+ options[:user] = user
+ end
+ if password = Puppet[:ldappassword] and password != ""
+ options[:password] = password
+ end
+ @connection = Puppet::Util::Ldap::Connection.new(Puppet[:ldapserver], Puppet[:ldapport], options)
+ end
+ @connection.start
+ begin
+ yield @connection.connection
+ ensure
+ @connection.close
+ end
+ return nil
+ end
+
+ # Convert the name to a dn, then pass the args along to
+ # our connection.
+ def delete(name)
+ connect { |connection| connection.delete dn(name) }
+ end
+
+ # Calculate the dn for a given resource.
+ def dn(name)
+ ["#{rdn.to_s}=%s" % name, base].join(",")
+ end
+
+ # Convert an ldap-style entry hash to a provider-style hash.
+ def entry2provider(entry)
+ raise ArgumentError, "Could not get dn from ldap entry" unless entry["dn"]
+
+ # DN is always a single-entry array. Strip off the bits before the
+ # first comma, then the bits after the remaining equal sign. This is the
+ # name.
+ name = entry["dn"].dup.pop.split(",").shift.split("=").pop
+
+ result = {:name => name}
+
+ @ldap2puppet.each do |ldap, puppet|
+ result[puppet] = entry[ldap.to_s] || :absent
+ end
+
+ result
+ end
+
+ # Create our normal search filter.
+ def filter
+ return "objectclass=%s" % objectclasses[0] if objectclasses.length == 1
+ return "(&(objectclass=" + objectclasses.join(")(objectclass=") + "))"
+ end
+
+ # Find the associated entry for a resource. Returns a hash, minus
+ # 'dn', or nil if the entry cannot be found.
+ def find(name)
+ result = nil
+ connect do |conn|
+ begin
+ conn.search2(dn(name), 0, "objectclass=*") do |result|
+ # Convert to puppet-appropriate attributes
+ return entry2provider(result)
+ end
+ rescue => detail
+ return nil
+ end
+ end
+ end
+
+ # Declare a new attribute generator.
+ def generates(parameter)
+ @generators << Puppet::Util::Ldap::Generator.new(parameter)
+ @generators[-1]
+ end
+
+ # Generate any extra values we need to make the ldap entry work.
+ def generate(values)
+ return unless @generators.length > 0
+
+ @generators.each do |generator|
+ # Don't override any values that might exist.
+ next if values[generator.name]
+
+ if generator.source
+ unless value = values[generator.source]
+ raise ArgumentError, "%s must be defined to generate %s" % [generator.source, generator.name]
+ end
+ result = generator.generate(value)
+ else
+ result = generator.generate
+ end
+
+ result = [result] unless result.is_a?(Array)
+ result = result.collect { |r| r.to_s }
+
+ values[generator.name] = result
+ end
+ end
+
+ def initialize
+ @rdn = :cn
+ @generators = []
+ end
+
+ # Specify what classes this provider models.
+ def manages(*classes)
+ @objectclasses = classes
+ return self
+ end
+
+ # Specify the attribute map. Assumes the keys are the puppet
+ # attributes, and the values are the ldap attributes, and creates a map
+ # for each direction.
+ def maps(attributes)
+ # The map with the puppet attributes as the keys
+ @puppet2ldap = attributes
+
+ # and the ldap attributes as the keys.
+ @ldap2puppet = attributes.inject({}) { |map, ary| map[ary[1]] = ary[0]; map }
+
+ return self
+ end
+
+ # Return the ldap name for a puppet attribute.
+ def ldap_name(attribute)
+ @puppet2ldap[attribute].to_s
+ end
+
+ # Convert the name to a dn, then pass the args along to
+ # our connection.
+ def modify(name, mods)
+ connect { |connection| connection.modify dn(name), mods }
+ end
+
+ # Specify the rdn that we use to build up our dn.
+ def named_by(attribute)
+ @rdn = attribute
+ self
+ end
+
+ # Return the puppet name for an ldap attribute.
+ def puppet_name(attribute)
+ @ldap2puppet[attribute]
+ end
+
+ # Search for all entries at our base. A potentially expensive search.
+ def search(sfilter = nil)
+ sfilter ||= filter()
+
+ result = []
+ connect do |conn|
+ conn.search2(base, 1, sfilter) do |entry|
+ result << entry2provider(entry)
+ end
+ end
+ return nil if result.empty?
+ return result
+ end
+
+ # Update the ldap entry with the desired state.
+ def update(name, is, should)
+ if should[:ensure] == :absent
+ Puppet.info "Removing %s from ldap" % dn(name)
+ delete(name)
+ return
+ end
+
+ # We're creating a new entry
+ if is.empty? or is[:ensure] == :absent
+ Puppet.info "Creating %s in ldap" % dn(name)
+ # Remove any :absent params and :ensure, then convert the names to ldap names.
+ attrs = ldap_convert(should)
+ create(name, attrs)
+ return
+ end
+
+ # We're modifying an existing entry. Yuck.
+
+ mods = []
+ # For each attribute we're deleting that is present, create a
+ # modify instance for deletion.
+ [is.keys, should.keys].flatten.uniq.each do |property|
+ # They're equal, so do nothing.
+ next if is[property] == should[property]
+
+ attributes = ldap_convert(should)
+
+ prop_name = ldap_name(property).to_s
+
+ # We're creating it.
+ if is[property] == :absent or is[property].nil?
+ mods << LDAP::Mod.new(LDAP::LDAP_MOD_ADD, prop_name, attributes[prop_name])
+ next
+ end
+
+ # We're deleting it
+ if should[property] == :absent or should[property].nil?
+ mods << LDAP::Mod.new(LDAP::LDAP_MOD_DELETE, prop_name, [])
+ next
+ end
+
+ # We're replacing an existing value
+ mods << LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE, prop_name, attributes[prop_name])
+ end
+
+ modify(name, mods)
+ end
+
+ # Is this a complete ldap configuration?
+ def valid?
+ location and objectclasses and ! objectclasses.empty? and puppet2ldap
+ end
+
+ private
+
+ # Convert a hash of attributes to ldap-like forms. This mostly means
+ # getting rid of :ensure and making sure everything's an array of strings.
+ def ldap_convert(attributes)
+ attributes.reject { |param, value| value == :absent or param == :ensure }.inject({}) do |result, ary|
+ value = (ary[1].is_a?(Array) ? ary[1] : [ary[1]]).collect { |v| v.to_s }
+ result[ldap_name(ary[0])] = value
+ result
+ end
+ end
+end
diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb
index 1b953c95e..eec86625d 100644
--- a/lib/puppet/util/settings.rb
+++ b/lib/puppet/util/settings.rb
@@ -123,7 +123,7 @@ class Puppet::Util::Settings
if pval = self.value(varname)
pval
else
- raise Puppet::DevError, "Could not find value for %s" % parent
+ raise Puppet::DevError, "Could not find value for %s" % value
end
end
diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb
index 9358a28e9..dc4e9cd71 100644
--- a/lib/puppet/util/storage.rb
+++ b/lib/puppet/util/storage.rb
@@ -6,6 +6,10 @@ class Puppet::Util::Storage
include Singleton
include Puppet::Util
+ def self.state
+ return @@state
+ end
+
def initialize
self.class.load
end
diff --git a/lib/puppet/util/variables.rb b/lib/puppet/util/variables.rb
deleted file mode 100644
index 1a78ef5c1..000000000
--- a/lib/puppet/util/variables.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-module Puppet::Util::Variables
- def inithooks
- @instance_init_hooks.dup
- end
-
- def initvars
- return unless defined? @class_init_hooks
- self.inithooks.each do |var, value|
- if value.is_a?(Class)
- instance_variable_set("@" + var.to_s, value.new)
- else
- instance_variable_set("@" + var.to_s, value)
- end
- end
- end
-
- def instancevar(hash)
- @instance_init_hooks ||= {}
-
- unless method_defined?(:initvars)
- define_method(:initvars) do
- self.class.inithooks.each do |var, value|
- if value.is_a?(Class)
- instance_variable_set("@" + var.to_s, value.new)
- else
- instance_variable_set("@" + var.to_s, value)
- end
- end
- end
- end
- hash.each do |var, value|
- raise("Already initializing %s" % var) if @instance_init_hooks[var]
-
- @instance_init_hooks[var] = value
- end
- end
-end
-
diff --git a/man/man8/filebucket.8 b/man/man8/filebucket.8
index fdaca9f31..0ef4c3842 100644
--- a/man/man8/filebucket.8
+++ b/man/man8/filebucket.8
@@ -112,5 +112,5 @@ Copyright (c) 2005 Reductive Labs, LLC Licensed under the GNU Public
License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/pi.8 b/man/man8/pi.8
index da13d8e52..ba685797a 100644
--- a/man/man8/pi.8
+++ b/man/man8/pi.8
@@ -30,5 +30,5 @@ Only list parameters without detail
Include metaparams
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppet.8 b/man/man8/puppet.8
index 0281d330e..8fc24aab2 100644
--- a/man/man8/puppet.8
+++ b/man/man8/puppet.8
@@ -73,5 +73,5 @@ Copyright (c) 2005 Reductive Labs, LLC Licensed under the GNU Public
License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppet.conf.8 b/man/man8/puppet.conf.8
index e8040fb8e..1bc1eb65a 100644
--- a/man/man8/puppet.conf.8
+++ b/man/man8/puppet.conf.8
@@ -4,7 +4,7 @@ Configuration Reference \-
.\" Man page generated from reStructeredText.
This page is autogenerated; any changes will get overwritten
-.I (last generated on Sat Mar 22 17:46:15 +1100 2008)
+.I (last generated on Mon May 05 09:33:01 +1000 2008)
@@ -691,7 +691,7 @@ puppetmasterd
.TP 2
\(bu
-Default: development
+Default: production
.SS environments
@@ -1739,9 +1739,9 @@ Default: $vardir/yaml
.ce 0
.sp
-.I This page autogenerated on Sat Mar 22 17:46:15 +1100 2008
+.I This page autogenerated on Mon May 05 09:33:01 +1000 2008
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppetca.8 b/man/man8/puppetca.8
index 42d6c1f1f..f9f89a95f 100644
--- a/man/man8/puppetca.8
+++ b/man/man8/puppetca.8
@@ -42,8 +42,8 @@ configuration options can also be generated by running puppetca with
.TP
-.B all: Operate on all outstanding requests. Only makes sense with
-\'\-\-sign\', or \'\-\-list\'.
+.B all: Operate on all items. Currently only makes sense with
+\'\-\-sign\', \'\-\-clean\', or \'\-\-list\'.
.TP
@@ -51,7 +51,9 @@ configuration options can also be generated by running puppetca with
This is useful when rebuilding hosts, since new certificate
signing requests will only be honored if puppetca does not
have a copy of a signed certificate for that host. The
-certificate of the host remains valid.
+certificate of the host remains valid. If \'\-\-all\' is specified
+then all host certificates, both signed and unsigned, will be
+removed.
debug: Enable full debugging.
@@ -112,5 +114,5 @@ Copyright (c) 2005 Reductive Labs, LLC Licensed under the GNU Public
License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppetd.8 b/man/man8/puppetd.8
index 83b172809..9cadcd4e3 100644
--- a/man/man8/puppetd.8
+++ b/man/man8/puppetd.8
@@ -180,5 +180,5 @@ Copyright (c) 2005, 2006 Reductive Labs, LLC Licensed under the GNU
Public License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppetdoc.8 b/man/man8/puppetdoc.8
index d7aacee75..8303b7ae0 100644
--- a/man/man8/puppetdoc.8
+++ b/man/man8/puppetdoc.8
@@ -58,5 +58,5 @@ Copyright (c) 2005\-2007 Reductive Labs, LLC Licensed under the GNU
Public License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppetmasterd.8 b/man/man8/puppetmasterd.8
index 5a8f02751..c14da72d3 100644
--- a/man/man8/puppetmasterd.8
+++ b/man/man8/puppetmasterd.8
@@ -83,5 +83,5 @@ Copyright (c) 2005 Reductive Labs, LLC Licensed under the GNU Public
License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/puppetrun.8 b/man/man8/puppetrun.8
index 753be1ca0..6b4048170 100644
--- a/man/man8/puppetrun.8
+++ b/man/man8/puppetrun.8
@@ -147,5 +147,5 @@ Copyright (c) 2005 Reductive Labs, LLC Licensed under the GNU Public
License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/man/man8/ralsh.8 b/man/man8/ralsh.8
index fc84fe0df..89c99c982 100644
--- a/man/man8/ralsh.8
+++ b/man/man8/ralsh.8
@@ -111,29 +111,16 @@ luke
.nf
$ ralsh user luke
-.fi
-
-.\" visit_block_quote
-
-.TP
-.B user { \'luke\':
-home => \'/home/luke\',
-uid => \'100\',
-ensure => \'present\',
-comment => \'Luke Kanies,,,\',
-gid => \'1000\',
-shell => \'/bin/bash\',
-groups => [\'sysadmin\',\'audio\',\'video\',\'puppet\']
-
-\.SH system-message
-System Message: WARNING/2 (./ralsh.rst:, line 87)
-Definition list ends without a blank line; unexpected unindent.
-
-
+user { \'luke\':
+ home => \'/home/luke\',
+ uid => \'100\',
+ ensure => \'present\',
+ comment => \'Luke Kanies,,,\',
+ gid => \'1000\',
+ shell => \'/bin/bash\',
+ groups => [\'sysadmin\',\'audio\',\'video\',\'puppet\']
}
-
-
-.\" depart_block_quote
+.fi
.SH AUTHOR
Luke Kanies
@@ -144,5 +131,5 @@ Copyright (c) 2005\-2007 Reductive Labs, LLC Licensed under the GNU
Public License
-.\" Generated by docutils manpage writer on 2008-03-22 17:46.
+.\" Generated by docutils manpage writer on 2008-05-05 09:33.
.\"
diff --git a/spec/integration/defaults.rb b/spec/integration/defaults.rb
index 185aa0026..b6f6bf109 100755
--- a/spec/integration/defaults.rb
+++ b/spec/integration/defaults.rb
@@ -5,8 +5,16 @@ require File.dirname(__FILE__) + '/../spec_helper'
require 'puppet/defaults'
describe "Puppet defaults" do
+ after { Puppet.settings.clear }
describe "when configuring the :crl" do
- after { Puppet.settings.clear }
+ it "should add the :factpath to Facter's search paths" do
+ Facter.expects(:search).with("/my/fact/path")
+
+ Puppet.settings[:factpath] = "/my/fact/path"
+ end
+ end
+
+ describe "when setting the :factpath" do
it "should warn if :cacrl is set to false" do
Puppet.expects(:warning)
diff --git a/spec/integration/node/catalog.rb b/spec/integration/node/catalog.rb
index b0e651511..285b85869 100755
--- a/spec/integration/node/catalog.rb
+++ b/spec/integration/node/catalog.rb
@@ -8,6 +8,12 @@ require File.dirname(__FILE__) + '/../../spec_helper'
describe Puppet::Node::Catalog do
describe "when using the indirector" do
after { Puppet::Util::Cacher.invalidate }
+ before do
+ # This is so the tests work w/out networking.
+ Facter.stubs(:to_hash).returns({"hostname" => "foo.domain.com"})
+ Facter.stubs(:value).returns("eh")
+ end
+
it "should be able to delegate to the :yaml terminus" do
Puppet::Node::Catalog.indirection.stubs(:terminus_class).returns :yaml
diff --git a/spec/integration/ral/types/package.rb b/spec/integration/type/package.rb
index 20567629d..c244fa1cd 100755
--- a/spec/integration/ral/types/package.rb
+++ b/spec/integration/type/package.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/type/package'
diff --git a/spec/unit/indirector/node/ldap.rb b/spec/unit/indirector/node/ldap.rb
index a40698662..878039c7c 100755
--- a/spec/unit/indirector/node/ldap.rb
+++ b/spec/unit/indirector/node/ldap.rb
@@ -17,6 +17,7 @@ describe Puppet::Node::Ldap do
@searcher.stubs(:connection).returns(@connection)
@searcher.stubs(:class_attributes).returns([])
@searcher.stubs(:parent_attribute).returns(nil)
+ @searcher.stubs(:stacked_attributes).returns([])
@searcher.stubs(:search_base).returns(:yay)
@searcher.stubs(:search_filter).returns(:filter)
@@ -195,6 +196,96 @@ describe Puppet::Node::Ldap do
proc { @searcher.find(@request) }.should raise_error(ArgumentError)
end
end
+
+ describe "and a puppet variable is specified" do
+ before do
+ @searcher.stubs(:stacked_attributes).returns(['puppetvar'])
+ end
+
+ it "should add the variable to the node parameters" do
+ @entry.stubs(:vals).with("puppetvar").returns(%w{one=two})
+ @entry.stubs(:to_hash).returns({})
+ @node.expects(:parameters=).with("one" => "two")
+ @searcher.find(@request)
+ end
+
+ it "should not overwrite node parameters specified as ldap object attribute" do
+ @entry.stubs(:vals).with("puppetvar").returns(%w{one=two})
+ @entry.stubs(:to_hash).returns("one" => "three")
+ @node.expects(:parameters=).with("one" => "three")
+ @searcher.find(@request)
+ end
+
+ it "should set entries without an equal sign to nil" do
+ @entry.stubs(:vals).with("puppetvar").returns(%w{one})
+ @entry.stubs(:to_hash).returns({})
+ @node.expects(:parameters=).with("one" => nil)
+ @searcher.find(@request)
+ end
+
+ it "should ignore empty entries" do
+ @entry.stubs(:vals).with("puppetvar").returns(%w{})
+ @entry.stubs(:to_hash).returns({})
+ @searcher.find(@request)
+ end
+ end
+ describe "and a puppet variable as well as a parent node are specified" do
+ before do
+ @parent = mock 'parent'
+
+ @searcher.meta_def(:search_filter) do |name|
+ return name
+ end
+ @connection.stubs(:search).with { |*args| args[2] == @name }.yields(@entry)
+ @connection.stubs(:search).with { |*args| args[2] == 'parent' }.yields(@parent)
+
+ @searcher.stubs(:stacked_attributes).returns(['puppetvar'])
+ @searcher.stubs(:parent_attribute).returns(:parent)
+ end
+
+ it "should add parent node variables to the child node parameters" do
+ @parent.stubs(:to_hash).returns({})
+ @parent.stubs(:vals).with("puppetvar").returns(%w{one=two})
+ @parent.stubs(:vals).with(:parent).returns(nil)
+
+ @entry.stubs(:to_hash).returns({})
+ @entry.stubs(:vals).with("puppetvar").returns(%w{})
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @node.expects(:parameters=).with("one" => "two")
+
+ @searcher.find(@request)
+ end
+
+ it "should overwrite parent node variables with child node parameters" do
+ @parent.stubs(:to_hash).returns({})
+ @parent.stubs(:vals).with("puppetvar").returns(%w{one=two})
+ @parent.stubs(:vals).with(:parent).returns(nil)
+
+ @entry.stubs(:to_hash).returns({})
+ @entry.stubs(:vals).with("puppetvar").returns(%w{one=three})
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @node.expects(:parameters=).with("one" => "three")
+
+ @searcher.find(@request)
+ end
+
+ it "should not overwrite parent node parameters specified as ldap object attribute" do
+ @parent.stubs(:to_hash).returns("one" => "three")
+ @parent.stubs(:vals).with("puppetvar").returns(%w{})
+ @parent.stubs(:vals).with(:parent).returns(nil)
+
+ @entry.stubs(:vals).with("puppetvar").returns(%w{one=two})
+ @entry.stubs(:to_hash).returns({})
+ @entry.stubs(:vals).with(:parent).returns(%w{parent})
+
+ @node.expects(:parameters=).with("one" => "three")
+
+ @searcher.find(@request)
+ end
+
+ end
end
end
diff --git a/spec/unit/parser/templatewrapper.rb b/spec/unit/parser/templatewrapper.rb
new file mode 100755
index 000000000..40465f955
--- /dev/null
+++ b/spec/unit/parser/templatewrapper.rb
@@ -0,0 +1,57 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Parser::TemplateWrapper do
+ before(:each) do
+ compiler = stub('compiler', :environment => "foo")
+ parser = stub('parser', :watch_file => true)
+ @scope = stub('scope', :compiler => compiler, :parser => parser)
+ @file = "fake_template"
+ Puppet::Module.stubs(:find_template).returns("/tmp/fake_template")
+ FileTest.stubs(:exists?).returns("true")
+ @tw = Puppet::Parser::TemplateWrapper.new(@scope, @file)
+ end
+
+ it "should create a new object TemplateWrapper from a scope and a file" do
+ Puppet::Module.expects(:find_template).with("fake_template", "foo").returns("/tmp/fake_template")
+ FileTest.expects(:exists?).with("/tmp/fake_template").returns(true)
+ tw = Puppet::Parser::TemplateWrapper.new(@scope, @file)
+ tw.should be_a_kind_of(Puppet::Parser::TemplateWrapper)
+ end
+
+ it "should turn into a string like template[name]" do
+ @tw.to_s.should eql("template[/tmp/fake_template]")
+ end
+
+ it "should return the processed template contents with a call to result" do
+ template_mock = mock("template", :result => "woot!")
+ File.expects(:read).with("/tmp/fake_template").returns("template contents")
+ ERB.expects(:new).with("template contents", 0, "-").returns(template_mock)
+ @tw.result.should eql("woot!")
+ end
+
+ it "should return the contents of a variable if called via method_missing" do
+ @scope.expects(:lookupvar).with("chicken", false).returns("is good")
+ tw = Puppet::Parser::TemplateWrapper.new(@scope, @file)
+ tw.chicken.should eql("is good")
+ end
+
+ it "should throw an exception if a variable is called via method_missing and it does not exist" do
+ @scope.expects(:lookupvar).with("chicken", false).returns(:undefined)
+ tw = Puppet::Parser::TemplateWrapper.new(@scope, @file)
+ lambda { tw.chicken }.should raise_error(Puppet::ParseError)
+ end
+
+ it "should allow you to check whether a variable is defined with has_variable?" do
+ @scope.expects(:lookupvar).with("chicken", false).returns("is good")
+ tw = Puppet::Parser::TemplateWrapper.new(@scope, @file)
+ tw.has_variable?("chicken").should eql(true)
+ end
+
+ it "should allow you to check whether a variable is not defined with has_variable?" do
+ @scope.expects(:lookupvar).with("chicken", false).returns(:undefined)
+ tw = Puppet::Parser::TemplateWrapper.new(@scope, @file)
+ tw.has_variable?("chicken").should eql(false)
+ end
+end
diff --git a/spec/unit/provider/confine.rb b/spec/unit/provider/confine.rb
new file mode 100755
index 000000000..6a9214e26
--- /dev/null
+++ b/spec/unit/provider/confine.rb
@@ -0,0 +1,67 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/provider/confine'
+
+describe Puppet::Provider::Confine do
+ it "should require a value" do
+ lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError)
+ end
+
+ it "should always convert values to an array" do
+ Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array)
+ end
+
+ it "should have a 'true' test" do
+ Puppet::Provider::Confine.test(:true).should be_instance_of(Class)
+ end
+
+ it "should have a 'false' test" do
+ Puppet::Provider::Confine.test(:false).should be_instance_of(Class)
+ end
+
+ it "should have a 'feature' test" do
+ Puppet::Provider::Confine.test(:feature).should be_instance_of(Class)
+ end
+
+ it "should have an 'exists' test" do
+ Puppet::Provider::Confine.test(:exists).should be_instance_of(Class)
+ end
+
+ it "should have a 'facter' test" do
+ Puppet::Provider::Confine.test(:facter).should be_instance_of(Class)
+ end
+
+ describe "when testing all values" do
+ before { @confine = Puppet::Provider::Confine.new(%w{a b c}) }
+
+ it "should be invalid if any values fail" do
+ @confine.stubs(:pass?).returns true
+ @confine.expects(:pass?).with("b").returns false
+ @confine.should_not be_valid
+ end
+
+ it "should be valid if all values pass" do
+ @confine.stubs(:pass?).returns true
+ @confine.should be_valid
+ end
+
+ it "should short-cut at the first failing value" do
+ @confine.expects(:pass?).once.returns false
+ @confine.valid?
+ end
+ end
+
+ describe "when testing the result of the values" do
+ before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) }
+
+ it "should return an array with the result of the test for each value" do
+ @confine.stubs(:pass?).returns true
+ @confine.expects(:pass?).with("b").returns false
+ @confine.expects(:pass?).with("d").returns false
+
+ @confine.result.should == [true, false, true, false]
+ end
+ end
+end
diff --git a/spec/unit/provider/confine/exists.rb b/spec/unit/provider/confine/exists.rb
new file mode 100755
index 000000000..1ab1d39f7
--- /dev/null
+++ b/spec/unit/provider/confine/exists.rb
@@ -0,0 +1,80 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/confine/exists'
+
+describe Puppet::Provider::Confine::Exists do
+ before do
+ @confine = Puppet::Provider::Confine::Exists.new("/my/file")
+ end
+
+ it "should be named :exists" do
+ Puppet::Provider::Confine::Exists.name.should == :exists
+ end
+
+ it "should use the 'pass?' method to test validity" do
+ @confine.expects(:pass?).with("/my/file")
+ @confine.valid?
+ end
+
+ it "should return false if the value is false" do
+ @confine.pass?(false).should be_false
+ end
+
+ it "should return false if the value does not point to a file" do
+ FileTest.expects(:exist?).with("/my/file").returns false
+ @confine.pass?("/my/file").should be_false
+ end
+
+ it "should return true if the value points to a file" do
+ FileTest.expects(:exist?).with("/my/file").returns true
+ @confine.pass?("/my/file").should be_true
+ end
+
+ it "should produce a message saying that a file is missing" do
+ @confine.message("/my/file").should be_include("does not exist")
+ end
+
+ describe "and the confine is for binaries" do
+ before { @confine.stubs(:for_binary).returns true }
+ it "should use its 'binary' method to look up the full path of the file" do
+ @confine.expects(:binary).returns nil
+ @confine.pass?("/my/file")
+ end
+
+ it "should return false if no binary can be found" do
+ @confine.expects(:binary).with("/my/file").returns nil
+ @confine.pass?("/my/file").should be_false
+ end
+
+ it "should return true if the binary can be found and the file exists" do
+ @confine.expects(:binary).with("/my/file").returns "/my/file"
+ FileTest.expects(:exist?).with("/my/file").returns true
+ @confine.pass?("/my/file").should be_true
+ end
+
+ it "should return false if the binary can be found but the file does not exist" do
+ @confine.expects(:binary).with("/my/file").returns "/my/file"
+ FileTest.expects(:exist?).with("/my/file").returns true
+ @confine.pass?("/my/file").should be_true
+ end
+ end
+
+ it "should produce a summary containing all missing files" do
+ FileTest.stubs(:exist?).returns true
+ FileTest.expects(:exist?).with("/two").returns false
+ FileTest.expects(:exist?).with("/four").returns false
+
+ confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four}
+ confine.summary.should == %w{/two /four}
+ end
+
+ it "should summarize multiple instances by returning a flattened array of their summaries" do
+ c1 = mock '1', :summary => %w{one}
+ c2 = mock '2', :summary => %w{two}
+ c3 = mock '3', :summary => %w{three}
+
+ Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three}
+ end
+end
diff --git a/spec/unit/provider/confine/facter.rb b/spec/unit/provider/confine/facter.rb
new file mode 100755
index 000000000..560263257
--- /dev/null
+++ b/spec/unit/provider/confine/facter.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/confine/facter'
+
+describe Puppet::Provider::Confine::Facter::Facter do
+ it "should be named :facter" do
+ Puppet::Provider::Confine::Facter.name.should == :facter
+ end
+
+ it "should require a value" do
+ lambda { Puppet::Provider::Confine::Facter.new() }.should raise_error(ArgumentError)
+ end
+
+ it "should always convert values to an array" do
+ Puppet::Provider::Confine::Facter.new("/some/file").values.should be_instance_of(Array)
+ end
+
+ it "should have an accessor for its fact" do
+ Puppet::Provider::Confine::Facter.new(:bar).should respond_to(:fact)
+ end
+
+ describe "when testing values" do
+ before { @confine = Puppet::Provider::Confine::Facter.new("foo") }
+ it "should use the 'pass?' method to test validity" do
+ @confine.expects(:pass?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return true if the value matches the facter value" do
+ Facter.expects(:value).returns("foo")
+
+ @confine.pass?("foo").should be_true
+ end
+
+ it "should return false if the value does not match the facter value" do
+ Facter.expects(:value).returns("boo")
+
+ @confine.pass?("foo").should be_false
+ end
+
+ it "should be case insensitive" do
+ Facter.expects(:value).returns("FOO")
+
+ @confine.pass?("foo").should be_true
+ end
+
+ it "should not care whether the value is a string or symbol" do
+ Facter.expects(:value).returns("FOO")
+
+ @confine.pass?(:foo).should be_true
+ end
+
+ it "should cache the fact during testing" do
+ Facter.expects(:value).once.returns("FOO")
+
+ @confine.pass?(:foo)
+ @confine.pass?(:foo)
+ end
+
+ it "should produce a message that the fact value is not correct" do
+ @confine = Puppet::Provider::Confine::Facter.new(%w{bar bee})
+ message = @confine.message("value")
+ message.should be_include("facter")
+ message.should be_include("bar,bee")
+ end
+ end
+
+ describe "when summarizing multiple instances" do
+ it "should return a hash of failing variables and their values" do
+ c1 = stub '1', :valid? => false, :values => %w{one}, :fact => "uno"
+ c2 = stub '2', :valid? => true, :values => %w{two}, :fact => "dos"
+ c3 = stub '3', :valid? => false, :values => %w{three}, :fact => "tres"
+
+ Puppet::Provider::Confine::Facter.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}}
+ end
+
+ it "should combine the values of multiple confines with the same fact" do
+ c1 = stub '1', :valid? => false, :values => %w{one}, :fact => "uno"
+ c2 = stub '2', :valid? => false, :values => %w{two}, :fact => "uno"
+
+ Puppet::Provider::Confine::Facter.summarize([c1, c2]).should == {"uno" => %w{one two}}
+ end
+ end
+end
diff --git a/spec/unit/provider/confine/false.rb b/spec/unit/provider/confine/false.rb
new file mode 100755
index 000000000..c6c43e391
--- /dev/null
+++ b/spec/unit/provider/confine/false.rb
@@ -0,0 +1,52 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/confine/false'
+
+describe Puppet::Provider::Confine::False do
+ it "should be named :false" do
+ Puppet::Provider::Confine::False.name.should == :false
+ end
+
+ it "should require a value" do
+ lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError)
+ end
+
+ describe "when testing values" do
+ before { @confine = Puppet::Provider::Confine::False.new("foo") }
+
+ it "should use the 'pass?' method to test validity" do
+ @confine = Puppet::Provider::Confine::False.new("foo")
+ @confine.expects(:pass?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return true if the value is false" do
+ @confine.pass?(false).should be_true
+ end
+
+ it "should return false if the value is not false" do
+ @confine.pass?("else").should be_false
+ end
+
+ it "should produce a message that a value is true" do
+ @confine = Puppet::Provider::Confine::False.new("foo")
+ @confine.message("eh").should be_include("true")
+ end
+ end
+
+ it "should be able to produce a summary with the number of incorrectly true values" do
+ confine = Puppet::Provider::Confine::False.new %w{one two three four}
+ confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false)
+ confine.summary.should == 2
+ end
+
+ it "should summarize multiple instances by summing their summaries" do
+ c1 = mock '1', :summary => 1
+ c2 = mock '2', :summary => 2
+ c3 = mock '3', :summary => 3
+
+ Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6
+ end
+end
diff --git a/spec/unit/provider/confine/feature.rb b/spec/unit/provider/confine/feature.rb
new file mode 100755
index 000000000..1845c9a47
--- /dev/null
+++ b/spec/unit/provider/confine/feature.rb
@@ -0,0 +1,59 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/confine/feature'
+
+describe Puppet::Provider::Confine::Feature do
+ it "should be named :feature" do
+ Puppet::Provider::Confine::Feature.name.should == :feature
+ end
+
+ it "should require a value" do
+ lambda { Puppet::Provider::Confine::Feature.new() }.should raise_error(ArgumentError)
+ end
+
+ it "should always convert values to an array" do
+ Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array)
+ end
+
+ describe "when testing values" do
+ before do
+ @features = mock 'features'
+ Puppet.stubs(:features).returns @features
+ @confine = Puppet::Provider::Confine::Feature.new("myfeature")
+ end
+
+ it "should use the Puppet features instance to test validity" do
+ @features.expects(:myfeature?)
+ @confine.valid?
+ end
+
+ it "should return true if the feature is present" do
+ @features.expects(:myfeature?).returns true
+ @confine.pass?("myfeature").should be_true
+ end
+
+ it "should return false if the value is false" do
+ @features.expects(:myfeature?).returns false
+ @confine.pass?("myfeature").should be_false
+ end
+
+ it "should log that a feature is missing" do
+ @confine.message("myfeat").should be_include("missing")
+ end
+ end
+
+ it "should summarize multiple instances by returning a flattened array of all missing features" do
+ confines = []
+ confines << Puppet::Provider::Confine::Feature.new(%w{one two})
+ confines << Puppet::Provider::Confine::Feature.new(%w{two})
+ confines << Puppet::Provider::Confine::Feature.new(%w{three four})
+
+ features = mock 'feature'
+ features.stub_everything
+ Puppet.stubs(:features).returns features
+
+ Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort
+ end
+end
diff --git a/spec/unit/provider/confine/true.rb b/spec/unit/provider/confine/true.rb
new file mode 100755
index 000000000..c9cc83c9e
--- /dev/null
+++ b/spec/unit/provider/confine/true.rb
@@ -0,0 +1,50 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/confine/true'
+
+describe Puppet::Provider::Confine::True do
+ it "should be named :true" do
+ Puppet::Provider::Confine::True.name.should == :true
+ end
+
+ it "should require a value" do
+ lambda { Puppet::Provider::Confine::True.new() }.should raise_error(ArgumentError)
+ end
+
+ describe "when testing values" do
+ before { @confine = Puppet::Provider::Confine::True.new("foo") }
+
+ it "should use the 'pass?' method to test validity" do
+ @confine.expects(:pass?).with("foo")
+ @confine.valid?
+ end
+
+ it "should return true if the value is not false" do
+ @confine.pass?("else").should be_true
+ end
+
+ it "should return false if the value is false" do
+ @confine.pass?(nil).should be_false
+ end
+
+ it "should produce the message that a value is false" do
+ @confine.message("eh").should be_include("false")
+ end
+ end
+
+ it "should produce the number of false values when asked for a summary" do
+ @confine = Puppet::Provider::Confine::True.new %w{one two three four}
+ @confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false)
+ @confine.summary.should == 2
+ end
+
+ it "should summarize multiple instances by summing their summaries" do
+ c1 = mock '1', :summary => 1
+ c2 = mock '2', :summary => 2
+ c3 = mock '3', :summary => 3
+
+ Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6
+ end
+end
diff --git a/spec/unit/provider/confine_collection.rb b/spec/unit/provider/confine_collection.rb
new file mode 100755
index 000000000..da4b3fe72
--- /dev/null
+++ b/spec/unit/provider/confine_collection.rb
@@ -0,0 +1,122 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/provider/confine_collection'
+
+describe Puppet::Provider::ConfineCollection do
+ it "should be able to add confines" do
+ Puppet::Provider::ConfineCollection.new.should respond_to(:confine)
+ end
+
+ describe "when creating confine instances" do
+ it "should create an instance of the named test with the provided values" do
+ test_class = mock 'test_class'
+ test_class.expects(:new).with(%w{my values})
+ Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class
+
+ Puppet::Provider::ConfineCollection.new.confine :foo => %w{my values}
+ end
+
+ describe "and the test cannot be found" do
+ before do
+ @facter = mock 'facter_test'
+
+ Puppet::Provider::Confine.expects(:test).with(:foo).returns nil
+ Puppet::Provider::Confine.expects(:test).with(:facter).returns @facter
+ end
+
+ it "should create a Facter test with the provided values and set the fact to the test name" do
+ confine = mock 'confine'
+ confine.expects(:fact=).with(:foo)
+ @facter.expects(:new).with(%w{my values}).returns confine
+ Puppet::Provider::ConfineCollection.new.confine :foo => %w{my values}
+ end
+ end
+
+ describe "and the 'for_binary' option was provided" do
+ it "should mark the test as a binary confine" do
+ confine = mock 'confine'
+ confine.expects(:for_binary=).with true
+ Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine
+ Puppet::Provider::ConfineCollection.new.confine :exists => :bar, :for_binary => true
+ end
+ end
+ end
+
+ it "should be valid if no confines are present" do
+ Puppet::Provider::ConfineCollection.new.should be_valid
+ end
+
+ it "should be valid if all confines pass" do
+ c1 = mock 'c1', :valid? => true
+ c2 = mock 'c2', :valid? => true
+
+ Puppet::Provider::Confine.test(:true).expects(:new).returns(c1)
+ Puppet::Provider::Confine.test(:false).expects(:new).returns(c2)
+
+ confiner = Puppet::Provider::ConfineCollection.new
+ confiner.confine :true => :bar, :false => :bee
+
+ confiner.should be_valid
+ end
+
+ it "should not be valid if any confines fail" do
+ c1 = stub 'c1', :valid? => true
+ c2 = stub 'c2', :valid? => false
+
+ Puppet::Provider::Confine.test(:true).expects(:new).returns(c1)
+ Puppet::Provider::Confine.test(:false).expects(:new).returns(c2)
+
+ confiner = Puppet::Provider::ConfineCollection.new
+ confiner.confine :true => :bar, :false => :bee
+
+ confiner.should_not be_valid
+ end
+
+ describe "when providing a summary" do
+ before do
+ @confiner = Puppet::Provider::ConfineCollection.new
+ end
+
+ it "should return a hash" do
+ @confiner.summary.should be_instance_of(Hash)
+ end
+
+ it "should return an empty hash if the confiner is valid" do
+ @confiner.summary.should == {}
+ end
+
+ it "should add each test type's summary to the hash" do
+ @confiner.confine :true => :bar, :false => :bee
+ Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm
+ Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+
+ @confiner.summary.should == {:true => :tsumm, :false => :fsumm}
+ end
+
+ it "should not include tests that return 0" do
+ @confiner.confine :true => :bar, :false => :bee
+ Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0
+ Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+
+ @confiner.summary.should == {:false => :fsumm}
+ end
+
+ it "should not include tests that return empty arrays" do
+ @confiner.confine :true => :bar, :false => :bee
+ Puppet::Provider::Confine.test(:true).expects(:summarize).returns []
+ Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+
+ @confiner.summary.should == {:false => :fsumm}
+ end
+
+ it "should not include tests that return empty hashes" do
+ @confiner.confine :true => :bar, :false => :bee
+ Puppet::Provider::Confine.test(:true).expects(:summarize).returns({})
+ Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+
+ @confiner.summary.should == {:false => :fsumm}
+ end
+ end
+end
diff --git a/spec/unit/provider/confiner.rb b/spec/unit/provider/confiner.rb
new file mode 100755
index 000000000..078fc4420
--- /dev/null
+++ b/spec/unit/provider/confiner.rb
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/provider/confiner'
+
+describe Puppet::Provider::Confiner do
+ before do
+ @object = Object.new
+ @object.extend(Puppet::Provider::Confiner)
+ end
+
+ it "should have a method for defining confines" do
+ @object.should respond_to(:confine)
+ end
+
+ it "should have a method for returning its confine collection" do
+ @object.should respond_to(:confine_collection)
+ end
+
+ it "should have a method for testing suitability" do
+ @object.should respond_to(:suitable?)
+ end
+
+ it "should delegate its confine method to its confine collection" do
+ coll = mock 'collection'
+ @object.stubs(:confine_collection).returns coll
+ coll.expects(:confine).with(:foo => :bar, :bee => :baz)
+ @object.confine(:foo => :bar, :bee => :baz)
+ end
+
+ it "should create a new confine collection if one does not exist" do
+ Puppet::Provider::ConfineCollection.expects(:new).returns "mycoll"
+ @object.confine_collection.should == "mycoll"
+ end
+
+ it "should reuse the confine collection" do
+ @object.confine_collection.should equal(@object.confine_collection)
+ end
+
+ describe "when testing suitability" do
+ before do
+ @coll = mock 'collection'
+ @object.stubs(:confine_collection).returns @coll
+ end
+
+ it "should return true if the confine collection is valid" do
+ @coll.expects(:valid?).returns true
+ @object.should be_suitable
+ end
+
+ it "should return false if the confine collection is invalid" do
+ @coll.expects(:valid?).returns false
+ @object.should_not be_suitable
+ end
+
+ it "should return the summary of the confine collection if a long result is asked for" do
+ @coll.expects(:summary).returns "myresult"
+ @object.suitable?(false).should == "myresult"
+ end
+ end
+end
diff --git a/spec/unit/provider/group/ldap.rb b/spec/unit/provider/group/ldap.rb
new file mode 100755
index 000000000..3f12d74e3
--- /dev/null
+++ b/spec/unit/provider/group/ldap.rb
@@ -0,0 +1,66 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-10.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:group).provider(:ldap)
+
+describe provider_class do
+ it "should have the Ldap provider class as its baseclass" do
+ provider_class.superclass.should equal(Puppet::Provider::Ldap)
+ end
+
+ it "should manage :posixGroup objectclass" do
+ provider_class.manager.objectclasses.should == [:posixGroup]
+ end
+
+ it "should use 'ou=Groups' as its relative base" do
+ provider_class.manager.location.should == "ou=Groups"
+ end
+
+ it "should use :cn as its rdn" do
+ provider_class.manager.rdn.should == :cn
+ end
+
+ it "should map :name to 'cn'" do
+ provider_class.manager.ldap_name(:name).should == 'cn'
+ end
+
+ it "should map :gid to 'gidNumber'" do
+ provider_class.manager.ldap_name(:gid).should == 'gidNumber'
+ end
+
+ it "should map :members to 'memberUid', to be used by the user ldap provider" do
+ provider_class.manager.ldap_name(:members).should == 'memberUid'
+ end
+
+ describe "when being created" do
+ before do
+ # So we don't try to actually talk to ldap
+ @connection = mock 'connection'
+ provider_class.manager.stubs(:connect).yields @connection
+ end
+
+ describe "with no gid specified" do
+ it "should pick the first available GID after the largest existing GID" do
+ low = {:name=>["luke"], :gid=>["100"]}
+ high = {:name=>["testing"], :gid=>["140"]}
+ provider_class.manager.expects(:search).returns([low, high])
+
+ resource = stub 'resource', :should => %w{whatever}
+ resource.stubs(:should).with(:gid).returns nil
+ resource.stubs(:should).with(:ensure).returns :present
+ instance = provider_class.new(:name => "luke", :ensure => :absent)
+ instance.stubs(:resource).returns resource
+
+ @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["141"] }
+
+ instance.create
+ instance.flush
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/ral/provider/interface/redhat.rb b/spec/unit/provider/interface/redhat.rb
index 9bf1b9722..99ac50f01 100755
--- a/spec/unit/ral/provider/interface/redhat.rb
+++ b/spec/unit/provider/interface/redhat.rb
@@ -3,18 +3,18 @@
# Created by Luke Kanies on 2007-11-20.
# Copyright (c) 2006. All rights reserved.
-require File.dirname(__FILE__) + '/../../../../spec_helper'
+require File.dirname(__FILE__) + '/../../../spec_helper'
provider_class = Puppet::Type.type(:interface).provider(:redhat)
describe provider_class do
it "should not be functional on systems without a network-scripts directory" do
- FileTest.expects(:exists?).with("/etc/sysconfig/network-scripts").returns(false)
+ FileTest.expects(:exist?).with("/etc/sysconfig/network-scripts").returns(false)
provider_class.should_not be_suitable
end
it "should be functional on systems with a network-scripts directory" do
- FileTest.expects(:exists?).with("/etc/sysconfig/network-scripts").returns(true)
+ FileTest.expects(:exist?).with("/etc/sysconfig/network-scripts").returns(true)
provider_class.should be_suitable
end
end
diff --git a/spec/unit/ral/provider/interface/sunos.rb b/spec/unit/provider/interface/sunos.rb
index 7b9f462e6..6a7bd19c1 100755
--- a/spec/unit/ral/provider/interface/sunos.rb
+++ b/spec/unit/provider/interface/sunos.rb
@@ -3,7 +3,7 @@
# Created by Luke Kanies on 2007-11-25.
# Copyright (c) 2006. All rights reserved.
-require File.dirname(__FILE__) + '/../../../../spec_helper'
+require File.dirname(__FILE__) + '/../../../spec_helper'
require 'puppet/provider/interface/sunos'
diff --git a/spec/unit/provider/ldap.rb b/spec/unit/provider/ldap.rb
new file mode 100755
index 000000000..fd5d1bdc3
--- /dev/null
+++ b/spec/unit/provider/ldap.rb
@@ -0,0 +1,248 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-21.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/provider/ldap'
+
+describe Puppet::Provider::Ldap do
+ before do
+ @class = Class.new(Puppet::Provider::Ldap)
+ end
+
+ it "should be able to define its manager" do
+ manager = mock 'manager'
+ Puppet::Util::Ldap::Manager.expects(:new).returns manager
+ @class.stubs :mk_resource_methods
+ manager.expects(:manages).with(:one)
+ @class.manages(:one).should equal(manager)
+ @class.manager.should equal(manager)
+ end
+
+ it "should be able to prefetch instances from ldap" do
+ @class.should respond_to(:prefetch)
+ end
+
+ it "should create its resource getter/setter methods when the manager is defined" do
+ manager = mock 'manager'
+ Puppet::Util::Ldap::Manager.expects(:new).returns manager
+ @class.expects :mk_resource_methods
+ manager.stubs(:manages)
+ @class.manages(:one).should equal(manager)
+ end
+
+ it "should have an instances method" do
+ @class.should respond_to(:instances)
+ end
+
+ describe "when providing a list of instances" do
+ it "should convert all results returned from the manager's :search method into provider instances" do
+ manager = mock 'manager'
+ @class.stubs(:manager).returns manager
+
+ manager.expects(:search).returns %w{one two three}
+
+ @class.expects(:new).with("one").returns(1)
+ @class.expects(:new).with("two").returns(2)
+ @class.expects(:new).with("three").returns(3)
+
+ @class.instances.should == [1,2,3]
+ end
+ end
+
+ it "should have a prefetch method" do
+ @class.should respond_to(:prefetch)
+ end
+
+ describe "when prefetching" do
+ before do
+ @manager = mock 'manager'
+ @class.stubs(:manager).returns @manager
+
+ @resource = mock 'resource'
+
+ @resources = {"one" => @resource}
+ end
+
+ it "should find an entry for each passed resource" do
+ @manager.expects(:find).with("one").returns nil
+
+ @class.stubs(:new)
+ @resource.stubs(:provider=)
+ @class.prefetch(@resources)
+ end
+
+ describe "resources that do not exist" do
+ it "should create a provider with :ensure => :absent" do
+ result = mock 'result'
+ @manager.expects(:find).with("one").returns nil
+
+ @class.expects(:new).with(:ensure => :absent).returns "myprovider"
+
+ @resource.expects(:provider=).with("myprovider")
+
+ @class.prefetch(@resources)
+ end
+ end
+
+ describe "resources that exist" do
+ it "should create a provider with the results of the find" do
+ @manager.expects(:find).with("one").returns("one" => "two")
+
+ @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider"
+
+ @resource.expects(:provider=).with("myprovider")
+
+ @class.prefetch(@resources)
+ end
+
+ it "should set :ensure to :present in the returned values" do
+ @manager.expects(:find).with("one").returns("one" => "two")
+
+ @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider"
+
+ @resource.expects(:provider=).with("myprovider")
+
+ @class.prefetch(@resources)
+ end
+ end
+ end
+
+ describe "when being initialized" do
+ it "should fail if no manager has been defined" do
+ lambda { @class.new }.should raise_error(Puppet::DevError)
+ end
+
+ it "should fail if the manager is invalid" do
+ manager = stub "manager", :valid? => false
+ @class.stubs(:manager).returns manager
+ lambda { @class.new }.should raise_error(Puppet::DevError)
+ end
+
+ describe "with a hash" do
+ before do
+ @manager = stub "manager", :valid? => true
+ @class.stubs(:manager).returns @manager
+
+ @resource_class = mock 'resource_class'
+ @class.stubs(:resource_type).returns @resource_class
+
+ @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property
+ @resource_class.stubs(:attrclass).with(:one).returns(@property_class)
+ @resource_class.stubs(:validattr?).returns true
+ end
+
+ it "should store a copy of the hash as its ldap_properties" do
+ instance = @class.new(:one => :two)
+ instance.ldap_properties.should == {:one => :two}
+ end
+
+ it "should only store the first value of each value array for those attributes that do not match all values" do
+ @property_class.expects(:array_matching).returns :first
+ instance = @class.new(:one => %w{two three})
+ instance.properties.should == {:one => "two"}
+ end
+
+ it "should store the whole value array for those attributes that match all values" do
+ @property_class.expects(:array_matching).returns :all
+ instance = @class.new(:one => %w{two three})
+ instance.properties.should == {:one => %w{two three}}
+ end
+
+ it "should only use the first value for attributes that are not properties" do
+ # Yay. hackish, but easier than mocking everything.
+ @resource_class.expects(:attrclass).with(:a).returns Puppet::Type.type(:user).attrclass(:name)
+ @property_class.stubs(:array_matching).returns :all
+
+ instance = @class.new(:one => %w{two three}, :a => %w{b c})
+ instance.properties.should == {:one => %w{two three}, :a => "b"}
+ end
+
+ it "should discard any properties not valid in the resource class" do
+ @resource_class.expects(:validattr?).with(:a).returns false
+ @property_class.stubs(:array_matching).returns :all
+
+ instance = @class.new(:one => %w{two three}, :a => %w{b})
+ instance.properties.should == {:one => %w{two three}}
+ end
+ end
+ end
+
+ describe "when an instance" do
+ before do
+ @manager = stub "manager", :valid? => true
+ @class.stubs(:manager).returns @manager
+ @instance = @class.new
+
+ @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property
+ @resource_class = stub 'resource_class', :attrclass => @property_class, :validattr? => true, :validproperties => [:one, :two]
+ @class.stubs(:resource_type).returns @resource_class
+ end
+
+ it "should have a method for creating the ldap entry" do
+ @instance.should respond_to(:create)
+ end
+
+ it "should have a method for removing the ldap entry" do
+ @instance.should respond_to(:delete)
+ end
+
+ it "should have a method for returning the class's manager" do
+ @instance.manager.should equal(@manager)
+ end
+
+ it "should indicate when the ldap entry already exists" do
+ @instance = @class.new(:ensure => :present)
+ @instance.exists?.should be_true
+ end
+
+ it "should indicate when the ldap entry does not exist" do
+ @instance = @class.new(:ensure => :absent)
+ @instance.exists?.should be_false
+ end
+
+ describe "is being flushed" do
+ it "should call the manager's :update method with its name, current attributes, and desired attributes" do
+ @instance.stubs(:name).returns "myname"
+ @instance.stubs(:ldap_properties).returns(:one => :two)
+ @instance.stubs(:properties).returns(:three => :four)
+ @manager.expects(:update).with(@instance.name, {:one => :two}, {:three => :four})
+ @instance.flush
+ end
+ end
+
+ describe "is being created" do
+ before do
+ @rclass = mock 'resource_class'
+ @rclass.stubs(:validproperties).returns([:one, :two])
+ @resource = mock 'resource'
+ @resource.stubs(:class).returns @rclass
+ @resource.stubs(:should).returns nil
+ @instance.stubs(:resource).returns @resource
+ end
+
+ it "should set its :ensure value to :present" do
+ @instance.create
+ @instance.properties[:ensure].should == :present
+ end
+
+ it "should set all of the other attributes from the resource" do
+ @resource.expects(:should).with(:one).returns "oneval"
+ @resource.expects(:should).with(:two).returns "twoval"
+
+ @instance.create
+ @instance.properties[:one].should == "oneval"
+ @instance.properties[:two].should == "twoval"
+ end
+ end
+
+ describe "is being deleted" do
+ it "should set its :ensure value to :absent" do
+ @instance.delete
+ @instance.properties[:ensure].should == :absent
+ end
+ end
+ end
+end
diff --git a/spec/unit/ral/provider/mount.rb b/spec/unit/provider/mount.rb
index 0b90d53c9..41abcd424 100755
--- a/spec/unit/ral/provider/mount.rb
+++ b/spec/unit/provider/mount.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/provider/mount'
diff --git a/spec/unit/ral/provider/mount/parsed.rb b/spec/unit/provider/mount/parsed.rb
index b7f08a464..8d043f97f 100755
--- a/spec/unit/ral/provider/mount/parsed.rb
+++ b/spec/unit/provider/mount/parsed.rb
@@ -3,7 +3,7 @@
# Created by Luke Kanies on 2007-9-12.
# Copyright (c) 2006. All rights reserved.
-require File.dirname(__FILE__) + '/../../../../spec_helper'
+require File.dirname(__FILE__) + '/../../../spec_helper'
require 'puppettest/support/utils'
require 'puppettest/fileparsing'
diff --git a/spec/unit/provider/ssh_authorized_key/parsed.rb b/spec/unit/provider/ssh_authorized_key/parsed.rb
new file mode 100755
index 000000000..c35ddc513
--- /dev/null
+++ b/spec/unit/provider/ssh_authorized_key/parsed.rb
@@ -0,0 +1,74 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppettest'
+require 'puppettest/support/utils'
+require 'puppettest/fileparsing'
+
+provider_class = Puppet::Type.type(:ssh_authorized_key).provider(:parsed)
+
+describe provider_class do
+ include PuppetTest
+ include PuppetTest::FileParsing
+
+ before :each do
+ @sshauthkey_class = Puppet.type(:ssh_authorized_key)
+ @provider = @sshauthkey_class.provider(:parsed)
+ end
+
+ after :each do
+ @provider.initvars
+ end
+
+ def mkkey(args)
+ fakeresource = fakeresource(:ssh_authorized_key, args[:name])
+
+ key = @provider.new(fakeresource)
+ args.each do |p,v|
+ key.send(p.to_s + "=", v)
+ end
+
+ return key
+ end
+
+ def genkey(key)
+ @provider.filetype = :ram
+ file = @provider.default_target
+
+ key.flush
+ text = @provider.target_object(file).read
+ return text
+ end
+
+ it "should be able to parse each example" do
+ fakedata("data/providers/ssh_authorized_key/parsed").each { |file|
+ puts "Parsing %s" % file
+ fakedataparse(file)
+ }
+ end
+
+ it "should be able to generate a basic authorized_keys file" do
+ key = mkkey({
+ :name => "Just Testing",
+ :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj",
+ :type => "ssh-dss",
+ :ensure => :present,
+ :options => [:absent]
+ })
+
+ genkey(key).should == "ssh-dss AAAAfsfddsjldjgksdflgkjsfdlgkj Just Testing\n"
+ end
+
+ it "should be able to generate a authorized_keys file with options" do
+ key = mkkey({
+ :name => "root@localhost",
+ :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj",
+ :type => "ssh-rsa",
+ :ensure => :present,
+ :options => ['from="192.168.1.1"', "no-pty", "no-X11-forwarding"]
+ })
+
+ genkey(key).should == "from=\"192.168.1.1\",no-pty,no-X11-forwarding ssh-rsa AAAAfsfddsjldjgksdflgkjsfdlgkj root@localhost\n"
+ end
+end
diff --git a/spec/unit/provider/user/ldap.rb b/spec/unit/provider/user/ldap.rb
new file mode 100755
index 000000000..c4731cbbb
--- /dev/null
+++ b/spec/unit/provider/user/ldap.rb
@@ -0,0 +1,252 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-10.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+provider_class = Puppet::Type.type(:user).provider(:ldap)
+
+describe provider_class do
+ it "should have the Ldap provider class as its baseclass" do
+ provider_class.superclass.should equal(Puppet::Provider::Ldap)
+ end
+
+ it "should manage :posixAccount and :person objectclasses" do
+ provider_class.manager.objectclasses.should == [:posixAccount, :person]
+ end
+
+ it "should use 'ou=People' as its relative base" do
+ provider_class.manager.location.should == "ou=People"
+ end
+
+ it "should use :uid as its rdn" do
+ provider_class.manager.rdn.should == :uid
+ end
+
+ {:name => "uid",
+ :password => "userPassword",
+ :comment => "cn",
+ :uid => "uidNumber",
+ :gid => "gidNumber",
+ :home => "homeDirectory",
+ :shell => "loginShell"
+ }.each do |puppet, ldap|
+ it "should map :#{puppet.to_s} to '#{ldap}'" do
+ provider_class.manager.ldap_name(puppet).should == ldap
+ end
+ end
+
+ describe "when being created" do
+ before do
+ # So we don't try to actually talk to ldap
+ @connection = mock 'connection'
+ provider_class.manager.stubs(:connect).yields @connection
+ end
+
+ it "should generate the sn as the last field of the cn" do
+ resource = stub 'resource', :should => %w{whatever}
+ resource.stubs(:should).with(:comment).returns ["Luke Kanies"]
+ resource.stubs(:should).with(:ensure).returns :present
+ instance = provider_class.new(:name => "luke", :ensure => :absent)
+ instance.stubs(:resource).returns resource
+
+ @connection.expects(:add).with { |dn, attrs| attrs["sn"] == ["Kanies"] }
+
+ instance.create
+ instance.flush
+ end
+
+ describe "with no uid specified" do
+ it "should pick the first available UID after the largest existing UID" do
+ low = {:name=>["luke"], :shell=>:absent, :uid=>["100"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["l k"]}
+ high = {:name=>["testing"], :shell=>:absent, :uid=>["140"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["t u"]}
+ provider_class.manager.expects(:search).returns([low, high])
+
+ resource = stub 'resource', :should => %w{whatever}
+ resource.stubs(:should).with(:uid).returns nil
+ resource.stubs(:should).with(:ensure).returns :present
+ instance = provider_class.new(:name => "luke", :ensure => :absent)
+ instance.stubs(:resource).returns resource
+
+ @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["141"] }
+
+ instance.create
+ instance.flush
+ end
+ end
+ end
+
+ describe "when flushing" do
+ before do
+ provider_class.stubs(:suitable?).returns true
+
+ @instance = provider_class.new(:name => "myname", :groups => %w{whatever}, :uid => "400")
+ end
+
+ it "should remove the :groups value before updating" do
+ @instance.class.manager.expects(:update).with { |name, ldap, puppet| puppet[:groups].nil? }
+
+ @instance.flush
+ end
+
+ it "should empty the property hash" do
+ @instance.class.manager.stubs(:update)
+
+ @instance.flush
+
+ @instance.uid.should == :absent
+ end
+
+ it "should empty the ldap property hash" do
+ @instance.class.manager.stubs(:update)
+
+ @instance.flush
+
+ @instance.ldap_properties[:uid].should be_nil
+ end
+ end
+
+ describe "when checking group membership" do
+ before do
+ @groups = Puppet::Type.type(:group).provider(:ldap)
+ @group_manager = @groups.manager
+ provider_class.stubs(:suitable?).returns true
+
+ @instance = provider_class.new(:name => "myname")
+ end
+
+ it "should show its group membership as the list of all groups returned by an ldap query of group memberships" do
+ one = {:name => "one"}
+ two = {:name => "two"}
+ @group_manager.expects(:search).with("memberUid=myname").returns([one, two])
+
+ @instance.groups.should == "one,two"
+ end
+
+ it "should show its group membership as :absent if no matching groups are found in ldap" do
+ @group_manager.expects(:search).with("memberUid=myname").returns(nil)
+
+ @instance.groups.should == :absent
+ end
+
+ it "should cache the group value" do
+ @group_manager.expects(:search).with("memberUid=myname").once.returns nil
+
+ @instance.groups
+ @instance.groups.should == :absent
+ end
+ end
+
+ describe "when modifying group membership" do
+ before do
+ @groups = Puppet::Type.type(:group).provider(:ldap)
+ @group_manager = @groups.manager
+ provider_class.stubs(:suitable?).returns true
+
+ @one = {:name => "one", :gid => "500"}
+ @group_manager.stubs(:find).with("one").returns(@one)
+
+ @two = {:name => "one", :gid => "600"}
+ @group_manager.stubs(:find).with("two").returns(@two)
+
+ @instance = provider_class.new(:name => "myname")
+
+ @instance.stubs(:groups).returns :absent
+ end
+
+ it "should fail if the group does not exist" do
+ @group_manager.expects(:find).with("mygroup").returns nil
+
+ lambda { @instance.groups = "mygroup" }.should raise_error(Puppet::Error)
+ end
+
+ it "should only pass the attributes it cares about to the group manager" do
+ @group_manager.expects(:update).with { |name, attrs| attrs[:gid].nil? }
+
+ @instance.groups = "one"
+ end
+
+ it "should always include :ensure => :present in the current values" do
+ @group_manager.expects(:update).with { |name, is, should| is[:ensure] == :present }
+
+ @instance.groups = "one"
+ end
+
+ it "should always include :ensure => :present in the desired values" do
+ @group_manager.expects(:update).with { |name, is, should| should[:ensure] == :present }
+
+ @instance.groups = "one"
+ end
+
+ it "should always pass the group's original member list" do
+ @one[:members] = %w{yay ness}
+ @group_manager.expects(:update).with { |name, is, should| is[:members] == %w{yay ness} }
+
+ @instance.groups = "one"
+ end
+
+ it "should find the group again when resetting its member list, so it has the full member list" do
+ @group_manager.expects(:find).with("one").returns(@one)
+
+ @group_manager.stubs(:update)
+
+ @instance.groups = "one"
+ end
+
+ describe "for groups that have no members" do
+ it "should create a new members attribute with its value being the user's name" do
+ @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{myname} }
+
+ @instance.groups = "one"
+ end
+ end
+
+ describe "for groups it is being removed from" do
+ it "should replace the group's member list with one missing the user's name" do
+ @one[:members] = %w{myname a}
+ @two[:members] = %w{myname b}
+
+ @group_manager.expects(:update).with { |name, is, should| name == "two" and should[:members] == %w{b} }
+
+ @instance.stubs(:groups).returns "one,two"
+ @instance.groups = "one"
+ end
+
+ it "should mark the member list as empty if there are no remaining members" do
+ @one[:members] = %w{myname}
+ @two[:members] = %w{myname b}
+
+ @group_manager.expects(:update).with { |name, is, should| name == "one" and should[:members] == :absent }
+
+ @instance.stubs(:groups).returns "one,two"
+ @instance.groups = "two"
+ end
+ end
+
+ describe "for groups that already have members" do
+ it "should replace each group's member list with a new list including the user's name" do
+ @one[:members] = %w{a b}
+ @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} }
+ @two[:members] = %w{b c}
+ @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{b c myname} }
+
+ @instance.groups = "one,two"
+ end
+ end
+
+ describe "for groups of which it is a member" do
+ it "should do nothing" do
+ @one[:members] = %w{a b}
+ @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} }
+
+ @two[:members] = %w{c myname}
+ @group_manager.expects(:update).with { |name, *other| name == "two" }.never
+
+ @instance.stubs(:groups).returns "two"
+
+ @instance.groups = "one,two"
+ end
+ end
+ end
+end
diff --git a/spec/unit/rails.rb b/spec/unit/rails.rb
index ae7ea9f95..4a4667543 100755
--- a/spec/unit/rails.rb
+++ b/spec/unit/rails.rb
@@ -9,8 +9,9 @@ describe Puppet::Rails, "when initializing any connection" do
before do
@logger = stub 'logger', :level= => nil
@logger.stub_everything
+ Logger.stubs(:new).returns(@logger)
- Logger.stubs(:new).returns @logger
+ ActiveRecord::Base.stubs(:logger).returns(@logger)
end
it "should use settings" do
diff --git a/spec/unit/ral/type.rb b/spec/unit/type.rb
index 5980167d6..9815ed32d 100755
--- a/spec/unit/ral/type.rb
+++ b/spec/unit/type.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.dirname(__FILE__) + '/../spec_helper'
describe Puppet::Type, " when in a configuration" do
before do
diff --git a/spec/unit/ral/type/exec.rb b/spec/unit/type/exec.rb
index 260804227..cf0e02929 100755
--- a/spec/unit/ral/type/exec.rb
+++ b/spec/unit/type/exec.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/type/exec'
diff --git a/spec/unit/ral/type/file.rb b/spec/unit/type/file.rb
index 83546bef0..d6add8609 100755
--- a/spec/unit/ral/type/file.rb
+++ b/spec/unit/type/file.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
describe Puppet::Type.type(:file) do
before do
diff --git a/spec/unit/ral/type/interface.rb b/spec/unit/type/interface.rb
index e51465a0c..4e27e35ea 100755
--- a/spec/unit/ral/type/interface.rb
+++ b/spec/unit/type/interface.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
interface = Puppet::Type.type(:interface)
diff --git a/spec/unit/ral/type/mount.rb b/spec/unit/type/mount.rb
index b1a01749d..9d09225cc 100755
--- a/spec/unit/ral/type/mount.rb
+++ b/spec/unit/type/mount.rb
@@ -1,69 +1,67 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
-require 'puppet/type/mount'
-
-describe Puppet::Type::Mount do
+describe Puppet::Type.type(:mount) do
it "should have a :refreshable feature that requires the :remount method" do
- Puppet::Type::Mount.provider_feature(:refreshable).methods.should == [:remount]
+ Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount]
end
it "should have no default value for :ensure" do
- mount = Puppet::Type::Mount.create(:name => "yay")
+ mount = Puppet::Type.type(:mount).create(:name => "yay")
mount.should(:ensure).should be_nil
end
end
-describe Puppet::Type::Mount, "when validating attributes" do
+describe Puppet::Type.type(:mount), "when validating attributes" do
[:name, :remounts].each do |param|
it "should have a #{param} parameter" do
- Puppet::Type::Mount.attrtype(param).should == :param
+ Puppet::Type.type(:mount).attrtype(param).should == :param
end
end
[:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param|
it "should have a #{param} property" do
- Puppet::Type::Mount.attrtype(param).should == :property
+ Puppet::Type.type(:mount).attrtype(param).should == :property
end
end
end
-describe Puppet::Type::Mount::Ensure, "when validating values" do
+describe Puppet::Type.type(:mount)::Ensure, "when validating values" do
before do
- @provider = stub 'provider', :class => Puppet::Type::Mount.defaultprovider, :clear => nil
- Puppet::Type::Mount.defaultprovider.expects(:new).returns(@provider)
+ @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil
+ Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider)
end
it "should support :present as a value to :ensure" do
- Puppet::Type::Mount.create(:name => "yay", :ensure => :present)
+ Puppet::Type.type(:mount).create(:name => "yay", :ensure => :present)
end
it "should alias :unmounted to :present as a value to :ensure" do
- mount = Puppet::Type::Mount.create(:name => "yay", :ensure => :unmounted)
+ mount = Puppet::Type.type(:mount).create(:name => "yay", :ensure => :unmounted)
mount.should(:ensure).should == :present
end
it "should support :absent as a value to :ensure" do
- Puppet::Type::Mount.create(:name => "yay", :ensure => :absent)
+ Puppet::Type.type(:mount).create(:name => "yay", :ensure => :absent)
end
it "should support :mounted as a value to :ensure" do
- Puppet::Type::Mount.create(:name => "yay", :ensure => :mounted)
+ Puppet::Type.type(:mount).create(:name => "yay", :ensure => :mounted)
end
end
-describe Puppet::Type::Mount::Ensure do
+describe Puppet::Type.type(:mount)::Ensure do
before :each do
- @provider = stub 'provider', :class => Puppet::Type::Mount.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock
- Puppet::Type::Mount.defaultprovider.stubs(:new).returns(@provider)
- @mount = Puppet::Type::Mount.create(:name => "yay", :check => :ensure)
+ @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock
+ Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider)
+ @mount = Puppet::Type.type(:mount).create(:name => "yay", :check => :ensure)
@ensure = @mount.property(:ensure)
end
def mount_stub(params)
- Puppet::Type::Mount.validproperties.each do |prop|
+ Puppet::Type.type(:mount).validproperties.each do |prop|
unless params[prop]
params[prop] = :absent
@mount[prop] = :absent
@@ -75,7 +73,7 @@ describe Puppet::Type::Mount::Ensure do
end
end
- describe Puppet::Type::Mount::Ensure, "when retrieving its current state" do
+ describe Puppet::Type.type(:mount)::Ensure, "when retrieving its current state" do
it "should return the provider's value if it is :absent" do
@provider.expects(:ensure).returns(:absent)
@@ -95,7 +93,7 @@ describe Puppet::Type::Mount::Ensure do
end
end
- describe Puppet::Type::Mount::Ensure, "when changing the host" do
+ describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do
it "should destroy itself if it should be absent" do
@provider.stubs(:mounted?).returns(false)
@@ -158,7 +156,7 @@ describe Puppet::Type::Mount::Ensure do
end
end
- describe Puppet::Type::Mount, "when responding to events" do
+ describe Puppet::Type.type(:mount), "when responding to events" do
it "should remount if it is currently mounted" do
@provider.expects(:mounted?).returns(true)
diff --git a/spec/unit/ral/type/nagios.rb b/spec/unit/type/nagios.rb
index 35f00b0e5..563c82c2f 100755
--- a/spec/unit/ral/type/nagios.rb
+++ b/spec/unit/type/nagios.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/external/nagios'
diff --git a/spec/unit/ral/type/noop_metaparam.rb b/spec/unit/type/noop_metaparam.rb
index 236599ed5..540603ef9 100755
--- a/spec/unit/ral/type/noop_metaparam.rb
+++ b/spec/unit/type/noop_metaparam.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/metatype/metaparams'
diff --git a/spec/unit/ral/type/package.rb b/spec/unit/type/package.rb
index 8cc11cc2c..d2fc85ed1 100755
--- a/spec/unit/ral/type/package.rb
+++ b/spec/unit/type/package.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/type/package'
diff --git a/spec/unit/ral/type/schedule.rb b/spec/unit/type/schedule.rb
index e53138c5e..b533d17e4 100755
--- a/spec/unit/ral/type/schedule.rb
+++ b/spec/unit/type/schedule.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/type/schedule'
diff --git a/spec/unit/ral/type/service.rb b/spec/unit/type/service.rb
index 17cb2105d..1a57bdd41 100755
--- a/spec/unit/ral/type/service.rb
+++ b/spec/unit/type/service.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
+require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/type/service'
diff --git a/spec/unit/type/ssh_authorized_key.rb b/spec/unit/type/ssh_authorized_key.rb
new file mode 100755
index 000000000..0e869747d
--- /dev/null
+++ b/spec/unit/type/ssh_authorized_key.rb
@@ -0,0 +1,80 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key)
+
+describe ssh_authorized_key do
+ before do
+ @class = Puppet::Type.type(:ssh_authorized_key)
+
+ @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true
+ @class.stubs(:defaultprovider).returns(@provider_class)
+ @class.stubs(:provider).returns(@provider_class)
+
+ @provider = stub 'provider', :class => @provider_class, :file_path => "/tmp/whatever", :clear => nil
+ @provider_class.stubs(:new).returns(@provider)
+ end
+
+ it "should have a name parameter" do
+ @class.attrtype(:name).should == :param
+ end
+
+ it "should have :name be its namevar" do
+ @class.namevar.should == :name
+ end
+
+ it "should have a :provider parameter" do
+ @class.attrtype(:provider).should == :param
+ end
+
+ it "should have an ensure property" do
+ @class.attrtype(:ensure).should == :property
+ end
+
+ it "should support :present as a value for :ensure" do
+ proc { @class.create(:name => "whev", :ensure => :present) }.should_not raise_error
+ end
+
+ it "should support :absent as a value for :ensure" do
+ proc { @class.create(:name => "whev", :ensure => :absent) }.should_not raise_error
+ end
+
+ it "should have an type property" do
+ @class.attrtype(:type).should == :property
+ end
+ it "should support ssh-dss as an type value" do
+ proc { @class.create(:name => "whev", :type => "ssh-dss") }.should_not raise_error
+ end
+ it "should support ssh-rsa as an type value" do
+ proc { @class.create(:name => "whev", :type => "ssh-rsa") }.should_not raise_error
+ end
+ it "should support :dsa as an type value" do
+ proc { @class.create(:name => "whev", :type => :dsa) }.should_not raise_error
+ end
+ it "should support :rsa as an type value" do
+ proc { @class.create(:name => "whev", :type => :rsa) }.should_not raise_error
+ end
+
+ it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa in the ssh_authorized_key_type" do
+ proc { @class.create(:name => "whev", :type => :something) }.should raise_error(Puppet::Error)
+ end
+
+ it "should have an key property" do
+ @class.attrtype(:key).should == :property
+ end
+
+ it "should have an user property" do
+ @class.attrtype(:user).should == :property
+ end
+
+ it "should have an options property" do
+ @class.attrtype(:options).should == :property
+ end
+
+ it "should have a target property" do
+ @class.attrtype(:target).should == :property
+ end
+
+ after { @class.clear }
+end
diff --git a/spec/unit/ral/type/user.rb b/spec/unit/type/user.rb
index 4e43a8ceb..d16d752f9 100755
--- a/spec/unit/ral/type/user.rb
+++ b/spec/unit/type/user.rb
@@ -1,14 +1,12 @@
#!/usr/bin/env ruby
-require File.dirname(__FILE__) + '/../../../spec_helper'
-
-require 'puppet/type/user'
+require File.dirname(__FILE__) + '/../../spec_helper'
module UserTestFunctions
def mkuser(name)
user = nil;
lambda {
- user = Puppet::Type::User.create(
+ user = Puppet::Type.type(:user).create(
:name => name,
:comment => "Puppet Testing User",
:gid => Puppet::Util::SUIDManager.gid,
@@ -30,12 +28,12 @@ module UserTestFunctions
end
end
-describe Puppet::Type::User do
+describe Puppet::Type.type(:user) do
include UserTestFunctions
it "should have a default provider inheriting from Puppet::Provider" do
- test_provider_class Puppet::Type::User.defaultprovider
+ test_provider_class Puppet::Type.type(:user).defaultprovider
end
it "should be able to create a instance" do
@@ -43,7 +41,7 @@ describe Puppet::Type::User do
end
end
-describe Puppet::Type::User, "instances" do
+describe Puppet::Type.type(:user), "instances" do
include UserTestFunctions
diff --git a/spec/unit/util/ldap/connection.rb b/spec/unit/util/ldap/connection.rb
new file mode 100755
index 000000000..212f3ca54
--- /dev/null
+++ b/spec/unit/util/ldap/connection.rb
@@ -0,0 +1,114 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-19.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/util/ldap/connection'
+
+# So our mocks and such all work, even when ldap isn't available.
+unless defined?(LDAP::Conn)
+ class LDAP
+ class Conn
+ def initialize(*args)
+ end
+ end
+ class SSLConn < Conn; end
+
+ LDAP_OPT_PROTOCOL_VERSION = 1
+ LDAP_OPT_REFERRALS = 2
+ LDAP_OPT_ON = 3
+ end
+end
+
+describe Puppet::Util::Ldap::Connection do
+ before do
+ Puppet.features.stubs(:ldap?).returns true
+
+ @ldapconn = mock 'ldap'
+ LDAP::Conn.stubs(:new).returns(@ldapconn)
+ LDAP::SSLConn.stubs(:new).returns(@ldapconn)
+
+ @ldapconn.stub_everything
+
+ @connection = Puppet::Util::Ldap::Connection.new("host", "port")
+ end
+
+
+ describe "when creating connections" do
+ it "should require the host and port" do
+ lambda { Puppet::Util::Ldap::Connection.new("myhost") }.should raise_error(ArgumentError)
+ end
+
+ it "should allow specification of a user and password" do
+ lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :user => "blah", :password => "boo") }.should_not raise_error
+ end
+
+ it "should allow specification of ssl" do
+ lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :ssl => :tsl) }.should_not raise_error
+ end
+
+ it "should support requiring a new connection" do
+ lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :reset => true) }.should_not raise_error
+ end
+
+ it "should fail if ldap is unavailable" do
+ Puppet.features.expects(:ldap?).returns(false)
+
+ lambda { Puppet::Util::Ldap::Connection.new("host", "port") }.should raise_error(Puppet::Error)
+ end
+
+ it "should use neither ssl nor tls by default" do
+ LDAP::Conn.expects(:new).with("host", "port").returns(@ldapconn)
+
+ @connection.start
+ end
+
+ it "should use LDAP::SSLConn if ssl is requested" do
+ LDAP::SSLConn.expects(:new).with("host", "port").returns(@ldapconn)
+
+ @connection.ssl = true
+
+ @connection.start
+ end
+
+ it "should use LDAP::SSLConn and tls if tls is requested" do
+ LDAP::SSLConn.expects(:new).with("host", "port", true).returns(@ldapconn)
+
+ @connection.ssl = :tls
+
+ @connection.start
+ end
+
+ it "should set the protocol version to 3 and enable referrals" do
+ @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @connection.start
+ end
+
+ it "should bind with the provided user and password" do
+ @connection.user = "myuser"
+ @connection.password = "mypassword"
+ @ldapconn.expects(:simple_bind).with("myuser", "mypassword")
+
+ @connection.start
+ end
+
+ it "should bind with no user and password if none has been provided" do
+ @ldapconn.expects(:simple_bind).with(nil, nil)
+ @connection.start
+ end
+ end
+
+ describe "when closing connections" do
+ it "should not close connections that are not open" do
+ @connection.stubs(:connection).returns(@ldapconn)
+
+ @ldapconn.expects(:bound?).returns false
+ @ldapconn.expects(:unbind).never
+
+ @connection.close
+ end
+ end
+end
diff --git a/spec/unit/util/ldap/generator.rb b/spec/unit/util/ldap/generator.rb
new file mode 100755
index 000000000..a6c69de83
--- /dev/null
+++ b/spec/unit/util/ldap/generator.rb
@@ -0,0 +1,54 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-28.
+# Copyright (c) 2008. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/util/ldap/generator'
+
+describe Puppet::Util::Ldap::Generator do
+ before do
+ @generator = Puppet::Util::Ldap::Generator.new(:uno)
+ end
+
+ it "should require a parameter name at initialization" do
+ lambda { Puppet::Util::Ldap::Generator.new }.should raise_error
+ end
+
+ it "should always return its name as a string" do
+ g = Puppet::Util::Ldap::Generator.new(:myname)
+ g.name.should == "myname"
+ end
+
+ it "should provide a method for declaring the source parameter" do
+ @generator.from(:dos)
+ end
+
+ it "should always return a set source as a string" do
+ @generator.from(:dos)
+ @generator.source.should == "dos"
+ end
+
+ it "should return the source as nil if there is no source" do
+ @generator.source.should be_nil
+ end
+
+ it "should return itself when declaring the source" do
+ @generator.from(:dos).should equal(@generator)
+ end
+
+ it "should run the provided block when asked to generate the value" do
+ @generator.with { "yayness" }
+ @generator.generate().should == "yayness"
+ end
+
+ it "should pass in any provided value to the block" do
+ @generator.with { |value| value.upcase }
+ @generator.generate("myval").should == "MYVAL"
+ end
+
+ it "should return itself when declaring the code used for generating" do
+ @generator.with { |value| value.upcase }.should equal(@generator)
+ end
+end
diff --git a/spec/unit/util/ldap/manager.rb b/spec/unit/util/ldap/manager.rb
new file mode 100755
index 000000000..b18b1b933
--- /dev/null
+++ b/spec/unit/util/ldap/manager.rb
@@ -0,0 +1,654 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2008-3-19.
+# Copyright (c) 2006. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/util/ldap/manager'
+
+# If the ldap classes aren't available, go ahead and
+# create some, so our tests will pass.
+unless defined?(LDAP::Mod)
+ class LDAP
+ LDAP_MOD_ADD = :adding
+ LDAP_MOD_REPLACE = :replacing
+ LDAP_MOD_DELETE = :deleting
+ class ResultError < RuntimeError; end
+ class Mod
+ def initialize(*args)
+ end
+ end
+ end
+end
+
+describe Puppet::Util::Ldap::Manager do
+ before do
+ @manager = Puppet::Util::Ldap::Manager.new
+ end
+
+ it "should return self when specifying objectclasses" do
+ @manager.manages(:one, :two).should equal(@manager)
+ end
+
+ it "should allow specification of what objectclasses are managed" do
+ @manager.manages(:one, :two).objectclasses.should == [:one, :two]
+ end
+
+ it "should return self when specifying the relative base" do
+ @manager.at("yay").should equal(@manager)
+ end
+
+ it "should allow specification of the relative base" do
+ @manager.at("yay").location.should == "yay"
+ end
+
+ it "should return self when specifying the attribute map" do
+ @manager.maps(:one => :two).should equal(@manager)
+ end
+
+ it "should allow specification of the rdn attribute" do
+ @manager.named_by(:uid).rdn.should == :uid
+ end
+
+ it "should allow specification of the attribute map" do
+ @manager.maps(:one => :two).puppet2ldap.should == {:one => :two}
+ end
+
+ it "should have a no-op 'and' method that just returns self" do
+ @manager.and.should equal(@manager)
+ end
+
+ it "should allow specification of generated attributes" do
+ @manager.generates(:thing).should be_instance_of(Puppet::Util::Ldap::Generator)
+ end
+
+ describe "when generating attributes" do
+ before do
+ @generator = stub 'generator', :source => "one", :name => "myparam"
+
+ Puppet::Util::Ldap::Generator.stubs(:new).with(:myparam).returns @generator
+ end
+
+ it "should create a generator to do the parameter generation" do
+ Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns @generator
+ @manager.generates(:myparam)
+ end
+
+ it "should return the generator from the :generates method" do
+ @manager.generates(:myparam).should equal(@generator)
+ end
+
+ it "should not replace already present values" do
+ @manager.generates(:myparam)
+
+ attrs = {"myparam" => "testing"}
+ @generator.expects(:generate).never
+
+ @manager.generate attrs
+
+ attrs["myparam"].should == "testing"
+ end
+
+ it "should look for the parameter as a string, not a symbol" do
+ @manager.generates(:myparam)
+ @generator.expects(:generate).with("yay").returns %w{double yay}
+ attrs = {"one" => "yay"}
+ @manager.generate attrs
+
+ attrs["myparam"].should == %w{double yay}
+ end
+
+ it "should fail if a source is specified and no source value is not defined" do
+ @manager.generates(:myparam)
+ lambda { @manager.generate "two" => "yay" }.should raise_error(ArgumentError)
+ end
+
+ it "should use the source value to generate the new value if a source attribute is specified" do
+ @manager.generates(:myparam)
+ @generator.expects(:generate).with("yay").returns %w{double yay}
+ @manager.generate "one" => "yay"
+ end
+
+ it "should not pass in any value if no source attribute is specified" do
+ @generator.stubs(:source).returns nil
+ @manager.generates(:myparam)
+ @generator.expects(:generate).with().returns %w{double yay}
+ @manager.generate "one" => "yay"
+ end
+
+ it "should convert any results to arrays of strings if necessary" do
+ @generator.expects(:generate).returns :test
+ @manager.generates(:myparam)
+
+ attrs = {"one" => "two"}
+ @manager.generate(attrs)
+ attrs["myparam"].should == ["test"]
+ end
+
+ it "should add the result to the passed-in attribute hash" do
+ @generator.expects(:generate).returns %w{test}
+ @manager.generates(:myparam)
+
+ attrs = {"one" => "two"}
+ @manager.generate(attrs)
+ attrs["myparam"].should == %w{test}
+ end
+ end
+
+ it "should be considered invalid if it is missing a location" do
+ @manager.manages :me
+ @manager.maps :me => :you
+ @manager.should_not be_valid
+ end
+
+ it "should be considered invalid if it is missing an objectclass list" do
+ @manager.maps :me => :you
+ @manager.at "ou=yayness"
+ @manager.should_not be_valid
+ end
+
+ it "should be considered invalid if it is missing an attribute map" do
+ @manager.manages :me
+ @manager.at "ou=yayness"
+ @manager.should_not be_valid
+ end
+
+ it "should be considered valid if it has an attribute map, location, and objectclass list" do
+ @manager.maps :me => :you
+ @manager.manages :me
+ @manager.at "ou=yayness"
+ @manager.should be_valid
+ end
+
+ it "should calculate an instance's dn using the :ldapbase setting and the relative base" do
+ Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing"
+ @manager.at "ou=mybase"
+ @manager.dn("me").should == "cn=me,ou=mybase,dc=testing"
+ end
+
+ it "should use the specified rdn when calculating an instance's dn" do
+ Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing"
+ @manager.named_by :uid
+ @manager.at "ou=mybase"
+ @manager.dn("me").should =~ /^uid=me/
+ end
+
+ it "should calculate its base using the :ldapbase setting and the relative base" do
+ Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing"
+ @manager.at "ou=mybase"
+ @manager.base.should == "ou=mybase,dc=testing"
+ end
+
+ describe "when generating its search filter" do
+ it "should using a single 'objectclass=<name>' filter if a single objectclass is specified" do
+ @manager.manages("testing")
+ @manager.filter.should == "objectclass=testing"
+ end
+
+ it "should create an LDAP AND filter if multiple objectclasses are specified" do
+ @manager.manages "testing", "okay", "done"
+ @manager.filter.should == "(&(objectclass=testing)(objectclass=okay)(objectclass=done))"
+ end
+ end
+
+ it "should have a method for converting a Puppet attribute name to an LDAP attribute name as a string" do
+ @manager.maps :puppet_attr => :ldap_attr
+ @manager.ldap_name(:puppet_attr).should == "ldap_attr"
+ end
+
+ it "should have a method for converting an LDAP attribute name to a Puppet attribute name" do
+ @manager.maps :puppet_attr => :ldap_attr
+ @manager.puppet_name(:ldap_attr).should == :puppet_attr
+ end
+
+ it "should have a :create method for creating ldap entries" do
+ @manager.should respond_to(:create)
+ end
+
+ it "should have a :delete method for deleting ldap entries" do
+ @manager.should respond_to(:delete)
+ end
+
+ it "should have a :modify method for modifying ldap entries" do
+ @manager.should respond_to(:modify)
+ end
+
+ it "should have a method for finding an entry by name in ldap" do
+ @manager.should respond_to(:find)
+ end
+
+ describe "when converting ldap entries to hashes for providers" do
+ before do
+ @manager.maps :uno => :one, :dos => :two
+
+ @result = @manager.entry2provider("dn" => ["cn=one,ou=people,dc=madstop"], "one" => ["two"], "three" => %w{four}, "objectclass" => %w{yay ness})
+ end
+
+ it "should set the name to the short portion of the dn" do
+ @result[:name].should == "one"
+ end
+
+ it "should remove the objectclasses" do
+ @result["objectclass"].should be_nil
+ end
+
+ it "should remove any attributes that are not mentioned in the map" do
+ @result["three"].should be_nil
+ end
+
+ it "should rename convert to symbols all attributes to their puppet names" do
+ @result[:uno].should == %w{two}
+ end
+
+ it "should set the value of all unset puppet attributes as :absent" do
+ @result[:dos].should == :absent
+ end
+ end
+
+ describe "when using an ldap connection" do
+ before do
+ @ldapconn = mock 'ldapconn'
+ @conn = stub 'connection', :connection => @ldapconn, :start => nil, :close => nil
+ Puppet::Util::Ldap::Connection.stubs(:new).returns(@conn)
+ end
+
+ it "should fail unless a block is given" do
+ lambda { @manager.connect }.should raise_error(ArgumentError)
+ end
+
+ it "should open the connection with its server set to :ldapserver" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldapserver).returns("myserver")
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[0] == "myserver" }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should open the connection with its port set to the :ldapport" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldapport).returns("28")
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[1] == "28" }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should open the connection with no user if :ldapuser is not set" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldapuser).returns("")
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user].nil? }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should open the connection with its user set to the :ldapuser if it is set" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldapuser).returns("mypass")
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user] == "mypass" }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should open the connection with no password if :ldappassword is not set" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldappassword).returns("")
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password].nil? }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should open the connection with its password set to the :ldappassword if it is set" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldappassword).returns("mypass")
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password] == "mypass" }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should set ssl to :tls if ldaptls is enabled" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldaptls).returns(true)
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == :tls }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should set ssl to true if ldapssl is enabled" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldapssl).returns(true)
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == true }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should set ssl to false if neither ldaptls nor ldapssl is enabled" do
+ Puppet.settings.stubs(:value).returns(false)
+ Puppet.settings.expects(:value).with(:ldapssl).returns(false)
+ Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == false }.returns @conn
+
+ @manager.connect { |c| }
+ end
+
+ it "should open, yield, and then close the connection" do
+ @conn.expects(:start)
+ @conn.expects(:close)
+ Puppet::Util::Ldap::Connection.expects(:new).returns(@conn)
+ @ldapconn.expects(:test)
+ @manager.connect { |c| c.test }
+ end
+
+ it "should close the connection even if there's an exception in the passed block" do
+ @conn.expects(:close)
+ lambda { @manager.connect { |c| raise ArgumentError } }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "when using ldap" do
+ before do
+ @conn = mock 'connection'
+ @manager.stubs(:connect).yields @conn
+ @manager.stubs(:objectclasses).returns [:oc1, :oc2]
+ @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro
+ end
+
+ describe "to create entries" do
+ it "should convert the first argument to its :create method to a full dn and pass the resulting argument list to its connection" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:add).with { |name, attrs| name == "mydn" }
+
+ @manager.create("myname", {"attr" => "myattrs"})
+ end
+
+ it "should add the objectclasses to the attributes" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("oc1") and attrs["objectClass"].include?("oc2") }
+
+ @manager.create("myname", {:one => :testing})
+ end
+
+ it "should add the rdn to the attributes" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:add).with { |name, attrs| attrs["cn"] == %w{myname} }
+
+ @manager.create("myname", {:one => :testing})
+ end
+
+ it "should add 'top' to the objectclasses if it is not listed" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("top") }
+
+ @manager.create("myname", {:one => :testing})
+ end
+
+ it "should add any generated values that are defined" do
+ generator = stub 'generator', :source => :one, :name => "myparam"
+
+ Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator
+
+ @manager.generates(:myparam)
+
+ @manager.stubs(:dn).with("myname").returns "mydn"
+
+ generator.expects(:generate).with(:testing).returns ["generated value"]
+ @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated value"] }
+
+ @manager.create("myname", {:one => :testing})
+ end
+
+ it "should convert any generated values to arrays of strings if necessary" do
+ generator = stub 'generator', :source => :one, :name => "myparam"
+
+ Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator
+
+ @manager.generates(:myparam)
+
+ @manager.stubs(:dn).returns "mydn"
+
+ generator.expects(:generate).returns :generated
+ @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated"] }
+
+ @manager.create("myname", {:one => :testing})
+ end
+ end
+
+ describe "do delete entries" do
+ it "should convert the first argument to its :delete method to a full dn and pass the resulting argument list to its connection" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:delete).with("mydn")
+
+ @manager.delete("myname")
+ end
+ end
+
+ describe "to modify entries" do
+ it "should convert the first argument to its :modify method to a full dn and pass the resulting argument list to its connection" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:modify).with("mydn", :mymods)
+
+ @manager.modify("myname", :mymods)
+ end
+ end
+
+ describe "to find a single entry" do
+ it "should use the dn of the provided name as the search base, a scope of 0, and 'objectclass=*' as the filter for a search2 call" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:search2).with("mydn", 0, "objectclass=*")
+
+ @manager.find("myname")
+ end
+
+ it "should return nil if an exception is thrown because no result is found" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ @conn.expects(:search2).raises LDAP::ResultError
+
+ @manager.find("myname").should be_nil
+ end
+
+ it "should return a converted provider hash if the result is found" do
+ @manager.expects(:dn).with("myname").returns "mydn"
+ result = {"one" => "two"}
+ @conn.expects(:search2).yields result
+
+ @manager.expects(:entry2provider).with(result).returns "myprovider"
+
+ @manager.find("myname").should == "myprovider"
+ end
+ end
+
+ describe "to search for multiple entries" do
+ before do
+ @manager.stubs(:filter).returns "myfilter"
+ end
+
+ it "should use the manager's search base as the dn of the provided name as the search base" do
+ @manager.expects(:base).returns "mybase"
+ @conn.expects(:search2).with { |base, scope, filter| base == "mybase" }
+
+ @manager.search
+ end
+
+ it "should use a scope of 1" do
+ @conn.expects(:search2).with { |base, scope, filter| scope == 1 }
+
+ @manager.search
+ end
+
+ it "should use any specified search filter" do
+ @manager.expects(:filter).never
+ @conn.expects(:search2).with { |base, scope, filter| filter == "boo" }
+
+ @manager.search("boo")
+ end
+
+ it "should turn its objectclass list into its search filter if one is not specified" do
+ @manager.expects(:filter).returns "yay"
+ @conn.expects(:search2).with { |base, scope, filter| filter == "yay" }
+
+ @manager.search
+ end
+
+ it "should return nil if no result is found" do
+ @conn.expects(:search2)
+
+ @manager.search.should be_nil
+ end
+
+ it "should return an array of the found results converted to provider hashes" do
+ # LAK: AFAICT, it's impossible to yield multiple times in an expectation.
+ one = {"dn" => "cn=one,dc=madstop,dc=com", "one" => "two"}
+ @conn.expects(:search2).yields(one)
+
+ @manager.expects(:entry2provider).with(one).returns "myprov"
+
+ @manager.search.should == ["myprov"]
+ end
+ end
+ end
+
+ describe "when an instance" do
+ before do
+ @name = "myname"
+ @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro
+ end
+
+ describe "is being updated" do
+ it "should get created if the current attribute list is empty and the desired attribute list has :ensure == :present" do
+ @manager.expects(:create)
+ @manager.update(@name, {}, {:ensure => :present})
+ end
+
+ it "should get created if the current attribute list has :ensure == :absent and the desired attribute list has :ensure == :present" do
+ @manager.expects(:create)
+ @manager.update(@name, {:ensure => :absent}, {:ensure => :present})
+ end
+
+ it "should get deleted if the current attribute list has :ensure == :present and the desired attribute list has :ensure == :absent" do
+ @manager.expects(:delete)
+ @manager.update(@name, {:ensure => :present}, {:ensure => :absent})
+ end
+
+ it "should get modified if both attribute lists have :ensure == :present" do
+ @manager.expects(:modify)
+ @manager.update(@name, {:ensure => :present, :one => :two}, {:ensure => :present, :one => :three})
+ end
+ end
+
+ describe "is being deleted" do
+ it "should call the :delete method with its name and manager" do
+ @manager.expects(:delete).with(@name)
+
+ @manager.update(@name, {}, {:ensure => :absent})
+ end
+ end
+
+ describe "is being created" do
+ before do
+ @is = {}
+ @should = {:ensure => :present, :one => :yay, :two => :absent}
+ end
+
+ it "should call the :create method with its name" do
+ @manager.expects(:create).with { |name, attrs| name == @name }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should call the :create method with its property hash converted to ldap attribute names" do
+ @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should convert the property names to strings" do
+ @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should convert the property values to arrays if necessary" do
+ @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should convert the property values to strings if necessary" do
+ @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should not include :ensure in the properties sent" do
+ @manager.expects(:create).with { |*args| args[1][:ensure].nil? }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should not include attributes set to :absent in the properties sent" do
+ @manager.expects(:create).with { |*args| args[1][:dos].nil? }
+ @manager.update(@name, @is, @should)
+ end
+ end
+
+ describe "is being modified" do
+ it "should call the :modify method with its name and an array of LDAP::Mod instances" do
+ LDAP::Mod.stubs(:new).returns "whatever"
+
+ @is = {:one => :yay}
+ @should = {:one => :yay, :two => :foo}
+
+ @manager.expects(:modify).with { |name, mods| name == @name }
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should create the LDAP::Mod with the property name converted to the ldap name as a string" do
+ @is = {:one => :yay}
+ @should = {:one => :yay, :two => :foo}
+ mod = mock 'module'
+ LDAP::Mod.expects(:new).with { |form, name, value| name == "dos" }.returns mod
+
+ @manager.stubs(:modify)
+
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should create an LDAP::Mod instance of type LDAP_MOD_ADD for each attribute being added, with the attribute value converted to a string of arrays" do
+ @is = {:one => :yay}
+ @should = {:one => :yay, :two => :foo}
+ mod = mock 'module'
+ LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_ADD, "dos", ["foo"]).returns mod
+
+ @manager.stubs(:modify)
+
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should create an LDAP::Mod instance of type LDAP_MOD_DELETE for each attribute being deleted" do
+ @is = {:one => :yay, :two => :foo}
+ @should = {:one => :yay, :two => :absent}
+ mod = mock 'module'
+ LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_DELETE, "dos", []).returns mod
+
+ @manager.stubs(:modify)
+
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should create an LDAP::Mod instance of type LDAP_MOD_REPLACE for each attribute being modified, with the attribute converted to a string of arrays" do
+ @is = {:one => :yay, :two => :four}
+ @should = {:one => :yay, :two => :five}
+ mod = mock 'module'
+ LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_REPLACE, "dos", ["five"]).returns mod
+
+ @manager.stubs(:modify)
+
+ @manager.update(@name, @is, @should)
+ end
+
+ it "should pass all created Mod instances to the modify method" do
+ @is = {:one => :yay, :two => :foo, :three => :absent}
+ @should = {:one => :yay, :two => :foe, :three => :fee, :four => :fie}
+ LDAP::Mod.expects(:new).times(3).returns("mod1").then.returns("mod2").then.returns("mod3")
+
+ @manager.expects(:modify).with do |name, mods|
+ mods.sort == %w{mod1 mod2 mod3}.sort
+ end
+
+ @manager.update(@name, @is, @should)
+ end
+ end
+ end
+end
diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb
new file mode 100755
index 000000000..55d1d1f61
--- /dev/null
+++ b/spec/unit/util/storage.rb
@@ -0,0 +1,237 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'yaml'
+require 'tempfile'
+
+require 'puppet/util/storage'
+
+describe Puppet::Util::Storage do
+ before(:all) do
+ Puppet[:statedir] = Dir.tmpdir()
+ end
+
+ before(:each) do
+ Puppet::Util::Storage.clear()
+ end
+
+ describe "when caching a symbol" do
+ it "should return an empty hash" do
+ Puppet::Util::Storage.cache(:yayness).should == {}
+ Puppet::Util::Storage.cache(:more_yayness).should == {}
+ end
+
+ it "should add the symbol to its internal state" do
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ end
+
+ it "should not clobber existing state when caching additional objects" do
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ Puppet::Util::Storage.cache(:bubblyness)
+ Puppet::Util::Storage.state().should == {:yayness=>{},:bubblyness=>{}}
+ end
+ end
+
+ describe "when caching a Puppet::Type" do
+ before(:all) do
+ @file_test = Puppet.type(:file).create(:name => "/yayness", :check => %w{checksum type})
+ @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness")
+ end
+
+ it "should return an empty hash" do
+ Puppet::Util::Storage.cache(@file_test).should == {}
+ Puppet::Util::Storage.cache(@exec_test).should == {}
+ end
+
+ it "should add the resource ref to its internal state" do
+ Puppet::Util::Storage.state().should == {}
+ Puppet::Util::Storage.cache(@file_test)
+ Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}}
+ Puppet::Util::Storage.cache(@exec_test)
+ Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}
+ end
+ end
+
+ describe "when caching invalid objects" do
+ before(:all) do
+ @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ]
+ end
+
+ it "should raise an ArgumentError" do
+ @bogus_objects.each do |object|
+ proc { Puppet::Util::Storage.cache(object) }.should raise_error()
+ end
+ end
+
+ it "should not add anything to its internal state" do
+ @bogus_objects.each do |object|
+ begin
+ Puppet::Util::Storage.cache(object)
+ rescue
+ Puppet::Util::Storage.state().should == {}
+ end
+ end
+ end
+ end
+
+ it "should clear its internal state when clear() is called" do
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ Puppet::Util::Storage.clear()
+ Puppet::Util::Storage.state().should == {}
+ end
+
+ describe "when loading from the state file" do
+ describe "when the state file/directory does not exist" do
+ before(:each) do
+ transient = Tempfile.new('storage_test')
+ @path = transient.path()
+ transient.close!()
+ end
+
+ it "should not fail to load()" do
+ FileTest.exists?(@path).should be_false()
+ Puppet[:statedir] = @path
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet[:statefile] = @path
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ end
+
+ it "should not lose its internal state when load() is called" do
+ FileTest.exists?(@path).should be_false()
+
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+
+ Puppet[:statefile] = @path
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ end
+ end
+
+ describe "when the state file/directory exists" do
+ before(:each) do
+ @state_file = Tempfile.new('storage_test')
+ @saved_statefile = Puppet[:statefile]
+ Puppet[:statefile] = @state_file.path()
+ end
+
+ it "should overwrite its internal state if load() is called" do
+ # Should the state be overwritten even if Puppet[:statefile] is not valid YAML?
+ Puppet::Util::Storage.cache(:yayness)
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == {}
+ end
+
+ it "should restore its internal state if the state file contains valid YAML" do
+ test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}}
+ YAML.expects(:load).returns(test_yaml)
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == test_yaml
+ end
+
+ it "should initialize with a clear internal state if the state file does not contain valid YAML" do
+ @state_file.write(:booness)
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == {}
+ end
+
+ it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do
+ @state_file.write(:booness)
+ File.chmod(0000, @state_file.path())
+
+ proc { Puppet::Util::Storage.load() }.should raise_error()
+ end
+
+ it "should attempt to rename the state file if the file is corrupted" do
+ # We fake corruption by causing YAML.load to raise an exception
+ YAML.expects(:load).raises(Puppet::Error)
+ File.expects(:rename).at_least_once
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ end
+
+ it "should fail gracefully on load() if the state file is not a regular file" do
+ @state_file.close!()
+ Dir.mkdir(Puppet[:statefile])
+ File.expects(:rename).returns(0)
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+
+ Dir.rmdir(Puppet[:statefile])
+ end
+
+ it "should fail gracefully on load() if it cannot get a read lock on the state file" do
+ Puppet::Util.expects(:readlock).yields(false)
+ test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}}
+ YAML.expects(:load).returns(test_yaml)
+
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == test_yaml
+ end
+
+ after(:each) do
+ @state_file.close!()
+ Puppet[:statefile] = @saved_statefile
+ end
+ end
+ end
+
+ describe "when storing to the state file" do
+ before(:each) do
+ @state_file = Tempfile.new('storage_test')
+ @saved_statefile = Puppet[:statefile]
+ Puppet[:statefile] = @state_file.path()
+ end
+
+ it "should create the state file if it does not exist" do
+ @state_file.close!()
+ FileTest.exists?(Puppet[:statefile]).should be_false()
+ Puppet::Util::Storage.cache(:yayness)
+
+ proc { Puppet::Util::Storage.store() }.should_not raise_error()
+ FileTest.exists?(Puppet[:statefile]).should be_true()
+ end
+
+ it "should raise an exception if the state file is not a regular file" do
+ @state_file.close!()
+ Dir.mkdir(Puppet[:statefile])
+ Puppet::Util::Storage.cache(:yayness)
+
+ proc { Puppet::Util::Storage.store() }.should raise_error()
+
+ Dir.rmdir(Puppet[:statefile])
+ end
+
+ it "should raise an exception if it cannot get a write lock on the state file" do
+ Puppet::Util.expects(:writelock).yields(false)
+ Puppet::Util::Storage.cache(:yayness)
+
+ proc { Puppet::Util::Storage.store() }.should raise_error()
+ end
+
+ it "should load() the same information that it store()s" do
+ Puppet::Util::Storage.cache(:yayness)
+
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ proc { Puppet::Util::Storage.store() }.should_not raise_error()
+ Puppet::Util::Storage.clear()
+ Puppet::Util::Storage.state().should == {}
+ proc { Puppet::Util::Storage.load() }.should_not raise_error()
+ Puppet::Util::Storage.state().should == {:yayness=>{}}
+ end
+
+ after(:each) do
+ @state_file.close!()
+ Puppet[:statefile] = @saved_statefile
+ end
+ end
+end
diff --git a/test/data/providers/ssh_authorized_key/parsed/authorized_keys b/test/data/providers/ssh_authorized_key/parsed/authorized_keys
new file mode 100644
index 000000000..033f98b87
--- /dev/null
+++ b/test/data/providers/ssh_authorized_key/parsed/authorized_keys
@@ -0,0 +1,5 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAJkupmdsJSDXfUy5EU5NTRBDr9Woo3w0YnB8KmnJW9ghU8C7SkWPB1fIHVe+esFfd3qWBseb83PoFX63geZJAg6bjV4/Rdn1zEoa9EO2QyUdYUen4+rpsh3vVKZ6HFNsn3+W5+kPYgE1F/N4INqkbjY3sqCkP/W1BL9+sbVVbuJFAAAAFQCfjWDk5XhvGUkPjNWWVqltBYzHtwAAAIEAg/XL7ky7x9Ad5banzPFAfmM+DGFe0A/JEbLDjKmr5KBM5x4RFohtEvZ8ECuVGUOqBWdgAjyYwsG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4knEl+mNfOLq+FH0011UhecOiqTcESMzQDtgQ1vJh2VchElBLjl3x/ZugAAACAAh5jGQC338t5ObP8trSlOefkx0sXmmEzUbo3Mt8mGUuGJPx8m+X0L8Xd+l5rQxytqE3SmV/RD+6REqBuPqHM8RQuqAzfjdOeg/Ajdggx1CRMTVhltZsgQoxO30cz9Qo0SdPoL+Jp1fLuaLZq7m/RmsWYvoLT3jebBlpvvQE8YlI= francois.deppierraz@nimag.net
+ssh-dss AAAAB3NzaC1kc3MAAACBAJkupmdsJSDXfUy5EU5NTRBDr9Woo3w0YnB8KmnJW9ghU8C7SkWPB1fIHVe+esFfd3qWBseb83PoFX63geZJAg6bjV4/Rdn1zEoa9EO2QyUdYUen4+rpsh3vVKZ6HFNsn3+W5+kPYgE1F/N4INqkbjY3sqCkP/W1BL9+sbVVbuJFAAAAFQCfjWDk5XhvGUkPjNWWVqltBYzHtwAAAIEAg/XL7ky7x9Ad5banzPFAfmM+DGFe0A/JEbLDjKmr5KBM5x4RFohtEvZ8ECuVGUOqBWdgAjyYwsG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4knEl+mNfOLq+FH0011UhecOiqTcESMzQDtgQ1vJh2VchElBLjl3x/ZugAAACAAh5jGQC338t5ObP8trSlOefkx0sXmmEzUbo3Mt8mGUuGJPx8m+X0L8Xd+l5rQxytqE3SmV/RD+6REqBuPqHM8RQuqAzfjdOeg/Ajdggx1CRMTVhltZsgQoxO30cz9Qo0SdPoL+Jp1fLuaLZq7m/RmsWYvoLT3jebBlpvvQE8YlI= Francois Deppierraz <francois@ctrlaltdel.ch>
+from="192.168.1.1",command="/bin/false",no-pty,no-port-forwarding ssh-dss AAAAB3NzaC1kc3MAAACBAJkupmdsJSDXfUy5EU5NTRBDr9Woo3w0YnB8KmnJW9ghU8C7SkWPB1fIHVe+esFfd3qWBseb83PoFX63geZJAg6bjV4/Rdn1zEoa9EO2QyUdYUen4+rpsh3vVKZ6HFNsn3+W5+kPYgE1F/N4INqkbjY3sqCkP/W1BL9+sbVVbuJFAAAAFQCfjWDk5XhvGUkPjNWWVqltBYzHtwAAAIEAg/XL7ky7x9Ad5banzPFAfmM+DGFe0A/JEbLDjKmr5KBM5x4RFohtEvZ8ECuVGUOqBWdgAjyYwsG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4knEl+mNfOLq+FH0011UhecOiqTcESMzQDtgQ1vJh2VchElBLjl3x/ZugAAACAAh5jGQC338t5ObP8trSlOefkx0sXmmEzUbo3Mt8mGUuGJPx8m+X0L8Xd+l5rQxytqE3SmV/RD+6REqBuPqHM8RQuqAzfjdOeg/Ajdggx1CRMTVhltZsgQoxO30cz9Qo0SdPoL+Jp1fLuaLZq7m/RmsWYvoLT3jebBlpvvQE8YlI= Francois Deppierraz
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2Vi+TdC3iOGYcIo5vGTvC9P9rjHl9RxCuZmSfn+YDFQ35RXf0waijtjp9I7GYh6R4hBjA5z0u/Pzi95LET5NfRM0Gdc0DJyvBI7K+ALBxIT383Iz6Yz4iKxe1TEJgHGM2he4+7BHkjc3kdIZqIpZjucCk4VsXSxujO4MKKvtaKK2l+kahlLQHHw/vZkDpIgL52iGVsjW9l8RLJaKHZ4mDHJN/Q/Rzn2W4EvcdHUzwhvGMwZlm8clDwITBrSsawYtnivJrQSYcmTRqJuS8wprNDrLIhTGjrwFg5WpruUuMt6fLuCqwe6TeEL+nh3DQ4g554c5aRp3oU6LGBKTvNZGWQ== francois@korn
+ssh-dss AAAAB3NzaC1kc3MAAACBAMPpCYnjywOemd8LqbbmC+bePNR3/H1rXsiFwjSLhYE3bbOpvclvOzN1DruFc34m0FopVnMkP+aubjdIYF8pijl+5hg9ggB7Kno2dl0Dd1rGN/swvmhA8OpLAQv7Qt7UnXKVho3as08zYZsrHxYFu0wlnkdbsv4cy4aXyQKd4MPVAAAAFQDSyQFWg8Qt3wU05buhZ10psoR7tQAAAIEAmAhguXwUnI3P2FF5NAW/mpJUmUERdL4pyZARUyAgpf7ezwrh9TJqrvGTQNBF97Xqaivyncm5JWQdMIsTBxEFaXZGkmBta02KnWcn447qvIh7iv8XpNL6M9flCkBEZOJ4t9El0ytTSHHaiCz8A20Et+E8evWyi1kXkFDt8ML2dGgAAACBAK0X4ympbdEjgV/ZyOc+BU22u7vOnfSOUJmyar4Ax1MIDNnoyNWKnGvxRutydQcQOKQHZEU0fE8MhPFn6nLF6CoVfEl/oz0EYz3hjV4WPFpHrF5DY/rhvNj8iuneKJ5P0dy/rG6m5qey25PnHyGFVoIRlkHJvBCJT40dHs40YEjI francois@korn
diff --git a/test/data/types/ssh_authorized_key/1 b/test/data/types/ssh_authorized_key/1
new file mode 100644
index 000000000..69d1af15e
--- /dev/null
+++ b/test/data/types/ssh_authorized_key/1
@@ -0,0 +1,2 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAJkupmdsJSDXfUy5EU5NTRBDr9Woo3w0YnB8KmnJW9ghU8C7SkWPB1fIHVe+esFfd3qWBseb83PoFX63geZJAg6bjV4/Rdn1zEoa9EO2QyUdYUen4+rpsh3vVKZ6HFNsn3+W5+kPYgE1F/N4INqkbjY3sqCkP/W1BL9+sbVVbuJFAAAAFQCfjWDk5XhvGUkPjNWWVqltBYzHtwAAAIEAg/XL7ky7x9Ad5banzPFAfmM+DGFe0A/JEbLDjKmr5KBM5x4RFohtEvZ8ECuVGUOqBWdgAjyYwsG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4knEl+mNfOLq+FH0011UhecOiqTcESMzQDtgQ1vJh2VchElBLjl3x/ZugAAACAAh5jGQC338t5ObP8trSlOefkx0sXmmEzUbo3Mt8mGUuGJPx8m+X0L8Xd+l5rQxytqE3SmV/RD+6REqBuPqHM8RQuqAzfjdOeg/Ajdggx1CRMTVhltZsgQoxO30cz9Qo0SdPoL+Jp1fLuaLZq7m/RmsWYvoLT3jebBlpvvQE8YlI= francois.deppierraz@camptocamp.com
+from="192.168.1.2",command="/usr/local/bin/backup.sh",no-agent-forwarding,no-port-forwarding,no-X11-forwarding ssh-rsa AAAAB3NzaC1kc3MAAACBAJkupmdsJSDXfUy5EU5NTRBDr9Woo3w0YnB8KmnJW9ghU8C7SkWPB1fIHVesG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4ksG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4ksG4oRVjLnKrf/rgmbNRzSFgEWkcAye3BVwk7Dt6hh4kxytqE3SmV/RD+6REqBuPqHM8RQuqAzfjdOeg/Ajdggx1CRMTVhltZsgQoxO30cz9Qo0SdPoL+Jp1fLuaL Backup system
diff --git a/test/ral/providers/cron/crontab.rb b/test/ral/providers/cron/crontab.rb
index 53bd76c50..900a0478e 100755
--- a/test/ral/providers/cron/crontab.rb
+++ b/test/ral/providers/cron/crontab.rb
@@ -599,5 +599,33 @@ class TestCronParsedProvider < Test::Unit::TestCase
result = target.read
assert_equal("# Puppet Name: test\n* 4 * * * /bin/echo yay\n", result, "Did not write out environment setting")
end
+
+ # Testing #1216
+ def test_strange_lines
+ @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram))
+ text = " 5 \t\t 1,2 * 1 0 /bin/echo funtest"
+
+ records = nil
+ assert_nothing_raised {
+ records = @provider.parse(text)
+ }
+
+ should = {
+ :minute => %w{5},
+ :hour => %w{1 2},
+ :monthday => :absent,
+ :month => %w{1},
+ :weekday => %w{0},
+ :command => "/bin/echo funtest"
+ }
+
+ is = records.shift
+ assert(is, "Did not get record")
+
+ should.each do |p, v|
+ assert_equal(v, is[p], "did not parse %s correctly" % p)
+ end
+ end
+
end
diff --git a/test/ral/providers/provider.rb b/test/ral/providers/provider.rb
index 2196fafce..70f606a37 100755
--- a/test/ral/providers/provider.rb
+++ b/test/ral/providers/provider.rb
@@ -37,12 +37,13 @@ class TestProvider < Test::Unit::TestCase
cleanup { Puppet::Type.rmtype(:provider_test) }
end
- def test_confine
- provider = newprovider
+ def test_confine_defaults_to_suitable
- assert(provider.suitable?,
- "Marked unsuitable with no confines")
+ provider = newprovider
+ assert(provider.suitable?, "Marked unsuitable with no confines")
+ end
+ def test_confine_results
{
{:true => true} => true,
{:true => false} => false,
@@ -54,6 +55,8 @@ class TestProvider < Test::Unit::TestCase
{:exists => echo} => true,
{:exists => "/this/file/does/not/exist"} => false,
}.each do |hash, result|
+ provider = newprovider
+
# First test :true
hash.each do |test, val|
assert_nothing_raised do
@@ -61,19 +64,25 @@ class TestProvider < Test::Unit::TestCase
end
end
- assert_equal(result, provider.suitable?,
- "Failed for %s" % [hash.inspect])
+ assert_equal(result, provider.suitable?, "Failed for %s" % [hash.inspect])
provider.initvars
end
+ end
+
+ def test_multiple_confines_do_not_override
+ provider = newprovider
# Make sure multiple confines don't overwrite each other
provider.confine :true => false
assert(! provider.suitable?)
provider.confine :true => true
assert(! provider.suitable?)
+ end
- provider.initvars
+ def test_one_failed_confine_is_sufficient
+
+ provider = newprovider
# Make sure we test multiple of them, and that a single false wins
provider.confine :true => true, :false => false
@@ -82,6 +91,18 @@ class TestProvider < Test::Unit::TestCase
assert(! provider.suitable?)
end
+ # #1197 - the binary should not be
+ def test_command_checks_for_binaries_each_time
+ provider = newprovider
+
+ provider.commands :testing => "/no/such/path"
+
+ provider.stubs(:binary).returns "/no/such/path"
+
+ provider.command(:testing)
+ assert_equal("/no/such/path", provider.command(:testing), "Did not return correct binary path")
+ end
+
def test_command
{:echo => "echo", :echo_with_path => echo, :missing => "nosuchcommand", :missing_qualified => "/path/to/nosuchcommand"}.each do |name, command|
provider = newprovider