From b2a3db94c7226c7660277c746254f47ab40cde7b Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 24 Apr 2008 23:23:22 +1000 Subject: Fixed #1196 - added /sbin/service support for the redhat service provider + some doco fixes --- lib/puppet/provider/service/base.rb | 8 ++++---- lib/puppet/provider/service/init.rb | 6 +++--- lib/puppet/provider/service/redhat.rb | 25 +++++++++++++++++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) 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..356f6ffbe 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 @@ -50,5 +50,22 @@ Puppet::Type.type(:service).provide :redhat, :parent => :init do [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 -- cgit From fff6ad90d781b8fda2b93916a67ef65b3b263c66 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Wed, 30 Apr 2008 23:23:41 +1000 Subject: Fix for ticket #1209 --- lib/puppet/parser/functions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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| -- cgit From 5a2bbad8b7239d64ffa4ad51ab1a68d4926c6a72 Mon Sep 17 00:00:00 2001 From: Nigel Kersten Date: Thu, 24 Apr 2008 21:28:36 -0700 Subject: fix bindir/sbindir defaults on OS X 10.5 --- install.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/install.rb b/install.rb index 087c3c860..70922cc98 100755 --- a/install.rb +++ b/install.rb @@ -200,6 +200,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']}" -- cgit From c57e194418b658ddcd82610c3c273bfa17ba5f8b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 1 May 2008 14:04:52 -0500 Subject: Fixing an error message to be more clear --- lib/puppet/parser/compiler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit From 69fc802fde27f1de09d6e0d68e1d79ff1c08acdd Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Mon, 5 May 2008 09:35:17 +1000 Subject: Update to man pages, fix to ralsh help text and fix for #1211 --- bin/ralsh | 2 +- install.rb | 2 +- man/man8/filebucket.8 | 2 +- man/man8/pi.8 | 2 +- man/man8/puppet.8 | 2 +- man/man8/puppet.conf.8 | 8 ++++---- man/man8/puppetca.8 | 10 ++++++---- man/man8/puppetd.8 | 2 +- man/man8/puppetdoc.8 | 2 +- man/man8/puppetmasterd.8 | 2 +- man/man8/puppetrun.8 | 2 +- man/man8/ralsh.8 | 33 ++++++++++----------------------- 12 files changed, 29 insertions(+), 40 deletions(-) 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/install.rb b/install.rb index 70922cc98..207f74a0b 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 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 190aab2f8..990b7cee9 100644 --- a/man/man8/puppet.8 +++ b/man/man8/puppet.8 @@ -74,5 +74,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. .\" -- cgit From 2b185af97882afb4a7feab42de97771ceed80b43 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Mon, 5 May 2008 20:45:19 +1000 Subject: Add values for dump parameter for the mount type closing #1212 --- lib/puppet/type/mount.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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? -- cgit From db8a46c605b8b4a205e65aa35a1442f2de32431b Mon Sep 17 00:00:00 2001 From: Francois Deppierraz Date: Thu, 10 Apr 2008 19:24:06 +0200 Subject: New native ssh_authorized_key type --- lib/puppet/provider/ssh_authorized_key/parsed.rb | 50 ++++++++++++++ lib/puppet/type/ssh_authorized_key.rb | 47 +++++++++++++ .../unit/ral/provider/ssh_authorized_key/parsed.rb | 74 ++++++++++++++++++++ spec/unit/ral/type/ssh_authorized_key.rb | 80 ++++++++++++++++++++++ .../ssh_authorized_key/parsed/authorized_keys | 5 ++ test/data/types/ssh_authorized_key/1 | 2 + 6 files changed, 258 insertions(+) create mode 100644 lib/puppet/provider/ssh_authorized_key/parsed.rb create mode 100644 lib/puppet/type/ssh_authorized_key.rb create mode 100644 spec/unit/ral/provider/ssh_authorized_key/parsed.rb create mode 100644 spec/unit/ral/type/ssh_authorized_key.rb create mode 100644 test/data/providers/ssh_authorized_key/parsed/authorized_keys create mode 100644 test/data/types/ssh_authorized_key/1 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..228bbc6fc --- /dev/null +++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb @@ -0,0 +1,50 @@ +require 'puppet/provider/parsedfile' + +Puppet::Type.type(:ssh_authorized_key).provide(:parsed, + :parent => Puppet::Provider::ParsedFile, + :filetype => :flat, + # Ugly but the parameter is required + :default_target => '/proc/NONEXISTANT' +) 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/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb new file mode 100644 index 000000000..97521ccab --- /dev/null +++ b/lib/puppet/type/ssh_authorized_key.rb @@ -0,0 +1,47 @@ +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") + newvalue("none") + + aliasvalue(:dsa, "ssh-dss") + aliasvalue(:rsa, "ssh-rsa") + + defaultto "none" + 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/spec/unit/ral/provider/ssh_authorized_key/parsed.rb b/spec/unit/ral/provider/ssh_authorized_key/parsed.rb new file mode 100644 index 000000000..459001cb5 --- /dev/null +++ b/spec/unit/ral/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/ral/type/ssh_authorized_key.rb b/spec/unit/ral/type/ssh_authorized_key.rb new file mode 100644 index 000000000..d27cb9f25 --- /dev/null +++ b/spec/unit/ral/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/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 +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 -- cgit From 67dc261a764d54dd5c1b9a487b8c43d67ad4347c Mon Sep 17 00:00:00 2001 From: Francois Deppierraz Date: Fri, 9 May 2008 17:26:20 +0200 Subject: Removed "none" as a valid type attribute value, it was useless anyway --- lib/puppet/type/ssh_authorized_key.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/puppet/type/ssh_authorized_key.rb b/lib/puppet/type/ssh_authorized_key.rb index 97521ccab..e28fb7cda 100644 --- a/lib/puppet/type/ssh_authorized_key.rb +++ b/lib/puppet/type/ssh_authorized_key.rb @@ -16,12 +16,9 @@ module Puppet newvalue("ssh-dss") newvalue("ssh-rsa") - newvalue("none") aliasvalue(:dsa, "ssh-dss") aliasvalue(:rsa, "ssh-rsa") - - defaultto "none" end newproperty(:key) do -- cgit From 88ec3d808b93058f39bf0c5ac20ed1fcb9477c2b Mon Sep 17 00:00:00 2001 From: Francois Deppierraz Date: Fri, 9 May 2008 17:35:44 +0200 Subject: Cosmetic fix --- lib/puppet/provider/ssh_authorized_key/parsed.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb index 228bbc6fc..7cb6626de 100644 --- a/lib/puppet/provider/ssh_authorized_key/parsed.rb +++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb @@ -3,8 +3,7 @@ require 'puppet/provider/parsedfile' Puppet::Type.type(:ssh_authorized_key).provide(:parsed, :parent => Puppet::Provider::ParsedFile, :filetype => :flat, - # Ugly but the parameter is required - :default_target => '/proc/NONEXISTANT' + :default_target => '' ) do desc "Parse and generate authorized_keys files for SSH." -- cgit From da4cdd27573a319e8fa5f89642c98377b8911fa4 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 10 May 2008 09:54:49 +1000 Subject: Fix for ticket #1218 - changed to appropriate variable name --- lib/puppet/util/settings.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index d27406d6d..0e6f91e48 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -122,7 +122,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 -- cgit From ae842ea8214a846624f89711d34f7481d5a8d01a Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sun, 11 May 2008 21:23:25 +1000 Subject: Fix for urpmi provider that fixes #1217 --- lib/puppet/provider/package/urpmi.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 -- cgit From f3fa58927527632472372a2612b288805b877b4f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 15:54:08 -0500 Subject: Fixing a test that wrote to ~. --- spec/unit/rails.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/unit/rails.rb b/spec/unit/rails.rb index ad32cac86..bb2f991ca 100755 --- a/spec/unit/rails.rb +++ b/spec/unit/rails.rb @@ -6,6 +6,14 @@ require 'puppet/rails' describe Puppet::Rails, "when initializing any connection" do confine Puppet.features.rails? => "Cannot test without ActiveRecord" + before do + @logger = mock 'logger' + @logger.stub_everything + Logger.stubs(:new).returns(@logger) + + ActiveRecord::Base.stubs(:logger).returns(@logger) + end + it "should use settings" do Puppet.settings.expects(:use).with(:main, :rails, :puppetmasterd) -- cgit From 270c007672c9a0458f14189cff3ccf1ff6311963 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 15:55:02 -0500 Subject: Clarifying the exception when there's a syntax error but a valid parser. This is related to #1215. --- lib/puppet/parser/interpreter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit From 20e60b111687c286737c981b13edfaac23e7b1a6 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 16:37:06 -0500 Subject: Applying patch by martin to fix #1207. The -o flag is now only used when the uid is being changed. --- lib/puppet/provider/nameservice/objectadd.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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] -- cgit From 9bf21a65bb05c7135cc7e4b46a59cb110fd40b5f Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Fri, 2 May 2008 12:54:35 -0700 Subject: Use our own count-matches for Emacs 21 compatibility Replace count-matches with a puppet-count-matches function that does the same as the Emacs 22 count-matches. The Emacs 21 count-matches is different and not useful for our purposes. --- ext/emacs/puppet-mode.el | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index 0a7ee1a5f..56a2b3acc 100644 --- a/ext/emacs/puppet-mode.el +++ b/ext/emacs/puppet-mode.el @@ -56,6 +56,15 @@ "*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 @@ -77,7 +86,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 () -- cgit From f52e3433da9001f85722f9efcb90208e4bb2fd2b Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Fri, 2 May 2008 20:37:38 -0700 Subject: Enhancements to syntax highlighting and indentation for Emacs More correctly handle multiple resources in a block, such as: type { "name": attr => foo, oattr => bar; "other": attr => baz; } Handle continued arguments in parentheses, such as define or realize arguments. Try to be a bit more robust about indenting in general. Add the remaining Puppet language keywords (I think) and improve the handling of type references at the top level. Remove the hack for continued include lines and fix it properly. Remove more non-Puppet code left over from the mode this one was based on. Use a more straightforward method of setting up the font-lock keywords and do it the same way for both XEmacs and Emacs since I think they can both handle the current method (and other modes agree). --- ext/emacs/puppet-mode.el | 284 ++++++++++++++++++++++++++--------------------- 1 file changed, 159 insertions(+), 125 deletions(-) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index 56a2b3acc..a6d47eea1 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 ;;; +;;; 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.") @@ -68,8 +69,24 @@ of Emacs." (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. + (if (= (puppet-count-matches "}" apoint opoint) 0) + (current-indentation) + nil)))))) (defun puppet-in-array () "If point is in an array, return the position of the opening '[' of @@ -99,15 +116,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 () @@ -119,6 +135,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 @@ -155,18 +172,12 @@ 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))) + ((looking-at "^\\s-*}\\s-*$") + ;; This line contains only a closing brace, so we should indent it + ;; matching the indentation of the opening brace of the block. + (if block-indent + (setq cur-indent block-indent) + (setq cur-indent 0))) (t ;; Otherwise, we did not start on a block-ending-only line. (save-excursion @@ -174,30 +185,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 () @@ -222,97 +339,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) -- cgit From 954aad4faa33ff9c4504eb5673c7dd122a914d4a Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Fri, 9 May 2008 15:09:47 -0700 Subject: Fix Emacs mode indentation of multiple nested blocks Indentation of multiple nested blocks was broken; the closing braces of all the outer blocks were put into column 0 because finding the block indentation failed. Do normal indentation if finding a block indentation fails, but more importantly, be smarter about searching backwards to find the beginning of the current block, taking balanced braces into account. There is probably some less-ugly and more Emacs-native way of doing this. --- ext/emacs/puppet-mode.el | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ext/emacs/puppet-mode.el b/ext/emacs/puppet-mode.el index a6d47eea1..f3f4589aa 100644 --- a/ext/emacs/puppet-mode.el +++ b/ext/emacs/puppet-mode.el @@ -84,7 +84,14 @@ of the closing brace of a block." (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. - (if (= (puppet-count-matches "}" apoint opoint) 0) + (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)))))) @@ -172,12 +179,11 @@ of the initial include plus puppet-include-indent." (setq cur-indent (current-column)))) (include-start (setq cur-indent include-start)) - ((looking-at "^\\s-*}\\s-*$") - ;; This line contains only a closing brace, so we should indent it - ;; matching the indentation of the opening brace of the block. - (if block-indent - (setq cur-indent block-indent) - (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 -- cgit From c56e9a6a0a9491270e22363e750046f284ee2793 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 16:59:30 -0500 Subject: Fixing another test that wrote to ~ --- spec/unit/node/catalog.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/unit/node/catalog.rb b/spec/unit/node/catalog.rb index cd27b925b..8178f953a 100755 --- a/spec/unit/node/catalog.rb +++ b/spec/unit/node/catalog.rb @@ -562,6 +562,7 @@ describe Puppet::Node::Catalog do # super() doesn't work in the setup method for some reason before do @catalog.host_config = true + Puppet::Util::Storage.stubs(:store) end it "should send a report if reporting is enabled" do -- cgit From 17e8158e35336291c551da03067b55dd815ab539 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 17:00:48 -0500 Subject: Adding ldap providers for the user and group type. These providers use posixAccount and posixGroup. This is a collapsed merge, fwiw. --- lib/puppet/provider/group/ldap.rb | 37 +++ lib/puppet/provider/ldap.rb | 137 ++++++++ lib/puppet/provider/user/ldap.rb | 115 +++++++ lib/puppet/util/ldap.rb | 5 + lib/puppet/util/ldap/connection.rb | 57 ++++ lib/puppet/util/ldap/generator.rb | 45 +++ lib/puppet/util/ldap/manager.rb | 281 ++++++++++++++++ spec/unit/provider/group/ldap.rb | 66 ++++ spec/unit/provider/ldap.rb | 248 ++++++++++++++ spec/unit/provider/user/ldap.rb | 252 ++++++++++++++ spec/unit/util/ldap/connection.rb | 114 +++++++ spec/unit/util/ldap/generator.rb | 54 +++ spec/unit/util/ldap/manager.rb | 654 +++++++++++++++++++++++++++++++++++++ 13 files changed, 2065 insertions(+) create mode 100644 lib/puppet/provider/group/ldap.rb create mode 100644 lib/puppet/provider/ldap.rb create mode 100644 lib/puppet/provider/user/ldap.rb create mode 100644 lib/puppet/util/ldap.rb create mode 100644 lib/puppet/util/ldap/connection.rb create mode 100644 lib/puppet/util/ldap/generator.rb create mode 100644 lib/puppet/util/ldap/manager.rb create mode 100755 spec/unit/provider/group/ldap.rb create mode 100755 spec/unit/provider/ldap.rb create mode 100755 spec/unit/provider/user/ldap.rb create mode 100755 spec/unit/util/ldap/connection.rb create mode 100755 spec/unit/util/ldap/generator.rb create mode 100755 spec/unit/util/ldap/manager.rb 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/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/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/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/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/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/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=' 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 -- cgit From 5e2a4b593f110b171117d75e6a7723bf52f5296b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 17:01:27 -0500 Subject: updating the changelog for the ldap providers --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 188a99c38..f076f3784 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ + 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. -- cgit From d8cc1c5e0289f7710050b1f305e0ed180a368169 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 12 May 2008 17:25:27 -0500 Subject: adding execute bits to tests --- spec/unit/ral/provider/ssh_authorized_key/parsed.rb | 0 spec/unit/ral/type/ssh_authorized_key.rb | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 spec/unit/ral/provider/ssh_authorized_key/parsed.rb mode change 100644 => 100755 spec/unit/ral/type/ssh_authorized_key.rb diff --git a/spec/unit/ral/provider/ssh_authorized_key/parsed.rb b/spec/unit/ral/provider/ssh_authorized_key/parsed.rb old mode 100644 new mode 100755 diff --git a/spec/unit/ral/type/ssh_authorized_key.rb b/spec/unit/ral/type/ssh_authorized_key.rb old mode 100644 new mode 100755 -- cgit From 49dde1187e8caea10af63ec1f92e19c0e5f1a595 Mon Sep 17 00:00:00 2001 From: Adam Jacob Date: Mon, 12 May 2008 21:41:04 -0700 Subject: Adding has_variable? support, fixing ticket #1177 --- lib/puppet/parser/templatewrapper.rb | 9 ++++++ spec/unit/parser/templatewrapper.rb | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 spec/unit/parser/templatewrapper.rb diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 7a8f74156..4f044befc 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -19,6 +19,15 @@ class Puppet::Parser::TemplateWrapper @scope.parser.watch_file(@file) 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. diff --git a/spec/unit/parser/templatewrapper.rb b/spec/unit/parser/templatewrapper.rb new file mode 100644 index 000000000..76fed3950 --- /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 in to 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 as 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 defiend 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 \ No newline at end of file -- cgit From 59b9958712d033bbd294ff6469ea3a242c45c82c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 13 May 2008 11:58:33 -0500 Subject: Correcting whitespace in the templatewrapper code. Also slightly modified the wording of some of the tests. --- lib/puppet/parser/templatewrapper.rb | 12 ++-- spec/unit/parser/templatewrapper.rb | 104 +++++++++++++++++------------------ 2 files changed, 58 insertions(+), 58 deletions(-) mode change 100644 => 100755 spec/unit/parser/templatewrapper.rb diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 4f044befc..4790cea30 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -19,14 +19,14 @@ class Puppet::Parser::TemplateWrapper @scope.parser.watch_file(@file) 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 + if @scope.lookupvar(name.to_s, false) != :undefined + true + else + false + end end # Ruby treats variables like methods, so we can cheat here and diff --git a/spec/unit/parser/templatewrapper.rb b/spec/unit/parser/templatewrapper.rb old mode 100644 new mode 100755 index 76fed3950..40465f955 --- a/spec/unit/parser/templatewrapper.rb +++ b/spec/unit/parser/templatewrapper.rb @@ -3,55 +3,55 @@ 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 in to 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 as 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 defiend 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 \ No newline at end of file + 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 -- cgit From c61fc0217fe8880216fe059ce54c4d0d61a78811 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 13 May 2008 16:09:28 -0500 Subject: Adding autotest info to the ext/ directory. Adding the autotest config, instructions, and a Rakefile that can install it. --- ext/autotest/Rakefile | 8 ++++++++ ext/autotest/config | 43 +++++++++++++++++++++++++++++++++++++++++++ ext/autotest/readme.rst | 9 +++++++++ 3 files changed, 60 insertions(+) create mode 100644 ext/autotest/Rakefile create mode 100644 ext/autotest/config create mode 100644 ext/autotest/readme.rst 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..338d4b0d3 --- /dev/null +++ b/ext/autotest/readme.rst @@ -0,0 +1,9 @@ +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). + +.. _zentest: http://www.zenspider.com/ZSS/Products/ZenTest/ +.. _growlnotify: http://growl.info/extras.php -- cgit From b5006897fbf1c52c5a53820d6d1175213027c019 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 13 May 2008 16:11:36 -0500 Subject: adding more autotest docs --- ext/autotest/readme.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/autotest/readme.rst b/ext/autotest/readme.rst index 338d4b0d3..93d9ed2fc 100644 --- a/ext/autotest/readme.rst +++ b/ext/autotest/readme.rst @@ -5,5 +5,12 @@ 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 -- cgit From 83ef1b0cda1b010eea3f7d001716ea52f7081c24 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Thu, 15 May 2008 17:13:30 +1000 Subject: Fix for #1219 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; Updated CHANGELOG Fix for #1219. 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; --- CHANGELOG | 4 ++++ lib/puppet/provider/service/redhat.rb | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f076f3784..a0c5a1213 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ + 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 diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb index 356f6ffbe..e2d6ac947 100755 --- a/lib/puppet/provider/service/redhat.rb +++ b/lib/puppet/provider/service/redhat.rb @@ -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,8 +42,7 @@ 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] -- cgit From 158d3df805ebe28f52db5ced928dda7129aeec1b Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Thu, 15 May 2008 08:27:07 +0200 Subject: Added the ability to add arbitrary attributes to ldap. This fixes #1179. --- ext/ldap/puppet.schema | 7 ++- lib/puppet/defaults.rb | 4 ++ lib/puppet/indirector/node/ldap.rb | 29 ++++++++++++ spec/unit/indirector/node/ldap.rb | 91 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) 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/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index eed1a00f3..df2fb9425 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -622,6 +622,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/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 -- cgit From e7bef08bbac951dd1b798273af3ff2b098bf9187 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 10:51:11 -0500 Subject: Fixing the user test. It was causing exceptions when run as part of the whole suite. --- spec/unit/ral/type/user.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/spec/unit/ral/type/user.rb b/spec/unit/ral/type/user.rb index 4e43a8ceb..b697426dc 100755 --- a/spec/unit/ral/type/user.rb +++ b/spec/unit/ral/type/user.rb @@ -2,13 +2,11 @@ require File.dirname(__FILE__) + '/../../../spec_helper' -require 'puppet/type/user' - 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 -- cgit From d02334f114a73facd2b3a90f35cb1f3c04365e2f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 19:11:08 -0500 Subject: Moving all tests that are in 'ral' up a level. This directory only existed as an organizational method, and the code never matched it, so I'm fixing it. --- spec/integration/ral/types/package.rb | 24 -- spec/integration/type/package.rb | 24 ++ spec/unit/provider/interface/redhat.rb | 268 ++++++++++++++++ spec/unit/provider/interface/sunos.rb | 239 +++++++++++++++ spec/unit/provider/mount.rb | 130 ++++++++ spec/unit/provider/mount/parsed.rb | 185 +++++++++++ spec/unit/provider/ssh_authorized_key/parsed.rb | 74 +++++ spec/unit/ral/provider/interface/redhat.rb | 268 ---------------- spec/unit/ral/provider/interface/sunos.rb | 239 --------------- spec/unit/ral/provider/mount.rb | 130 -------- spec/unit/ral/provider/mount/parsed.rb | 185 ----------- .../unit/ral/provider/ssh_authorized_key/parsed.rb | 74 ----- spec/unit/ral/type.rb | 29 -- spec/unit/ral/type/exec.rb | 93 ------ spec/unit/ral/type/file.rb | 100 ------ spec/unit/ral/type/interface.rb | 95 ------ spec/unit/ral/type/mount.rb | 192 ------------ spec/unit/ral/type/nagios.rb | 63 ---- spec/unit/ral/type/noop_metaparam.rb | 38 --- spec/unit/ral/type/package.rb | 247 --------------- spec/unit/ral/type/schedule.rb | 341 --------------------- spec/unit/ral/type/service.rb | 256 ---------------- spec/unit/ral/type/ssh_authorized_key.rb | 80 ----- spec/unit/ral/type/user.rb | 56 ---- spec/unit/type.rb | 29 ++ spec/unit/type/exec.rb | 93 ++++++ spec/unit/type/file.rb | 100 ++++++ spec/unit/type/interface.rb | 95 ++++++ spec/unit/type/mount.rb | 192 ++++++++++++ spec/unit/type/nagios.rb | 63 ++++ spec/unit/type/noop_metaparam.rb | 38 +++ spec/unit/type/package.rb | 247 +++++++++++++++ spec/unit/type/schedule.rb | 341 +++++++++++++++++++++ spec/unit/type/service.rb | 256 ++++++++++++++++ spec/unit/type/ssh_authorized_key.rb | 80 +++++ spec/unit/type/user.rb | 56 ++++ 36 files changed, 2510 insertions(+), 2510 deletions(-) delete mode 100755 spec/integration/ral/types/package.rb create mode 100755 spec/integration/type/package.rb create mode 100755 spec/unit/provider/interface/redhat.rb create mode 100755 spec/unit/provider/interface/sunos.rb create mode 100755 spec/unit/provider/mount.rb create mode 100755 spec/unit/provider/mount/parsed.rb create mode 100755 spec/unit/provider/ssh_authorized_key/parsed.rb delete mode 100755 spec/unit/ral/provider/interface/redhat.rb delete mode 100755 spec/unit/ral/provider/interface/sunos.rb delete mode 100755 spec/unit/ral/provider/mount.rb delete mode 100755 spec/unit/ral/provider/mount/parsed.rb delete mode 100755 spec/unit/ral/provider/ssh_authorized_key/parsed.rb delete mode 100755 spec/unit/ral/type.rb delete mode 100755 spec/unit/ral/type/exec.rb delete mode 100755 spec/unit/ral/type/file.rb delete mode 100755 spec/unit/ral/type/interface.rb delete mode 100755 spec/unit/ral/type/mount.rb delete mode 100755 spec/unit/ral/type/nagios.rb delete mode 100755 spec/unit/ral/type/noop_metaparam.rb delete mode 100755 spec/unit/ral/type/package.rb delete mode 100755 spec/unit/ral/type/schedule.rb delete mode 100755 spec/unit/ral/type/service.rb delete mode 100755 spec/unit/ral/type/ssh_authorized_key.rb delete mode 100755 spec/unit/ral/type/user.rb create mode 100755 spec/unit/type.rb create mode 100755 spec/unit/type/exec.rb create mode 100755 spec/unit/type/file.rb create mode 100755 spec/unit/type/interface.rb create mode 100755 spec/unit/type/mount.rb create mode 100755 spec/unit/type/nagios.rb create mode 100755 spec/unit/type/noop_metaparam.rb create mode 100755 spec/unit/type/package.rb create mode 100755 spec/unit/type/schedule.rb create mode 100755 spec/unit/type/service.rb create mode 100755 spec/unit/type/ssh_authorized_key.rb create mode 100755 spec/unit/type/user.rb diff --git a/spec/integration/ral/types/package.rb b/spec/integration/ral/types/package.rb deleted file mode 100755 index 20567629d..000000000 --- a/spec/integration/ral/types/package.rb +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/package' - -describe Puppet::Type::Package, "when choosing a default package provider" do - before do - # the default provider is cached. - Puppet::Type::Package.defaultprovider = nil - end - - def provider_name(os) - {"Debian" => :apt, "Darwin" => :apple, "RedHat" => :up2date, "Fedora" => :yum, "FreeBSD" => :ports, "OpenBSD" => :openbsd, "Solaris" => :sun}[os] - end - - it "should have a default provider" do - Puppet::Type::Package.defaultprovider.should_not be_nil - end - - it "should choose the correct provider each platform" do - Puppet::Type::Package.defaultprovider.name.should == provider_name(Facter.value(:operatingsystem)) - end -end diff --git a/spec/integration/type/package.rb b/spec/integration/type/package.rb new file mode 100755 index 000000000..20567629d --- /dev/null +++ b/spec/integration/type/package.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/package' + +describe Puppet::Type::Package, "when choosing a default package provider" do + before do + # the default provider is cached. + Puppet::Type::Package.defaultprovider = nil + end + + def provider_name(os) + {"Debian" => :apt, "Darwin" => :apple, "RedHat" => :up2date, "Fedora" => :yum, "FreeBSD" => :ports, "OpenBSD" => :openbsd, "Solaris" => :sun}[os] + end + + it "should have a default provider" do + Puppet::Type::Package.defaultprovider.should_not be_nil + end + + it "should choose the correct provider each platform" do + Puppet::Type::Package.defaultprovider.name.should == provider_name(Facter.value(:operatingsystem)) + end +end diff --git a/spec/unit/provider/interface/redhat.rb b/spec/unit/provider/interface/redhat.rb new file mode 100755 index 000000000..9bf1b9722 --- /dev/null +++ b/spec/unit/provider/interface/redhat.rb @@ -0,0 +1,268 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-20. +# Copyright (c) 2006. All rights reserved. + +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) + 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) + provider_class.should be_suitable + end +end + +describe provider_class, " when determining the file path" do + it "should always contain '/etc/sysconfig/network-scripts/ifcfg-'" do + provider = provider_class.new(:name => "192.168.0.1") + provider.file_path.should =~ %r{^/etc/sysconfig/network-scripts/ifcfg-} + end + + it "should include the interface name and the description when the interface is an alias" do + provider = provider_class.new(:name => "192.168.0.1", :interface => "eth0") + provider.interface_type = :alias + resource = stub 'resource' + resource.stubs(:[]).with(:interface_desc).returns("blah") + provider.resource = resource + provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-eth0:blah" + end + + it "should just include the description when the interface is not an alias" do + provider = provider_class.new(:name => "192.168.0.1") + provider.interface_type = :normal + resource = stub 'resource' + resource.stubs(:[]).with(:interface_desc).returns("eth0") + provider.resource = resource + provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-eth0" + end + + it "should use the interface description if one is available" do + provider = provider_class.new(:name => "192.168.0.1") + provider.interface_type = :normal + resource = stub 'resource' + resource.stubs(:[]).with(:interface_desc).returns("eth0") + provider.resource = resource + provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-eth0" + end + + it "should use the name if no interface description is available" do + provider = provider_class.new(:name => "192.168.0.1") + provider.interface_type = :normal + provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-192.168.0.1" + end + + it "should fail if no name or interface description can be found" do + provider = provider_class.new() + proc { provider.file_path }.should raise_error + end +end + +describe provider_class, " when returning instances" do + it "should consider each file in the network-scripts directory an interface instance" do + Dir.expects(:glob).with("/etc/sysconfig/network-scripts/ifcfg-*").returns(%w{one two}) + one = {:name => "one"} + two = {:name => "two"} + Puppet::Type::Interface::ProviderRedhat.expects(:parse).with("one").returns(one) + Puppet::Type::Interface::ProviderRedhat.expects(:parse).with("two").returns(two) + Puppet::Type::Interface::ProviderRedhat.expects(:new).with(one).returns(:one) + Puppet::Type::Interface::ProviderRedhat.expects(:new).with(two).returns(:two) + Puppet::Type::Interface::ProviderRedhat.instances.should == [:one, :two] + end +end + +describe provider_class, " when parsing" do + it "should return an unmodified provider if the file does not exist" do + FileTest.expects(:exist?).with("/my/file").returns(false) + provider = mock 'provider' + Puppet::Type::Interface::ProviderRedhat.expects(:new).returns(provider) + Puppet::Type::Interface::ProviderRedhat.parse("/my/file").should equal(provider) + end + + it "should set each attribute in the file on the provider" do + FileTest.expects(:exist?).with("/my/file").returns(true) + File.expects(:readlines).with("/my/file").returns(%w{one=two three=four}) + provider = mock 'provider' + Puppet::Type::Interface::ProviderRedhat.expects(:new).returns(provider) + provider.expects(:one=).with('two') + provider.expects(:three=).with('four') + Puppet::Type::Interface::ProviderRedhat.parse("/my/file").should equal(provider) + end +end + +describe provider_class, " when setting the device to a value containing ':'" do + before do + @provider = Puppet::Type::Interface::ProviderRedhat.new + @provider.device = "one:two" + end + it "should set the interface type to :alias" do + @provider.interface_type.should == :alias + end + it "should set the interface to the string to the left of the ':'" do + @provider.interface.should == "one" + end + it "should set the ifnum to the string to the right of the ':'" do + @provider.ifnum.should == "two" + end +end + +describe provider_class, " when setting the device to a value starting with 'dummy-'" do + before do + @provider = Puppet::Type::Interface::ProviderRedhat.new + @provider.device = "dummy5" + end + it "should set the interface type to :loopback" do + @provider.interface_type.should == :loopback + end + it "should set the interface to 'dummy'" do + @provider.interface.should == "dummy" + end + it "should set the ifnum to remainder of value after removing 'dummy'" do + @provider.ifnum.should == "5" + end +end + +describe provider_class, " when setting the device to a value containing neither 'dummy-' nor ':'" do + before do + @provider = Puppet::Type::Interface::ProviderRedhat.new + @provider.device = "whatever" + end + it "should set the interface type to :normal" do + @provider.interface_type.should == :normal + end + it "should set the interface to the device value" do + @provider.interface.should == "whatever" + end +end + +describe provider_class, " when setting the on_boot value" do + before do + @provider = Puppet::Type::Interface::ProviderRedhat.new + end + it "should set it to :true if the value is 'yes'" do + @provider.on_boot = "yes" + @provider.onboot.should == :true + end + it "should set it to :false if the value is not 'yes'" do + @provider.on_boot = "no" + @provider.onboot.should == :false + end +end + +describe provider_class, " when setting the ipaddr value" do + before do + @provider = Puppet::Type::Interface::ProviderRedhat.new + end + + it "should set the name to the provided value" do + @provider.ipaddr = "yay" + @provider.name.should == "yay" + end +end + +describe provider_class, " when generating" do + before do + @provider = Puppet::Type::Interface::ProviderRedhat.new + @provider.interface_type = :alias + @provider.stubs(:device).returns("mydevice") + @provider.stubs(:on_boot).returns("myboot") + @provider.stubs(:name).returns("myname") + @provider.stubs(:netmask).returns("mynetmask") + @provider.interface_type = :alias + + @text = @provider.generate + end + + it "should set the bootproto to none if the interface is an alias" do + @text.should =~ /^BOOTPROTO=none$/ + end + + it "should set the bootproto to static if the interface is a loopback" do + @provider.interface_type = :loopback + @text = @provider.generate + @text.should =~ /^BOOTPROTO=static$/ + end + + it "should set the broadcast address to nothing" do + @text.should =~ /^BROADCAST=$/ + end + + it "should set the netmask to mynetmask" do + @text.should =~ /^NETMASK=mynetmask$/ + end + + it "should set the device to the provider's device" do + @text.should =~ /^DEVICE=mydevice$/ + end + + it "should set the onboot to the provider's on_boot value" do + @text.should =~ /^ONBOOT=myboot$/ + end + + it "should set the ipaddr to the provider's name" do + @text.should =~ /^IPADDR=myname$/ + end +end + +describe provider_class, " when creating and destroying" do + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + @path = "/etc/sysconfig/network-scripts/ifcfg-testing" + end + + it "should consider the interface present if the file exists" do + FileTest.expects(:exist?).with(@path).returns(true) + @provider.should be_exists + end + + it "should consider the interface absent if the file does not exist" do + FileTest.expects(:exist?).with(@path).returns(false) + @provider.should_not be_exists + end + + it "should remove the file if the interface is being destroyed" do + File.expects(:unlink).with(@path) + @provider.destroy + end + + it "should mark :ensure as :absent if the interface is destroyed" do + File.stubs(:unlink) + @provider.destroy + @provider.ensure.should == :absent + end + + it "should mark :ensure as :present if the interface is being created" do + resource = stub 'resource', :name => 'testing' + resource.stubs(:should).with { |name| name == :ensure }.returns(:present) + resource.stubs(:should).with { |name| name != :ensure }.returns(nil) + @provider.resource = resource + @provider.create + @provider.ensure.should == :present + end + + it "should write the generated text to disk when the interface is flushed" do + fh = mock("filehandle") + File.expects(:open).yields(fh) + fh.expects(:puts).with("generated") + resource = stub 'resource', :name => 'testing' + resource.stubs(:[]).with(:interface_desc).returns(nil) + resource.stubs(:should).with { |name| name == :ensure }.returns(:present) + resource.stubs(:should).with { |name| name != :ensure }.returns(nil) + @provider.resource = resource + @provider.create + + @provider.stubs(:generate).returns("generated") + @provider.flush + end + + it "should not write the generated text to disk when the interface is flushed if :ensure == :absent" do + @provider.ensure = :absent + @provider.flush + end +end diff --git a/spec/unit/provider/interface/sunos.rb b/spec/unit/provider/interface/sunos.rb new file mode 100755 index 000000000..7b9f462e6 --- /dev/null +++ b/spec/unit/provider/interface/sunos.rb @@ -0,0 +1,239 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-11-25. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppet/provider/interface/sunos' + + +provider_class = Puppet::Type.type(:interface).provider(:sunos) + +describe provider_class do + it "should not be functional on non-SunOS kernels" do + Facter.expects(:value).with(:kernel).returns("Linux") + provider_class.should_not be_suitable + end + + it "should be functional on SunOS kernels" do + Facter.expects(:value).with(:kernel).returns("SunOS") + provider_class.should be_suitable + end + + it "should pick its file path by combining '/etc/hostname.' with the interface if one is set" do + provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing", :interface => 'eth0') + provider.file_path.should == "/etc/hostname.eth0" + end + + it "should pick its file path by combining '/etc/hostname.' with the resource's interface if one is not set in the provider" do + provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") + resource = mock 'resource' + resource.stubs(:[]).with(:interface).returns("eth0") + provider.resource = resource + provider.file_path.should == "/etc/hostname.eth0" + end + + it "should fail when picking its file path if there is no resource nor an interface set in the provider" do + provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") + proc { provider.file_path }.should raise_error(Puppet::Error) + end +end + +describe provider_class, " when listing interfaces" do + it "should return an instance for every file matching /etc/hostname.*, created with the interface name set from the file" do + Dir.expects(:glob).with("/etc/hostname.*").returns(%w{/etc/hostname.one /etc/hostname.two}) + one_instance = stub 'one_instance', :parse => nil + two_instance = stub 'two_instance', :parse => nil + provider_class.expects(:new).with(:interface => "one").returns(one_instance) + provider_class.expects(:new).with(:interface => "two").returns(two_instance) + + provider_class.instances.should == [one_instance, two_instance] + end + + it "should call parse on each instance being returned" do + Dir.expects(:glob).with("/etc/hostname.*").returns(%w{/etc/hostname.one}) + one_instance = mock 'one_instance' + provider_class.expects(:new).with(:interface => "one").returns(one_instance) + + one_instance.expects(:parse) + + provider_class.instances + end + + it "should assign matching providers to any prefetched instances" do + Dir.expects(:glob).with("/etc/hostname.*").returns(%w{one two}) + one_instance = stub 'one_instance', :name => "one", :parse => nil + two_instance = stub 'two_instance', :name => "two", :parse => nil + provider_class.expects(:new).with(:interface => "one").returns(one_instance) + provider_class.expects(:new).with(:interface => "two").returns(two_instance) + + resources = {"one" => mock("one"), "three" => mock('three')} + resources["one"].expects(:provider=).with(one_instance) + + provider_class.prefetch(resources) + end +end + +describe provider_class, " when creating and destroying" do + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + end + + it "should consider the interface present if the file exists" do + FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(true) + @provider.should be_exists + end + + it "should consider the interface absent if the file does not exist" do + FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(false) + @provider.should_not be_exists + end + + it "should remove the file if the interface is being destroyed" do + File.expects(:unlink).with("/etc/hostname.eth0") + @provider.destroy + end + + it "should mark :ensure as :absent if the interface is destroyed" do + File.stubs(:unlink) + @provider.destroy + @provider.ensure.should == :absent + end + + it "should mark :ensure as :present if the interface is being created" do + resource = stub 'resource', :name => 'testing' + resource.stubs(:should).with { |name| name == :ensure }.returns(:present) + resource.stubs(:should).with { |name| name != :ensure }.returns(nil) + @provider.resource = resource + @provider.create + @provider.ensure.should == :present + end + + it "should write the generated text to disk when the interface is flushed" do + fh = mock("filehandle") + File.expects(:open).yields(fh) + fh.expects(:print).with("testing\n") + resource = stub 'resource', :name => 'testing' + resource.stubs(:should).with { |name| name == :ensure }.returns(:present) + resource.stubs(:should).with { |name| name != :ensure }.returns(nil) + @provider.resource = resource + @provider.create + @provider.flush + end + + it "should not write the generated text to disk when the interface is flushed if :ensure == :absent" do + @provider.ensure = :absent + @provider.flush + end +end + +describe provider_class, " when parsing a non-existant file" do + it "should mark the interface as absent" do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(false) + @provider.parse + @provider.ensure.should == :absent + end +end + +describe provider_class, " when parsing an existing file" do + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + FileTest.stubs(:exist?).with("/etc/hostname.eth0").returns(true) + end + + def set_text(text) + File.stubs(:read).with("/etc/hostname.eth0").returns(text) + end + + it "should retain the interface name" do + set_text "testing" + @provider.parse + @provider.ensure.should == :present + @provider.interface.should == "eth0" + end + + it "should mark the interface as present" do + set_text "testing" + @provider.parse + @provider.ensure.should == :present + end + + it "should mark the interface as an alias if the first word is 'addif'" do + set_text "addif testing" + @provider.parse + @provider.interface_type.should == :alias + end + + it "should not mark the interface as normal if the first word is not 'addif'" do + set_text "testing" + @provider.parse + @provider.interface_type.should == :normal + end + + it "should start the interface on boot of the last word is 'up'" do + set_text "testing up" + @provider.parse + @provider.onboot.should == :true + end + + it "should not start the interface on boot of the last word is not 'up'" do + set_text "testing" + @provider.parse + @provider.onboot.should == :false + end + + it "should set the interface to the first non-behavioural word" do + set_text "addif testing up" + @provider.parse + @provider.name.should == "testing" + end + + it "should consider any remaining terms to be interface options" do + set_text "addif testing -O up" + @provider.parse + @provider.ifopts.should == "-O" + end +end + +describe provider_class, " when generating" do + before do + @provider = provider_class.new(:interface => "eth0", :name => "testing") + end + + it "should prefix the text with 'addif' if the interface is an alias" do + @provider.interface_type = :alias + @provider.generate.should == "addif testing" + end + + it "should not prefix the text with 'addif' if the interface is not an alias" do + @provider.generate.should == "testing" + end + + it "should put the ifopts after the name if they are present" do + @provider.ifopts = "-O" + @provider.generate.should == "testing -O" + end + + it "should mark the interface up if onboot is enabled" do + @provider.onboot = :true + @provider.generate.should == "testing up" + end + + it "should use the resource name if no provider name is present" do + provider = provider_class.new(:interface => "eth0") + resource = stub 'resource', :name => "rtest" + provider.resource = resource + provider.generate.should == "rtest" + end + + it "should use the provider name if present" do + @provider.generate.should == "testing" + end + + it "should fail if neither a resource nor the provider name is present" do + provider = provider_class.new(:interface => "eth0") + proc { provider.generate }.should raise_error + end +end diff --git a/spec/unit/provider/mount.rb b/spec/unit/provider/mount.rb new file mode 100755 index 000000000..0b90d53c9 --- /dev/null +++ b/spec/unit/provider/mount.rb @@ -0,0 +1,130 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/provider/mount' + +describe Puppet::Provider::Mount do + before :each do + @mounter = Object.new + @mounter.extend(Puppet::Provider::Mount) + + @name = "/" + + @resource = stub 'resource' + @resource.stubs(:[]).with(:name).returns(@name) + + @mounter.stubs(:resource).returns(@resource) + end + + describe Puppet::Provider::Mount, " when mounting" do + + it "should use the 'mountcmd' method to mount" do + @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd) + + @mounter.mount + end + + it "should flush before mounting if a flush method exists" do + @mounter.meta_def(:flush) { } + @mounter.expects(:flush) + @mounter.stubs(:mountcmd) + @mounter.stubs(:options).returns(nil) + + @mounter.mount + end + + it "should add the options following '-o' if they exist and are not set to :absent" do + @mounter.stubs(:options).returns("ro") + @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } + + @mounter.mount + end + + it "should specify the filesystem name to the mount command" do + @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } + + @mounter.mount + end + end + + describe Puppet::Provider::Mount, " when remounting" do + + it "should use '-o remount' if the resource specifies it supports remounting" do + @mounter.stubs(:info) + @resource.stubs(:[]).with(:remounts).returns(:true) + @mounter.expects(:mountcmd).with("-o", "remount", @name) + @mounter.remount + end + + it "should unmount and mount if the resource does not specify it supports remounting" do + @mounter.stubs(:info) + @resource.stubs(:[]).with(:remounts).returns(false) + @mounter.expects(:unmount) + @mounter.expects(:mount) + @mounter.remount + end + + it "should log that it is remounting" do + @resource.stubs(:[]).with(:remounts).returns(:true) + @mounter.stubs(:mountcmd) + @mounter.expects(:info).with("Remounting") + @mounter.remount + end + end + + describe Puppet::Provider::Mount, " when unmounting" do + + it "should call the :umount command with the resource name" do + @mounter.expects(:umount).with(@name) + @mounter.unmount + end + end + + describe Puppet::Provider::Mount, " when determining if it is mounted" do + + it "should parse the results of running the mount command with no arguments" do + Facter.stubs(:value).returns("whatever") + @mounter.expects(:mountcmd).returns("") + + @mounter.mounted? + end + + it "should match ' on /private/var/automount' if the operating system is Darwin" do + Facter.stubs(:value).with("operatingsystem").returns("Darwin") + @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") + + @mounter.should be_mounted + end + + it "should match ' on ' if the operating system is Darwin" do + Facter.stubs(:value).with("operatingsystem").returns("Darwin") + @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") + + @mounter.should be_mounted + end + + it "should match '^ on' if the operating system is Solaris" do + Facter.stubs(:value).with("operatingsystem").returns("Solaris") + @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") + + @mounter.should be_mounted + end + + it "should match ' on ' if the operating system is not Darwin or Solaris" do + Facter.stubs(:value).with("operatingsystem").returns("Debian") + @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") + + @mounter.should be_mounted + end + + it "should not be considered mounted if it did not match the mount output" do + Facter.stubs(:value).with("operatingsystem").returns("Debian") + @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") + + @mounter.should_not be_mounted + end + end +end diff --git a/spec/unit/provider/mount/parsed.rb b/spec/unit/provider/mount/parsed.rb new file mode 100755 index 000000000..21276d911 --- /dev/null +++ b/spec/unit/provider/mount/parsed.rb @@ -0,0 +1,185 @@ +#!/usr/bin/env ruby +# +# Created by Luke Kanies on 2007-9-12. +# Copyright (c) 2006. All rights reserved. + +require File.dirname(__FILE__) + '/../../../../spec_helper' + +require 'puppettest/support/utils' +require 'puppettest/fileparsing' + +module ParsedMountTesting + include PuppetTest::Support::Utils + include PuppetTest::FileParsing + + def fake_fstab + os = Facter['operatingsystem'] + if os == "Solaris" + name = "solaris.fstab" + elsif os == "FreeBSD" + name = "freebsd.fstab" + else + # Catchall for other fstabs + name = "linux.fstab" + end + oldpath = @provider_class.default_target + return fakefile(File::join("data/types/mount", name)) + end + + def mkmountargs + mount = nil + + if defined? @pcount + @pcount += 1 + else + @pcount = 1 + end + args = { + :name => "/fspuppet%s" % @pcount, + :device => "/dev/dsk%s" % @pcount, + } + + @provider_class.fields(:parsed).each do |field| + unless args.include? field + args[field] = "fake%s%s" % [field, @pcount] + end + end + + return args + end + + def mkmount + hash = mkmountargs() + #hash[:provider] = @provider_class.name + + fakeresource = stub :type => :mount, :name => hash[:name] + fakeresource.stubs(:[]).with(:name).returns(hash[:name]) + fakeresource.stubs(:should).with(:target).returns(nil) + + mount = @provider_class.new(fakeresource) + hash[:record_type] = :parsed + hash[:ensure] = :present + mount.property_hash = hash + + return mount + end + + # Here we just create a fake host type that answers to all of the methods + # but does not modify our actual system. + def mkfaketype + @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) + end +end + +provider_class = Puppet::Type.type(:mount).provider(:parsed) + +describe provider_class do + before :each do + @mount_class = Puppet.type(:mount) + @provider_class = @mount_class.provider(:parsed) + end + + + describe provider_class do + include ParsedMountTesting + + it "should be able to parse all of the example mount tabs" do + tab = fake_fstab + @provider = @provider_class + + # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. + # I suppose this is more of an integration test? I dunno. + fakedataparse(tab) do + # Now just make we've got some mounts we know will be there + hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } + (hashes.length > 0).should be_true + root = hashes.find { |i| i[:name] == "/" } + + proc { @provider_class.to_file(hashes) }.should_not raise_error + end + end + + # LAK:FIXME I can't mock Facter because this test happens at parse-time. + it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do + should = case Facter.value(:operatingsystem) + when "Solaris": "/etc/vfstab" + else + "/etc/fstab" + end + Puppet::Type.type(:mount).provider(:parsed).default_target.should == should + end + end + + describe provider_class, " when mounting an absent filesystem" do + include ParsedMountTesting + + # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted + it "should flush the fstab to disk" do + mount = mkmount + + # Mark the mount as absent + mount.property_hash[:ensure] = :absent + + mount.stubs(:mountcmd) # just so we don't actually try to mount anything + + mount.expects(:flush) + mount.mount + end + end + + describe provider_class, " when modifying the filesystem tab" do + include ParsedMountTesting + before do + @mount = mkmount + @target = @provider_class.default_target + + # Never write to disk, only to RAM. + @provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) + end + + it "should write the mount to disk when :flush is called" do + @mount.flush + + text = @provider_class.target_object(@provider_class.default_target).read + text.should == @mount.class.to_line(@mount.property_hash) + "\n" + end + end + + describe provider_class, " when parsing information about the root filesystem" do + confine "Mount type not tested on Darwin" => Facter["operatingsystem"].value != "Darwin" + include ParsedMountTesting + + before do + @mount = @mount_class.create :name => "/" + @provider = @mount.provider + end + + it "should have a filesystem tab" do + FileTest.should be_exist(@provider_class.default_target) + end + + it "should find the root filesystem" do + @provider_class.prefetch("/" => @mount) + @mount.provider.property_hash[:ensure].should == :present + end + + it "should determine that the root fs is mounted" do + @provider_class.prefetch("/" => @mount) + @mount.provider.should be_mounted + end + + after do + Puppet::Type.allclear + end + end + + describe provider_class, " when mounting and unmounting" do + include ParsedMountTesting + + it "should call the 'mount' command to mount the filesystem" + + it "should call the 'unmount' command to unmount the filesystem" + + it "should specify the filesystem when remounting a filesystem" + end +end 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..459001cb5 --- /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/ral/provider/interface/redhat.rb b/spec/unit/ral/provider/interface/redhat.rb deleted file mode 100755 index 9bf1b9722..000000000 --- a/spec/unit/ral/provider/interface/redhat.rb +++ /dev/null @@ -1,268 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-20. -# Copyright (c) 2006. All rights reserved. - -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) - 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) - provider_class.should be_suitable - end -end - -describe provider_class, " when determining the file path" do - it "should always contain '/etc/sysconfig/network-scripts/ifcfg-'" do - provider = provider_class.new(:name => "192.168.0.1") - provider.file_path.should =~ %r{^/etc/sysconfig/network-scripts/ifcfg-} - end - - it "should include the interface name and the description when the interface is an alias" do - provider = provider_class.new(:name => "192.168.0.1", :interface => "eth0") - provider.interface_type = :alias - resource = stub 'resource' - resource.stubs(:[]).with(:interface_desc).returns("blah") - provider.resource = resource - provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-eth0:blah" - end - - it "should just include the description when the interface is not an alias" do - provider = provider_class.new(:name => "192.168.0.1") - provider.interface_type = :normal - resource = stub 'resource' - resource.stubs(:[]).with(:interface_desc).returns("eth0") - provider.resource = resource - provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-eth0" - end - - it "should use the interface description if one is available" do - provider = provider_class.new(:name => "192.168.0.1") - provider.interface_type = :normal - resource = stub 'resource' - resource.stubs(:[]).with(:interface_desc).returns("eth0") - provider.resource = resource - provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-eth0" - end - - it "should use the name if no interface description is available" do - provider = provider_class.new(:name => "192.168.0.1") - provider.interface_type = :normal - provider.file_path.should == "/etc/sysconfig/network-scripts/ifcfg-192.168.0.1" - end - - it "should fail if no name or interface description can be found" do - provider = provider_class.new() - proc { provider.file_path }.should raise_error - end -end - -describe provider_class, " when returning instances" do - it "should consider each file in the network-scripts directory an interface instance" do - Dir.expects(:glob).with("/etc/sysconfig/network-scripts/ifcfg-*").returns(%w{one two}) - one = {:name => "one"} - two = {:name => "two"} - Puppet::Type::Interface::ProviderRedhat.expects(:parse).with("one").returns(one) - Puppet::Type::Interface::ProviderRedhat.expects(:parse).with("two").returns(two) - Puppet::Type::Interface::ProviderRedhat.expects(:new).with(one).returns(:one) - Puppet::Type::Interface::ProviderRedhat.expects(:new).with(two).returns(:two) - Puppet::Type::Interface::ProviderRedhat.instances.should == [:one, :two] - end -end - -describe provider_class, " when parsing" do - it "should return an unmodified provider if the file does not exist" do - FileTest.expects(:exist?).with("/my/file").returns(false) - provider = mock 'provider' - Puppet::Type::Interface::ProviderRedhat.expects(:new).returns(provider) - Puppet::Type::Interface::ProviderRedhat.parse("/my/file").should equal(provider) - end - - it "should set each attribute in the file on the provider" do - FileTest.expects(:exist?).with("/my/file").returns(true) - File.expects(:readlines).with("/my/file").returns(%w{one=two three=four}) - provider = mock 'provider' - Puppet::Type::Interface::ProviderRedhat.expects(:new).returns(provider) - provider.expects(:one=).with('two') - provider.expects(:three=).with('four') - Puppet::Type::Interface::ProviderRedhat.parse("/my/file").should equal(provider) - end -end - -describe provider_class, " when setting the device to a value containing ':'" do - before do - @provider = Puppet::Type::Interface::ProviderRedhat.new - @provider.device = "one:two" - end - it "should set the interface type to :alias" do - @provider.interface_type.should == :alias - end - it "should set the interface to the string to the left of the ':'" do - @provider.interface.should == "one" - end - it "should set the ifnum to the string to the right of the ':'" do - @provider.ifnum.should == "two" - end -end - -describe provider_class, " when setting the device to a value starting with 'dummy-'" do - before do - @provider = Puppet::Type::Interface::ProviderRedhat.new - @provider.device = "dummy5" - end - it "should set the interface type to :loopback" do - @provider.interface_type.should == :loopback - end - it "should set the interface to 'dummy'" do - @provider.interface.should == "dummy" - end - it "should set the ifnum to remainder of value after removing 'dummy'" do - @provider.ifnum.should == "5" - end -end - -describe provider_class, " when setting the device to a value containing neither 'dummy-' nor ':'" do - before do - @provider = Puppet::Type::Interface::ProviderRedhat.new - @provider.device = "whatever" - end - it "should set the interface type to :normal" do - @provider.interface_type.should == :normal - end - it "should set the interface to the device value" do - @provider.interface.should == "whatever" - end -end - -describe provider_class, " when setting the on_boot value" do - before do - @provider = Puppet::Type::Interface::ProviderRedhat.new - end - it "should set it to :true if the value is 'yes'" do - @provider.on_boot = "yes" - @provider.onboot.should == :true - end - it "should set it to :false if the value is not 'yes'" do - @provider.on_boot = "no" - @provider.onboot.should == :false - end -end - -describe provider_class, " when setting the ipaddr value" do - before do - @provider = Puppet::Type::Interface::ProviderRedhat.new - end - - it "should set the name to the provided value" do - @provider.ipaddr = "yay" - @provider.name.should == "yay" - end -end - -describe provider_class, " when generating" do - before do - @provider = Puppet::Type::Interface::ProviderRedhat.new - @provider.interface_type = :alias - @provider.stubs(:device).returns("mydevice") - @provider.stubs(:on_boot).returns("myboot") - @provider.stubs(:name).returns("myname") - @provider.stubs(:netmask).returns("mynetmask") - @provider.interface_type = :alias - - @text = @provider.generate - end - - it "should set the bootproto to none if the interface is an alias" do - @text.should =~ /^BOOTPROTO=none$/ - end - - it "should set the bootproto to static if the interface is a loopback" do - @provider.interface_type = :loopback - @text = @provider.generate - @text.should =~ /^BOOTPROTO=static$/ - end - - it "should set the broadcast address to nothing" do - @text.should =~ /^BROADCAST=$/ - end - - it "should set the netmask to mynetmask" do - @text.should =~ /^NETMASK=mynetmask$/ - end - - it "should set the device to the provider's device" do - @text.should =~ /^DEVICE=mydevice$/ - end - - it "should set the onboot to the provider's on_boot value" do - @text.should =~ /^ONBOOT=myboot$/ - end - - it "should set the ipaddr to the provider's name" do - @text.should =~ /^IPADDR=myname$/ - end -end - -describe provider_class, " when creating and destroying" do - before do - @provider = provider_class.new(:interface => "eth0", :name => "testing") - @path = "/etc/sysconfig/network-scripts/ifcfg-testing" - end - - it "should consider the interface present if the file exists" do - FileTest.expects(:exist?).with(@path).returns(true) - @provider.should be_exists - end - - it "should consider the interface absent if the file does not exist" do - FileTest.expects(:exist?).with(@path).returns(false) - @provider.should_not be_exists - end - - it "should remove the file if the interface is being destroyed" do - File.expects(:unlink).with(@path) - @provider.destroy - end - - it "should mark :ensure as :absent if the interface is destroyed" do - File.stubs(:unlink) - @provider.destroy - @provider.ensure.should == :absent - end - - it "should mark :ensure as :present if the interface is being created" do - resource = stub 'resource', :name => 'testing' - resource.stubs(:should).with { |name| name == :ensure }.returns(:present) - resource.stubs(:should).with { |name| name != :ensure }.returns(nil) - @provider.resource = resource - @provider.create - @provider.ensure.should == :present - end - - it "should write the generated text to disk when the interface is flushed" do - fh = mock("filehandle") - File.expects(:open).yields(fh) - fh.expects(:puts).with("generated") - resource = stub 'resource', :name => 'testing' - resource.stubs(:[]).with(:interface_desc).returns(nil) - resource.stubs(:should).with { |name| name == :ensure }.returns(:present) - resource.stubs(:should).with { |name| name != :ensure }.returns(nil) - @provider.resource = resource - @provider.create - - @provider.stubs(:generate).returns("generated") - @provider.flush - end - - it "should not write the generated text to disk when the interface is flushed if :ensure == :absent" do - @provider.ensure = :absent - @provider.flush - end -end diff --git a/spec/unit/ral/provider/interface/sunos.rb b/spec/unit/ral/provider/interface/sunos.rb deleted file mode 100755 index 7b9f462e6..000000000 --- a/spec/unit/ral/provider/interface/sunos.rb +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-11-25. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../../spec_helper' - -require 'puppet/provider/interface/sunos' - - -provider_class = Puppet::Type.type(:interface).provider(:sunos) - -describe provider_class do - it "should not be functional on non-SunOS kernels" do - Facter.expects(:value).with(:kernel).returns("Linux") - provider_class.should_not be_suitable - end - - it "should be functional on SunOS kernels" do - Facter.expects(:value).with(:kernel).returns("SunOS") - provider_class.should be_suitable - end - - it "should pick its file path by combining '/etc/hostname.' with the interface if one is set" do - provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing", :interface => 'eth0') - provider.file_path.should == "/etc/hostname.eth0" - end - - it "should pick its file path by combining '/etc/hostname.' with the resource's interface if one is not set in the provider" do - provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") - resource = mock 'resource' - resource.stubs(:[]).with(:interface).returns("eth0") - provider.resource = resource - provider.file_path.should == "/etc/hostname.eth0" - end - - it "should fail when picking its file path if there is no resource nor an interface set in the provider" do - provider = provider_class.new(:record_type => :sunos, :interface_type => :normal, :name => "testing") - proc { provider.file_path }.should raise_error(Puppet::Error) - end -end - -describe provider_class, " when listing interfaces" do - it "should return an instance for every file matching /etc/hostname.*, created with the interface name set from the file" do - Dir.expects(:glob).with("/etc/hostname.*").returns(%w{/etc/hostname.one /etc/hostname.two}) - one_instance = stub 'one_instance', :parse => nil - two_instance = stub 'two_instance', :parse => nil - provider_class.expects(:new).with(:interface => "one").returns(one_instance) - provider_class.expects(:new).with(:interface => "two").returns(two_instance) - - provider_class.instances.should == [one_instance, two_instance] - end - - it "should call parse on each instance being returned" do - Dir.expects(:glob).with("/etc/hostname.*").returns(%w{/etc/hostname.one}) - one_instance = mock 'one_instance' - provider_class.expects(:new).with(:interface => "one").returns(one_instance) - - one_instance.expects(:parse) - - provider_class.instances - end - - it "should assign matching providers to any prefetched instances" do - Dir.expects(:glob).with("/etc/hostname.*").returns(%w{one two}) - one_instance = stub 'one_instance', :name => "one", :parse => nil - two_instance = stub 'two_instance', :name => "two", :parse => nil - provider_class.expects(:new).with(:interface => "one").returns(one_instance) - provider_class.expects(:new).with(:interface => "two").returns(two_instance) - - resources = {"one" => mock("one"), "three" => mock('three')} - resources["one"].expects(:provider=).with(one_instance) - - provider_class.prefetch(resources) - end -end - -describe provider_class, " when creating and destroying" do - before do - @provider = provider_class.new(:interface => "eth0", :name => "testing") - end - - it "should consider the interface present if the file exists" do - FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(true) - @provider.should be_exists - end - - it "should consider the interface absent if the file does not exist" do - FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(false) - @provider.should_not be_exists - end - - it "should remove the file if the interface is being destroyed" do - File.expects(:unlink).with("/etc/hostname.eth0") - @provider.destroy - end - - it "should mark :ensure as :absent if the interface is destroyed" do - File.stubs(:unlink) - @provider.destroy - @provider.ensure.should == :absent - end - - it "should mark :ensure as :present if the interface is being created" do - resource = stub 'resource', :name => 'testing' - resource.stubs(:should).with { |name| name == :ensure }.returns(:present) - resource.stubs(:should).with { |name| name != :ensure }.returns(nil) - @provider.resource = resource - @provider.create - @provider.ensure.should == :present - end - - it "should write the generated text to disk when the interface is flushed" do - fh = mock("filehandle") - File.expects(:open).yields(fh) - fh.expects(:print).with("testing\n") - resource = stub 'resource', :name => 'testing' - resource.stubs(:should).with { |name| name == :ensure }.returns(:present) - resource.stubs(:should).with { |name| name != :ensure }.returns(nil) - @provider.resource = resource - @provider.create - @provider.flush - end - - it "should not write the generated text to disk when the interface is flushed if :ensure == :absent" do - @provider.ensure = :absent - @provider.flush - end -end - -describe provider_class, " when parsing a non-existant file" do - it "should mark the interface as absent" do - @provider = provider_class.new(:interface => "eth0", :name => "testing") - FileTest.expects(:exist?).with("/etc/hostname.eth0").returns(false) - @provider.parse - @provider.ensure.should == :absent - end -end - -describe provider_class, " when parsing an existing file" do - before do - @provider = provider_class.new(:interface => "eth0", :name => "testing") - FileTest.stubs(:exist?).with("/etc/hostname.eth0").returns(true) - end - - def set_text(text) - File.stubs(:read).with("/etc/hostname.eth0").returns(text) - end - - it "should retain the interface name" do - set_text "testing" - @provider.parse - @provider.ensure.should == :present - @provider.interface.should == "eth0" - end - - it "should mark the interface as present" do - set_text "testing" - @provider.parse - @provider.ensure.should == :present - end - - it "should mark the interface as an alias if the first word is 'addif'" do - set_text "addif testing" - @provider.parse - @provider.interface_type.should == :alias - end - - it "should not mark the interface as normal if the first word is not 'addif'" do - set_text "testing" - @provider.parse - @provider.interface_type.should == :normal - end - - it "should start the interface on boot of the last word is 'up'" do - set_text "testing up" - @provider.parse - @provider.onboot.should == :true - end - - it "should not start the interface on boot of the last word is not 'up'" do - set_text "testing" - @provider.parse - @provider.onboot.should == :false - end - - it "should set the interface to the first non-behavioural word" do - set_text "addif testing up" - @provider.parse - @provider.name.should == "testing" - end - - it "should consider any remaining terms to be interface options" do - set_text "addif testing -O up" - @provider.parse - @provider.ifopts.should == "-O" - end -end - -describe provider_class, " when generating" do - before do - @provider = provider_class.new(:interface => "eth0", :name => "testing") - end - - it "should prefix the text with 'addif' if the interface is an alias" do - @provider.interface_type = :alias - @provider.generate.should == "addif testing" - end - - it "should not prefix the text with 'addif' if the interface is not an alias" do - @provider.generate.should == "testing" - end - - it "should put the ifopts after the name if they are present" do - @provider.ifopts = "-O" - @provider.generate.should == "testing -O" - end - - it "should mark the interface up if onboot is enabled" do - @provider.onboot = :true - @provider.generate.should == "testing up" - end - - it "should use the resource name if no provider name is present" do - provider = provider_class.new(:interface => "eth0") - resource = stub 'resource', :name => "rtest" - provider.resource = resource - provider.generate.should == "rtest" - end - - it "should use the provider name if present" do - @provider.generate.should == "testing" - end - - it "should fail if neither a resource nor the provider name is present" do - provider = provider_class.new(:interface => "eth0") - proc { provider.generate }.should raise_error - end -end diff --git a/spec/unit/ral/provider/mount.rb b/spec/unit/ral/provider/mount.rb deleted file mode 100755 index 0b90d53c9..000000000 --- a/spec/unit/ral/provider/mount.rb +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/provider/mount' - -describe Puppet::Provider::Mount do - before :each do - @mounter = Object.new - @mounter.extend(Puppet::Provider::Mount) - - @name = "/" - - @resource = stub 'resource' - @resource.stubs(:[]).with(:name).returns(@name) - - @mounter.stubs(:resource).returns(@resource) - end - - describe Puppet::Provider::Mount, " when mounting" do - - it "should use the 'mountcmd' method to mount" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd) - - @mounter.mount - end - - it "should flush before mounting if a flush method exists" do - @mounter.meta_def(:flush) { } - @mounter.expects(:flush) - @mounter.stubs(:mountcmd) - @mounter.stubs(:options).returns(nil) - - @mounter.mount - end - - it "should add the options following '-o' if they exist and are not set to :absent" do - @mounter.stubs(:options).returns("ro") - @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } - - @mounter.mount - end - - it "should specify the filesystem name to the mount command" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } - - @mounter.mount - end - end - - describe Puppet::Provider::Mount, " when remounting" do - - it "should use '-o remount' if the resource specifies it supports remounting" do - @mounter.stubs(:info) - @resource.stubs(:[]).with(:remounts).returns(:true) - @mounter.expects(:mountcmd).with("-o", "remount", @name) - @mounter.remount - end - - it "should unmount and mount if the resource does not specify it supports remounting" do - @mounter.stubs(:info) - @resource.stubs(:[]).with(:remounts).returns(false) - @mounter.expects(:unmount) - @mounter.expects(:mount) - @mounter.remount - end - - it "should log that it is remounting" do - @resource.stubs(:[]).with(:remounts).returns(:true) - @mounter.stubs(:mountcmd) - @mounter.expects(:info).with("Remounting") - @mounter.remount - end - end - - describe Puppet::Provider::Mount, " when unmounting" do - - it "should call the :umount command with the resource name" do - @mounter.expects(:umount).with(@name) - @mounter.unmount - end - end - - describe Puppet::Provider::Mount, " when determining if it is mounted" do - - it "should parse the results of running the mount command with no arguments" do - Facter.stubs(:value).returns("whatever") - @mounter.expects(:mountcmd).returns("") - - @mounter.mounted? - end - - it "should match ' on /private/var/automount' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/whatever on /private/var/automount/\ndevfs on /dev") - - @mounter.should be_mounted - end - - it "should match ' on ' if the operating system is Darwin" do - Facter.stubs(:value).with("operatingsystem").returns("Darwin") - @mounter.expects(:mountcmd).returns("/dev/disk03 on / (local, journaled)\ndevfs on /dev") - - @mounter.should be_mounted - end - - it "should match '^ on' if the operating system is Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Solaris") - @mounter.expects(:mountcmd).returns("/ on /dev/dsk/whatever\n/var on /dev/dsk/other") - - @mounter.should be_mounted - end - - it "should match ' on ' if the operating system is not Darwin or Solaris" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on / and stuff\n/dev/other/disk on /var and stuff") - - @mounter.should be_mounted - end - - it "should not be considered mounted if it did not match the mount output" do - Facter.stubs(:value).with("operatingsystem").returns("Debian") - @mounter.expects(:mountcmd).returns("/dev/dsk/whatever on /something/else and stuff\n/dev/other/disk on /var and stuff") - - @mounter.should_not be_mounted - end - end -end diff --git a/spec/unit/ral/provider/mount/parsed.rb b/spec/unit/ral/provider/mount/parsed.rb deleted file mode 100755 index 21276d911..000000000 --- a/spec/unit/ral/provider/mount/parsed.rb +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env ruby -# -# Created by Luke Kanies on 2007-9-12. -# Copyright (c) 2006. All rights reserved. - -require File.dirname(__FILE__) + '/../../../../spec_helper' - -require 'puppettest/support/utils' -require 'puppettest/fileparsing' - -module ParsedMountTesting - include PuppetTest::Support::Utils - include PuppetTest::FileParsing - - def fake_fstab - os = Facter['operatingsystem'] - if os == "Solaris" - name = "solaris.fstab" - elsif os == "FreeBSD" - name = "freebsd.fstab" - else - # Catchall for other fstabs - name = "linux.fstab" - end - oldpath = @provider_class.default_target - return fakefile(File::join("data/types/mount", name)) - end - - def mkmountargs - mount = nil - - if defined? @pcount - @pcount += 1 - else - @pcount = 1 - end - args = { - :name => "/fspuppet%s" % @pcount, - :device => "/dev/dsk%s" % @pcount, - } - - @provider_class.fields(:parsed).each do |field| - unless args.include? field - args[field] = "fake%s%s" % [field, @pcount] - end - end - - return args - end - - def mkmount - hash = mkmountargs() - #hash[:provider] = @provider_class.name - - fakeresource = stub :type => :mount, :name => hash[:name] - fakeresource.stubs(:[]).with(:name).returns(hash[:name]) - fakeresource.stubs(:should).with(:target).returns(nil) - - mount = @provider_class.new(fakeresource) - hash[:record_type] = :parsed - hash[:ensure] = :present - mount.property_hash = hash - - return mount - end - - # Here we just create a fake host type that answers to all of the methods - # but does not modify our actual system. - def mkfaketype - @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - end -end - -provider_class = Puppet::Type.type(:mount).provider(:parsed) - -describe provider_class do - before :each do - @mount_class = Puppet.type(:mount) - @provider_class = @mount_class.provider(:parsed) - end - - - describe provider_class do - include ParsedMountTesting - - it "should be able to parse all of the example mount tabs" do - tab = fake_fstab - @provider = @provider_class - - # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. - # I suppose this is more of an integration test? I dunno. - fakedataparse(tab) do - # Now just make we've got some mounts we know will be there - hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } - (hashes.length > 0).should be_true - root = hashes.find { |i| i[:name] == "/" } - - proc { @provider_class.to_file(hashes) }.should_not raise_error - end - end - - # LAK:FIXME I can't mock Facter because this test happens at parse-time. - it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do - should = case Facter.value(:operatingsystem) - when "Solaris": "/etc/vfstab" - else - "/etc/fstab" - end - Puppet::Type.type(:mount).provider(:parsed).default_target.should == should - end - end - - describe provider_class, " when mounting an absent filesystem" do - include ParsedMountTesting - - # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted - it "should flush the fstab to disk" do - mount = mkmount - - # Mark the mount as absent - mount.property_hash[:ensure] = :absent - - mount.stubs(:mountcmd) # just so we don't actually try to mount anything - - mount.expects(:flush) - mount.mount - end - end - - describe provider_class, " when modifying the filesystem tab" do - include ParsedMountTesting - before do - @mount = mkmount - @target = @provider_class.default_target - - # Never write to disk, only to RAM. - @provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - end - - it "should write the mount to disk when :flush is called" do - @mount.flush - - text = @provider_class.target_object(@provider_class.default_target).read - text.should == @mount.class.to_line(@mount.property_hash) + "\n" - end - end - - describe provider_class, " when parsing information about the root filesystem" do - confine "Mount type not tested on Darwin" => Facter["operatingsystem"].value != "Darwin" - include ParsedMountTesting - - before do - @mount = @mount_class.create :name => "/" - @provider = @mount.provider - end - - it "should have a filesystem tab" do - FileTest.should be_exist(@provider_class.default_target) - end - - it "should find the root filesystem" do - @provider_class.prefetch("/" => @mount) - @mount.provider.property_hash[:ensure].should == :present - end - - it "should determine that the root fs is mounted" do - @provider_class.prefetch("/" => @mount) - @mount.provider.should be_mounted - end - - after do - Puppet::Type.allclear - end - end - - describe provider_class, " when mounting and unmounting" do - include ParsedMountTesting - - it "should call the 'mount' command to mount the filesystem" - - it "should call the 'unmount' command to unmount the filesystem" - - it "should specify the filesystem when remounting a filesystem" - end -end diff --git a/spec/unit/ral/provider/ssh_authorized_key/parsed.rb b/spec/unit/ral/provider/ssh_authorized_key/parsed.rb deleted file mode 100755 index 459001cb5..000000000 --- a/spec/unit/ral/provider/ssh_authorized_key/parsed.rb +++ /dev/null @@ -1,74 +0,0 @@ -#!/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/ral/type.rb b/spec/unit/ral/type.rb deleted file mode 100755 index 5980167d6..000000000 --- a/spec/unit/ral/type.rb +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Type, " when in a configuration" do - before do - @catalog = Puppet::Node::Catalog.new - @container = Puppet::Type.type(:component).create(:name => "container") - @one = Puppet::Type.type(:file).create(:path => "/file/one") - @two = Puppet::Type.type(:file).create(:path => "/file/two") - @catalog.add_resource @container - @catalog.add_resource @one - @catalog.add_resource @two - @catalog.add_edge @container, @one - @catalog.add_edge @container, @two - end - - it "should have no parent if there is no in edge" do - @container.parent.should be_nil - end - - it "should set its parent to its in edge" do - @one.parent.ref.should == @container.ref - end - - after do - @catalog.clear(true) - end -end diff --git a/spec/unit/ral/type/exec.rb b/spec/unit/ral/type/exec.rb deleted file mode 100755 index 260804227..000000000 --- a/spec/unit/ral/type/exec.rb +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/exec' - -module ExecModuleTesting - def create_resource(command, output, exitstatus) - @user_name = 'some_user_name' - @group_name = 'some_group_name' - Puppet.features.stubs(:root?).returns(true) - @execer = Puppet::Type.type(:exec).create(:name => command, :path => %w{/usr/bin /bin}, :user => @user_name, :group => @group_name) - - status = stub "process" - status.stubs(:exitstatus).returns(exitstatus) - - Puppet::Util::SUIDManager.expects(:run_and_capture).with([command], @user_name, @group_name).returns([output, status]) - end - - def create_logging_resource(command, output, exitstatus, logoutput, loglevel) - create_resource(command, output, exitstatus) - @execer[:logoutput] = logoutput - @execer[:loglevel] = loglevel - end - - def expect_output(output, loglevel) - output.split(/\n/).each do |line| - @execer.property(:returns).expects(loglevel).with(line) - end - end -end - -describe Puppet::Type::Exec, " when execing" do - include ExecModuleTesting - - it "should use the 'run_and_capture' method to exec" do - command = "true" - create_resource(command, "", 0) - - @execer.refresh.should == :executed_command - end - - it "should report a failure" do - command = "false" - create_resource(command, "", 1) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - it "should log the output on success" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 0, true, :err) - expect_output(output, :err) - @execer.refresh - end - - it "should log the output on failure" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 1, true, :err) - expect_output(output, :err) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - -end - - -describe Puppet::Type::Exec, " when logoutput=>on_failure is set," do - include ExecModuleTesting - - it "should log the output on failure" do - #Puppet::Util::Log.newdestination :console - command = "false" - output = "output1\noutput2\n" - create_logging_resource(command, output, 1, :on_failure, :err) - expect_output(output, :err) - - proc { @execer.refresh }.should raise_error(Puppet::Error) - end - - it "shouldn't log the output on success" do - #Puppet::Util::Log.newdestination :console - command = "true" - output = "output1\noutput2\n" - create_logging_resource(command, output, 0, :on_failure, :err) - @execer.property(:returns).expects(:err).never - @execer.refresh - end -end diff --git a/spec/unit/ral/type/file.rb b/spec/unit/ral/type/file.rb deleted file mode 100755 index e1a597434..000000000 --- a/spec/unit/ral/type/file.rb +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -describe Puppet::Type.type(:file) do - before do - @path = Tempfile.new("puppetspec") - @path.close!() - @path = @path.path - @file = Puppet::Type::File.create(:name => @path) - end - - describe "when used with content and replace=>false" do - before do - @file[:content] = "foo" - @file[:replace] = false - end - - it "should be insync if the file exists and the content is different" do - File.open(@path, "w") do |f| f.puts "bar" end - @file.property(:content).insync?("bar").should be_true - end - - it "should be insync if the file exists and the content is right" do - File.open(@path, "w") do |f| f.puts "foo" end - @file.property(:content).insync?("foo").should be_true - end - - it "should not be insync if the file does not exist" do - @file.property(:content).insync?(:nil).should be_false - end - end - - describe "when retrieving remote files" do - before do - @filesource = Puppet::Type::File::FileSource.new - @filesource.server = mock 'fileserver' - - @file.stubs(:uri2obj).returns(@filesource) - - @file[:source] = "puppet:///test" - end - - it "should fail without writing if it cannot retrieve remote contents" do - # create the file, because we only get the problem when it starts - # out absent. - File.open(@file[:path], "w") { |f| f.puts "a" } - @file.expects(:write).never - - @filesource.server.stubs(:describe).returns("493\tfile\t100\t0\t{md5}3f5fef3bddbc4398c46a7bd7ba7b3af7") - @filesource.server.stubs(:retrieve).raises(RuntimeError) - @file.property(:source).retrieve - lambda { @file.property(:source).sync }.should raise_error(Puppet::Error) - end - end - - describe "when managing links" do - require 'puppettest/support/assertions' - include PuppetTest - require 'tempfile' - - before do - @basedir = tempfile - Dir.mkdir(@basedir) - @file = File.join(@basedir, "file") - @link = File.join(@basedir, "link") - - File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } - File.symlink(@file, @link) - - @resource = Puppet.type(:file).create( - :path => @link, - :mode => "755" - ) - @catalog = Puppet::Node::Catalog.new - @catalog.add_resource @resource - end - - after do - remove_tmp_files - end - - it "should default to managing the link" do - @catalog.apply - # I convert them to strings so they display correctly if there's an error. - ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 - end - - it "should be able to follow links" do - @resource[:links] = :follow - @catalog.apply - - ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 - end - end - - after do - Puppet::Type::File.clear - end -end diff --git a/spec/unit/ral/type/interface.rb b/spec/unit/ral/type/interface.rb deleted file mode 100755 index 2e0176152..000000000 --- a/spec/unit/ral/type/interface.rb +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -interface = Puppet::Type.type(:interface) - -describe interface do - before do - @class = Puppet::Type.type(:interface) - - @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 interface_type property" do - @class.attrtype(:interface_type).should == :property - end - it "should support :loopback as an interface_type value" do - proc { @class.create(:name => "whev", :interface_type => :loopback) }.should_not raise_error - end - it "should support :alias as an interface_type value" do - proc { @class.create(:name => "whev", :interface_type => :alias) }.should_not raise_error - end - it "should support :normal as an interface_type value" do - proc { @class.create(:name => "whev", :interface_type => :normal) }.should_not raise_error - end - it "should alias :dummy to the :loopback interface_type value" do - int = @class.create(:name => "whev", :interface_type => :dummy) - int.should(:interface_type).should == :loopback - end - - it "should not support values other than :loopback, :alias, :normal, and :dummy in the interface_type" do - proc { @class.create(:name => "whev", :interface_type => :something) }.should raise_error(Puppet::Error) - end - - it "should have an interface_desc parameter" do - @class.attrtype(:interface_desc).should == :param - end - - it "should have an onboot property" do - @class.attrtype(:onboot).should == :property - end - it "should support :true as an onboot value" do - proc { @class.create(:name => "whev", :onboot => :true) }.should_not raise_error - end - it "should support :false as an onboot value" do - proc { @class.create(:name => "whev", :onboot => :false) }.should_not raise_error - end - - it "should have an ifnum property" do - @class.attrtype(:ifnum).should == :property - end - - it "should have a netmask property" do - @class.attrtype(:netmask).should == :property - end - - it "should have an ifopts property" do - @class.attrtype(:ifopts).should == :property - end - - it "should have a target parameter" do - @class.attrtype(:target).should == :param - end - - after { @class.clear } -end diff --git a/spec/unit/ral/type/mount.rb b/spec/unit/ral/type/mount.rb deleted file mode 100755 index 8fa2e6f7c..000000000 --- a/spec/unit/ral/type/mount.rb +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/mount' - -describe Puppet::Type::Mount do - it "should have a :refreshable feature that requires the :remount method" do - Puppet::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.should(:ensure).should be_nil - end - - after { Puppet::Type::Mount.clear } -end - -describe Puppet::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 - 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 - end - end -end - -describe Puppet::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) - end - - it "should support :present as a value to :ensure" do - Puppet::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.should(:ensure).should == :present - end - - it "should support :absent as a value to :ensure" do - Puppet::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) - end - - after { Puppet::Type::Mount.clear } -end - -describe Puppet::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) - - @ensure = @mount.property(:ensure) - end - - after :each do - Puppet::Type::Mount.clear - end - - def mount_stub(params) - Puppet::Type::Mount.validproperties.each do |prop| - unless params[prop] - params[prop] = :absent - @mount[prop] = :absent - end - end - - params.each do |param, value| - @provider.stubs(param).returns(value) - end - end - - describe Puppet::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) - @ensure.retrieve.should == :absent - end - - it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(true) - @ensure.retrieve.should == :mounted - end - - it "should return :present if the provider indicates it is not mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:mounted?).returns(false) - @ensure.retrieve.should == :present - end - end - - describe Puppet::Type::Mount::Ensure, "when changing the host" do - - it "should destroy itself if it should be absent" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should unmount itself before destroying if it is mounted and should be absent" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:unmount) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end - - it "should create itself if it is absent and should be present" do - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.should = :present - @ensure.sync - end - - it "should unmount itself if it is mounted and should be present" do - @provider.stubs(:mounted?).returns(true) - - # The interface here is just too much work to test right now. - @ensure.stubs(:syncothers) - @provider.expects(:unmount) - @ensure.should = :present - @ensure.sync - end - - it "should create and mount itself if it does not exist and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(false) - @provider.expects(:create) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - - it "should mount itself if it is present and should be mounted" do - @provider.stubs(:ensure).returns(:present) - @provider.stubs(:mounted?).returns(false) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end - - it "should create but not mount itself if it is absent and mounted and should be mounted" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:mounted?).returns(true) - @ensure.stubs(:syncothers) - @provider.expects(:create) - @ensure.should = :mounted - @ensure.sync - end - end - - describe Puppet::Type::Mount, "when responding to events" do - - it "should remount if it is currently mounted" do - @provider.expects(:mounted?).returns(true) - @provider.expects(:remount) - - @mount.refresh - end - - it "should not remount if it is not currently mounted" do - @provider.expects(:mounted?).returns(false) - @provider.expects(:remount).never - - @mount.refresh - end - - it "should not remount swap filesystems" do - @mount[:fstype] = "swap" - @provider.expects(:remount).never - - @mount.refresh - end - end -end diff --git a/spec/unit/ral/type/nagios.rb b/spec/unit/ral/type/nagios.rb deleted file mode 100755 index 35f00b0e5..000000000 --- a/spec/unit/ral/type/nagios.rb +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/external/nagios' - -describe "Nagios resource types" do - Nagios::Base.eachtype do |name, nagios_type| - puppet_type = Puppet::Type.type("nagios_" + name.to_s) - - it "should have a valid type for #{name}" do - puppet_type.should_not be_nil - end - - next unless puppet_type - - describe puppet_type do - it "should be defined as a Puppet resource type" do - puppet_type.should_not be_nil - end - - it "should have documentation" do - puppet_type.instance_variable_get("@doc").should_not == "" - end - - it "should have %s as its namevar" % nagios_type.namevar do - puppet_type.namevar.should == nagios_type.namevar - end - - it "should have documentation for its %s parameter" % nagios_type.namevar do - puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil - end - - it "should have an ensure property" do - puppet_type.should be_validproperty(:ensure) - end - - it "should have a target property" do - puppet_type.should be_validproperty(:target) - end - - it "should have documentation for its target property" do - puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil - end - - nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| - it "should have a %s property" % param do - puppet_type.should be_validproperty(param) - end - - it "should have documentation for its %s property" % param do - puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil - end - end - - nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| - it "should have not have a %s property" % param do - puppet_type.should_not be_validproperty(:param) - end - end - end - end -end diff --git a/spec/unit/ral/type/noop_metaparam.rb b/spec/unit/ral/type/noop_metaparam.rb deleted file mode 100755 index 0cbed3714..000000000 --- a/spec/unit/ral/type/noop_metaparam.rb +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/metatype/metaparams' - -describe Puppet::Type.type(:file).attrclass(:noop) do - before do - @file = Puppet::Type.newfile :path => "/what/ever" - end - - after { Puppet::Type::File.clear } - - it "should accept true as a value" do - lambda { @file[:noop] = true }.should_not raise_error - end - - it "should accept false as a value" do - lambda { @file[:noop] = false }.should_not raise_error - end - - describe "when set on a resource" do - it "should default to the :noop setting" do - Puppet.settings.expects(:value).with(:noop).returns "myval" - @file.noop.should == "myval" - end - - it "should prefer true values from the attribute" do - @file[:noop] = true - @file.noop.should be_true - end - - it "should prefer false values from the attribute" do - @file[:noop] = false - @file.noop.should be_false - end - end -end diff --git a/spec/unit/ral/type/package.rb b/spec/unit/ral/type/package.rb deleted file mode 100755 index 5d96dc4ae..000000000 --- a/spec/unit/ral/type/package.rb +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/package' - -describe Puppet::Type::Package do - it "should have an :installable feature that requires the :install method" do - Puppet::Type::Package.provider_feature(:installable).methods.should == [:install] - end - - it "should have an :uninstallable feature that requires the :uninstall method" do - Puppet::Type::Package.provider_feature(:uninstallable).methods.should == [:uninstall] - end - - it "should have an :upgradeable feature that requires :update and :latest methods" do - Puppet::Type::Package.provider_feature(:upgradeable).methods.should == [:update, :latest] - end - - it "should have a :purgeable feature that requires the :purge latest method" do - Puppet::Type::Package.provider_feature(:purgeable).methods.should == [:purge] - end - - it "should have a :versionable feature" do - Puppet::Type::Package.provider_feature(:versionable).should_not be_nil - end - - it "should default to being installed" do - pkg = Puppet::Type::Package.create(:name => "yay") - pkg.should(:ensure).should == :present - end - - after { Puppet::Type::Package.clear } -end - -describe Puppet::Type::Package, "when validating attributes" do - [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| - it "should have a #{param} parameter" do - Puppet::Type::Package.attrtype(param).should == :param - end - end - - it "should have an ensure property" do - Puppet::Type::Package.attrtype(:ensure).should == :property - end -end - -describe Puppet::Type::Package, "when validating attribute values" do - before do - @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil - Puppet::Type::Package.defaultprovider.expects(:new).returns(@provider) - end - - it "should support :present as a value to :ensure" do - Puppet::Type::Package.create(:name => "yay", :ensure => :present) - end - - it "should alias :installed to :present as a value to :ensure" do - pkg = Puppet::Type::Package.create(:name => "yay", :ensure => :installed) - pkg.should(:ensure).should == :present - end - - it "should support :absent as a value to :ensure" do - Puppet::Type::Package.create(:name => "yay", :ensure => :absent) - end - - it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do - @provider.expects(:satisfies?).with(:purgeable).returns(true) - Puppet::Type::Package.create(:name => "yay", :ensure => :purged) - end - - it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do - @provider.expects(:satisfies?).with(:purgeable).returns(false) - proc { Puppet::Type::Package.create(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) - end - - it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do - @provider.expects(:satisfies?).with(:upgradeable).returns(true) - Puppet::Type::Package.create(:name => "yay", :ensure => :latest) - end - - it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do - @provider.expects(:satisfies?).with(:upgradeable).returns(false) - proc { Puppet::Type::Package.create(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) - end - - it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do - @provider.expects(:satisfies?).with(:versionable).returns(true) - Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") - end - - it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do - @provider.expects(:satisfies?).with(:versionable).returns(false) - proc { Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) - end - - it "should accept any string as an argument to :source" do - proc { Puppet::Type::Package.create(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) - end - - after { Puppet::Type::Package.clear } -end - -module PackageEvaluationTesting - def setprops(properties) - @provider.stubs(:properties).returns(properties) - end -end - -describe Puppet::Type::Package do - before :each do - @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock - Puppet::Type::Package.defaultprovider.stubs(:new).returns(@provider) - @package = Puppet::Type::Package.create(:name => "yay") - - @catalog = Puppet::Node::Catalog.new - @catalog.add_resource(@package) - end - - after :each do - @catalog.clear(true) - Puppet::Type::Package.clear - end - - - describe Puppet::Type::Package, "when it should be purged" do - include PackageEvaluationTesting - - before { @package[:ensure] = :purged } - - it "should do nothing if it is :purged" do - @provider.expects(:properties).returns(:ensure => :purged) - @catalog.apply - end - - [:absent, :installed, :present, :latest].each do |state| - it "should purge if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:purge) - @catalog.apply - end - end - end - - describe Puppet::Type::Package, "when it should be absent" do - include PackageEvaluationTesting - - before { @package[:ensure] = :absent } - - [:purged, :absent].each do |state| - it "should do nothing if it is #{state.to_s}" do - @provider.expects(:properties).returns(:ensure => state) - @catalog.apply - end - end - - [:installed, :present, :latest].each do |state| - it "should uninstall if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:uninstall) - @catalog.apply - end - end - end - - describe Puppet::Type::Package, "when it should be present" do - include PackageEvaluationTesting - - before { @package[:ensure] = :present } - - [:present, :latest, "1.0"].each do |state| - it "should do nothing if it is #{state.to_s}" do - @provider.expects(:properties).returns(:ensure => state) - @catalog.apply - end - end - - [:purged, :absent].each do |state| - it "should install if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:install) - @catalog.apply - end - end - end - - describe Puppet::Type::Package, "when it should be latest" do - include PackageEvaluationTesting - - before { @package[:ensure] = :latest } - - [:purged, :absent].each do |state| - it "should upgrade if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:update) - @catalog.apply - end - end - - it "should upgrade if the current version is not equal to the latest version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.stubs(:latest).returns("2.0") - @provider.expects(:update) - @catalog.apply - end - - it "should do nothing if it is equal to the latest version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.stubs(:latest).returns("1.0") - @provider.expects(:update).never - @catalog.apply - end - - it "should do nothing if the provider returns :present as the latest version" do - @provider.stubs(:properties).returns(:ensure => :present) - @provider.stubs(:latest).returns("1.0") - @provider.expects(:update).never - @catalog.apply - end - end - - describe Puppet::Type::Package, "when it should be a specific version" do - include PackageEvaluationTesting - - before { @package[:ensure] = "1.0" } - - [:purged, :absent].each do |state| - it "should install if it is #{state.to_s}" do - @provider.stubs(:properties).returns(:ensure => state) - @provider.expects(:install) - @catalog.apply - end - end - - it "should do nothing if the current version is equal to the desired version" do - @provider.stubs(:properties).returns(:ensure => "1.0") - @provider.expects(:install).never - @catalog.apply - end - - it "should install if the current version is not equal to the specified version" do - @provider.stubs(:properties).returns(:ensure => "2.0") - @provider.expects(:install) - @catalog.apply - end - end -end diff --git a/spec/unit/ral/type/schedule.rb b/spec/unit/ral/type/schedule.rb deleted file mode 100755 index 4e9840c34..000000000 --- a/spec/unit/ral/type/schedule.rb +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/schedule' - -module ScheduleTesting - - def format(time) - time.strftime("%H:%M:%S") - end - - def diff(unit, incr, method, count) - diff = Time.now.to_i.send(method, incr * count) - Time.at(diff) - end - - def month(method, count) - diff(:hour, 3600 * 24 * 30, method, count) - end - - def week(method, count) - diff(:hour, 3600 * 24 * 7, method, count) - end - - def day(method, count) - diff(:hour, 3600 * 24, method, count) - end - - def hour(method, count) - diff(:hour, 3600, method, count) - end - - def min(method, count) - diff(:min, 60, method, count) - end - - def sec(method, count) - diff(:sec, 1, method, count) - end - -end - -describe Puppet::Type::Schedule do - - before :each do - Puppet.settings.stubs(:value).with(:ignoreschedules).returns(false) - - @schedule = Puppet::Type::Schedule.create(:name => "testing") - end - - after :each do - Puppet::Type::Schedule.clear - end - - - describe Puppet::Type::Schedule do - include ScheduleTesting - - it "should default to :distance for period-matching" do - @schedule[:periodmatch].should == :distance - end - - it "should default to a :repeat of 1" do - @schedule[:repeat].should == 1 - end - - it "should never match when the period is :never" do - @schedule[:period] = :never - @schedule.match?.should be_false - end - end - - describe Puppet::Type::Schedule, "when producing default schedules" do - include ScheduleTesting - - %w{hourly daily weekly monthly never}.each do |period| - period = period.to_sym - it "should produce a #{period} schedule with the period set appropriately" do - schedules = Puppet::Type::Schedule.mkdefaultschedules - schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type::Schedule) - end - end - - it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do - schedules = Puppet::Type::Schedule.mkdefaultschedules - schedules.find { |s| - s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 - }.should be_instance_of(Puppet::Type::Schedule) - end - end - - describe Puppet::Type::Schedule, "when matching ranges" do - include ScheduleTesting - - it "should match when the start time is before the current time and the end time is after the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now + 10)] - @schedule.match?.should be_true - end - - it "should not match when the start time is after the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now + 5), format(Time.now + 10)] - @schedule.match?.should be_false - end - - it "should not match when the end time is previous to the current time" do - @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now - 5)] - @schedule.match?.should be_false - end - end - - describe Puppet::Type::Schedule, "when matching hourly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :hourly - @schedule[:periodmatch] = :distance - end - - it "should match an hour ago" do - @schedule.match?(hour("-", 1)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match 59 minutes ago" do - @schedule.match?(min("-", 59)).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching daily by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:periodmatch] = :distance - end - - it "should match when the previous time was one day ago" do - @schedule.match?(day("-", 1)).should be_true - end - - it "should not match when the previous time is now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match when the previous time was 23 hours ago" do - @schedule.match?(hour("-", 23)).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching weekly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :weekly - @schedule[:periodmatch] = :distance - end - - it "should match seven days ago" do - @schedule.match?(day("-", 7)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match six days ago" do - @schedule.match?(day("-", 6)).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching monthly by distance" do - include ScheduleTesting - - before do - @schedule[:period] = :monthly - @schedule[:periodmatch] = :distance - end - - it "should match 32 days ago" do - @schedule.match?(day("-", 32)).should be_true - end - - it "should not match now" do - @schedule.match?(Time.now).should be_false - end - - it "should not match 27 days ago" do - @schedule.match?(day("-", 27)).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching hourly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :hourly - @schedule[:periodmatch] = :number - end - - it "should match if the times are one minute apart and the current minute is 0" do - current = Time.now - - # Subtract an hour, reset the minute to zero, then add 59 minutes, so we're the previous hour plus 59 minutes. - previous = (current - 3600 - (current.min * 60) + (59 * 60)) - - # Now set the "current" time to the zero minute of the current hour. - now = (current - (current.min * 60)) - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_true - end - - it "should not match if the times are 58 minutes apart and the current minute is 59" do - current = Time.now - - # reset the minute to zero - previous = current - (current.min * 60) - - # Now set the "current" time to the 59th minute of the current hour. - now = (current - (current.min * 60) + (59 * 60)) - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching daily by number" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:periodmatch] = :number - end - - it "should match if the times are one minute apart and the current minute and hour are 0" do - zero = Time.now - - # Reset the current time to X:00:00 - current = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec - - # Now set the previous time to one minute before that - previous = current - 60 - - Time.stubs(:now).returns(current) - @schedule.match?(previous).should be_true - end - - it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do - zero = Time.now - - # Reset the previous time to 00:00:00 - previous = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec - - # Set the current time to 23:59 - now = previous + (23 * 3600) + (59 * 60) - - Time.stubs(:now).returns(now) - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching weekly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :weekly - @schedule[:periodmatch] = :number - end - - it "should match if the previous time is prior to the most recent Sunday" do - now = Time.now - - # Subtract the number days we've progressed into the week, plus one because we're zero-indexed. - previous = now - (3600 * 24 * (now.wday + 1)) - - @schedule.match?(previous).should be_true - end - - it "should not match if the previous time is after the most recent Saturday" do - now = Time.now - - # Subtract the number days we've progressed into the week - previous = now - (3600 * 24 * now.wday) - - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching monthly by number" do - include ScheduleTesting - - before do - @schedule[:period] = :monthly - @schedule[:periodmatch] = :number - end - - it "should match when the previous time is prior to the first day of this month" do - now = Time.now - - # Subtract the number days we've progressed into the month - previous = now - (3600 * 24 * now.day) - - @schedule.match?(previous).should be_true - end - - it "should not match when the previous time is after the last day of last month" do - now = Time.now - - # Subtract the number days we've progressed into the month, minus one - previous = now - (3600 * 24 * (now.day - 1)) - - @schedule.match?(previous).should be_false - end - end - - describe Puppet::Type::Schedule, "when matching with a repeat greater than one" do - include ScheduleTesting - - before do - @schedule[:period] = :daily - @schedule[:repeat] = 2 - end - - it "should fail if the periodmatch is 'number'" do - @schedule[:periodmatch] = :number - proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) - end - - it "should match if the previous run was further away than the distance divided by the repeat" do - previous = Time.now - (3600 * 13) - @schedule.match?(previous).should be_true - end - - it "should not match if the previous run was closer than the distance divided by the repeat" do - previous = Time.now - (3600 * 11) - @schedule.match?(previous).should be_false - end - end -end diff --git a/spec/unit/ral/type/service.rb b/spec/unit/ral/type/service.rb deleted file mode 100755 index 0f00992fa..000000000 --- a/spec/unit/ral/type/service.rb +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -require 'puppet/type/service' - -describe Puppet::Type::Service do - it "should have an :enableable feature that requires the :enable, :disable, and :enabled? methods" do - Puppet::Type::Service.provider_feature(:enableable).methods.should == [:disable, :enable, :enabled?] - end - - it "should have a :refreshable feature that requires the :restart method" do - Puppet::Type::Service.provider_feature(:refreshable).methods.should == [:restart] - end -end - -describe Puppet::Type::Service, "when validating attributes" do - [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| - it "should have a #{param} parameter" do - Puppet::Type::Service.attrtype(param).should == :param - end - end - - [:ensure, :enable].each do |param| - it "should have an #{param} property" do - Puppet::Type::Service.attrtype(param).should == :property - end - end -end - -describe Puppet::Type::Service, "when validating attribute values" do - before do - @provider = stub 'provider', :class => Puppet::Type::Service.defaultprovider, :clear => nil, :controllable? => false - Puppet::Type::Service.defaultprovider.stubs(:new).returns(@provider) - end - - it "should support :running as a value to :ensure" do - Puppet::Type::Service.create(:name => "yay", :ensure => :running) - end - - it "should support :stopped as a value to :ensure" do - Puppet::Type::Service.create(:name => "yay", :ensure => :stopped) - end - - it "should alias the value :true to :running in :ensure" do - svc = Puppet::Type::Service.create(:name => "yay", :ensure => true) - svc.should(:ensure).should == :running - end - - it "should alias the value :false to :stopped in :ensure" do - svc = Puppet::Type::Service.create(:name => "yay", :ensure => false) - svc.should(:ensure).should == :stopped - end - - it "should support :true as a value to :enable" do - Puppet::Type::Service.create(:name => "yay", :enable => :true) - end - - it "should support :false as a value to :enable" do - Puppet::Type::Service.create(:name => "yay", :enable => :false) - end - - it "should support :true as a value to :hasstatus" do - Puppet::Type::Service.create(:name => "yay", :hasstatus => :true) - end - - it "should support :false as a value to :hasstatus" do - Puppet::Type::Service.create(:name => "yay", :hasstatus => :false) - end - - it "should support :true as a value to :hasrestart" do - Puppet::Type::Service.create(:name => "yay", :hasrestart => :true) - end - - it "should support :false as a value to :hasrestart" do - Puppet::Type::Service.create(:name => "yay", :hasrestart => :false) - end - - it "should allow setting the :enable parameter if the provider has the :enableable feature" do - Puppet::Type::Service.defaultprovider.stubs(:supports_parameter?).returns(true) - Puppet::Type::Service.defaultprovider.expects(:supports_parameter?).with(Puppet::Type::Service.attrclass(:enable)).returns(true) - svc = Puppet::Type::Service.create(:name => "yay", :enable => true) - svc.should(:enable).should == :true - end - - it "should not allow setting the :enable parameter if the provider is missing the :enableable feature" do - Puppet::Type::Service.defaultprovider.stubs(:supports_parameter?).returns(true) - Puppet::Type::Service.defaultprovider.expects(:supports_parameter?).with(Puppet::Type::Service.attrclass(:enable)).returns(false) - svc = Puppet::Type::Service.create(:name => "yay", :enable => true) - svc.should(:enable).should be_nil - end - - it "should discard paths that do not exist" do - FileTest.stubs(:exist?).returns(false) - FileTest.stubs(:directory?).returns(false) - svc = Puppet::Type::Service.create(:name => "yay", :path => "/one/two") - svc[:path].should be_empty - end - - it "should discard paths that are not directories" do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(false) - svc = Puppet::Type::Service.create(:name => "yay", :path => "/one/two") - svc[:path].should be_empty - end - - it "should split paths on ':'" do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - svc = Puppet::Type::Service.create(:name => "yay", :path => "/one/two:/three/four") - svc[:path].should == %w{/one/two /three/four} - end - - it "should accept arrays of paths joined by ':'" do - FileTest.stubs(:exist?).returns(true) - FileTest.stubs(:directory?).returns(true) - svc = Puppet::Type::Service.create(:name => "yay", :path => ["/one:/two", "/three:/four"]) - svc[:path].should == %w{/one /two /three /four} - end - - after { Puppet::Type::Service.clear } -end - -describe Puppet::Type::Service, "when setting default attribute values" do - it "should default to the provider's default path if one is available" do - FileTest.stubs(:directory?).returns(true) - FileTest.stubs(:exist?).returns(true) - - Puppet::Type::Service.defaultprovider.stubs(:respond_to?).returns(true) - Puppet::Type::Service.defaultprovider.stubs(:defpath).returns("testing") - svc = Puppet::Type::Service.create(:name => "other") - svc[:path].should == ["testing"] - end - - it "should default 'pattern' to the binary if one is provided" do - svc = Puppet::Type::Service.create(:name => "other", :binary => "/some/binary") - svc[:pattern].should == "/some/binary" - end - - it "should default 'pattern' to the name if no pattern is provided" do - svc = Puppet::Type::Service.create(:name => "other") - svc[:pattern].should == "other" - end - - it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do - provider = stub 'provider', :controllable? => true, :class => Puppet::Type::Service.defaultprovider, :clear => nil - Puppet::Type::Service.defaultprovider.stubs(:new).returns(provider) - svc = Puppet::Type::Service.create(:name => "nfs.client") - svc[:control].should == "NFS_CLIENT_START" - end - - after { Puppet::Type::Service.clear } -end - -describe Puppet::Type::Service, "when retrieving the host's current state" do - before do - @service = Puppet::Type::Service.create(:name => "yay") - end - - it "should use the provider's status to determine whether the service is running" do - @service.provider.expects(:status).returns(:yepper) - @service[:ensure] = :running - @service.property(:ensure).retrieve.should == :yepper - end - - it "should ask the provider whether it is enabled" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service.provider.expects(:enabled?).returns(:yepper) - @service[:enable] = true - @service.property(:enable).retrieve.should == :yepper - end - - after { Puppet::Type::Service.clear } -end - -describe Puppet::Type::Service, "when changing the host" do - before do - @service = Puppet::Type::Service.create(:name => "yay") - end - - it "should start the service if it is supposed to be running" do - @service[:ensure] = :running - @service.provider.expects(:start) - @service.property(:ensure).sync - end - - it "should stop the service if it is supposed to be stopped" do - @service[:ensure] = :stopped - @service.provider.expects(:stop) - @service.property(:ensure).sync - end - - it "should enable the service if it is supposed to be enabled" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service[:enable] = true - @service.provider.expects(:enable) - @service.property(:enable).sync - end - - it "should disable the service if it is supposed to be disabled" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service[:enable] = false - @service.provider.expects(:disable) - @service.property(:enable).sync - end - - it "should sync the service's enable state when changing the state of :ensure if :enable is being managed" do - @service.provider.class.stubs(:supports_parameter?).returns(true) - @service[:enable] = false - @service[:ensure] = :stopped - - @service.property(:enable).expects(:retrieve).returns("whatever") - @service.property(:enable).expects(:insync?).returns(false) - @service.property(:enable).expects(:sync) - - @service.provider.stubs(:stop) - - @service.property(:ensure).sync - end - - after { Puppet::Type::Service.clear } -end - -describe Puppet::Type::Service, "when refreshing the service" do - before do - @service = Puppet::Type::Service.create(:name => "yay") - end - - it "should restart the service if it is running" do - @service[:ensure] = :running - @service.provider.expects(:status).returns(:running) - @service.provider.expects(:restart) - @service.refresh - end - - it "should restart the service if it is running, even if it is supposed to stopped" do - @service[:ensure] = :stopped - @service.provider.expects(:status).returns(:running) - @service.provider.expects(:restart) - @service.refresh - end - - it "should not restart the service if it is not running" do - @service[:ensure] = :running - @service.provider.expects(:status).returns(:stopped) - @service.refresh - end - - it "should add :ensure as a property if it is not being managed" do - @service.provider.expects(:status).returns(:running) - @service.provider.expects(:restart) - @service.refresh - end - - after { Puppet::Type::Service.clear } -end diff --git a/spec/unit/ral/type/ssh_authorized_key.rb b/spec/unit/ral/type/ssh_authorized_key.rb deleted file mode 100755 index d27cb9f25..000000000 --- a/spec/unit/ral/type/ssh_authorized_key.rb +++ /dev/null @@ -1,80 +0,0 @@ -#!/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/ral/type/user.rb deleted file mode 100755 index b697426dc..000000000 --- a/spec/unit/ral/type/user.rb +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../../spec_helper' - -module UserTestFunctions - def mkuser(name) - user = nil; - lambda { - user = Puppet::Type.type(:user).create( - :name => name, - :comment => "Puppet Testing User", - :gid => Puppet::Util::SUIDManager.gid, - :shell => "/bin/sh", - :home => "/home/%s" % name - ) }.should_not raise_error - user.should_not be_nil - user - end - - def test_provider_class(klass) - klass.should_not be_nil - klass.should be_an_instance_of(Class) - superclasses = [] - while klass = klass.superclass - superclasses << klass - end - superclasses.should include(Puppet::Provider) - end -end - -describe Puppet::Type.type(:user) do - - include UserTestFunctions - - it "should have a default provider inheriting from Puppet::Provider" do - test_provider_class Puppet::Type.type(:user).defaultprovider - end - - it "should be able to create a instance" do - mkuser "123testuser1" - end -end - -describe Puppet::Type.type(:user), "instances" do - - include UserTestFunctions - - it "should have a valid provider" do - user = mkuser "123testuser2" - user.provider.should_not be_nil - test_provider_class user.provider.class - end - -end - - diff --git a/spec/unit/type.rb b/spec/unit/type.rb new file mode 100755 index 000000000..5980167d6 --- /dev/null +++ b/spec/unit/type.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +describe Puppet::Type, " when in a configuration" do + before do + @catalog = Puppet::Node::Catalog.new + @container = Puppet::Type.type(:component).create(:name => "container") + @one = Puppet::Type.type(:file).create(:path => "/file/one") + @two = Puppet::Type.type(:file).create(:path => "/file/two") + @catalog.add_resource @container + @catalog.add_resource @one + @catalog.add_resource @two + @catalog.add_edge @container, @one + @catalog.add_edge @container, @two + end + + it "should have no parent if there is no in edge" do + @container.parent.should be_nil + end + + it "should set its parent to its in edge" do + @one.parent.ref.should == @container.ref + end + + after do + @catalog.clear(true) + end +end diff --git a/spec/unit/type/exec.rb b/spec/unit/type/exec.rb new file mode 100755 index 000000000..260804227 --- /dev/null +++ b/spec/unit/type/exec.rb @@ -0,0 +1,93 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/exec' + +module ExecModuleTesting + def create_resource(command, output, exitstatus) + @user_name = 'some_user_name' + @group_name = 'some_group_name' + Puppet.features.stubs(:root?).returns(true) + @execer = Puppet::Type.type(:exec).create(:name => command, :path => %w{/usr/bin /bin}, :user => @user_name, :group => @group_name) + + status = stub "process" + status.stubs(:exitstatus).returns(exitstatus) + + Puppet::Util::SUIDManager.expects(:run_and_capture).with([command], @user_name, @group_name).returns([output, status]) + end + + def create_logging_resource(command, output, exitstatus, logoutput, loglevel) + create_resource(command, output, exitstatus) + @execer[:logoutput] = logoutput + @execer[:loglevel] = loglevel + end + + def expect_output(output, loglevel) + output.split(/\n/).each do |line| + @execer.property(:returns).expects(loglevel).with(line) + end + end +end + +describe Puppet::Type::Exec, " when execing" do + include ExecModuleTesting + + it "should use the 'run_and_capture' method to exec" do + command = "true" + create_resource(command, "", 0) + + @execer.refresh.should == :executed_command + end + + it "should report a failure" do + command = "false" + create_resource(command, "", 1) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + it "should log the output on success" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 0, true, :err) + expect_output(output, :err) + @execer.refresh + end + + it "should log the output on failure" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 1, true, :err) + expect_output(output, :err) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + +end + + +describe Puppet::Type::Exec, " when logoutput=>on_failure is set," do + include ExecModuleTesting + + it "should log the output on failure" do + #Puppet::Util::Log.newdestination :console + command = "false" + output = "output1\noutput2\n" + create_logging_resource(command, output, 1, :on_failure, :err) + expect_output(output, :err) + + proc { @execer.refresh }.should raise_error(Puppet::Error) + end + + it "shouldn't log the output on success" do + #Puppet::Util::Log.newdestination :console + command = "true" + output = "output1\noutput2\n" + create_logging_resource(command, output, 0, :on_failure, :err) + @execer.property(:returns).expects(:err).never + @execer.refresh + end +end diff --git a/spec/unit/type/file.rb b/spec/unit/type/file.rb new file mode 100755 index 000000000..e1a597434 --- /dev/null +++ b/spec/unit/type/file.rb @@ -0,0 +1,100 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Type.type(:file) do + before do + @path = Tempfile.new("puppetspec") + @path.close!() + @path = @path.path + @file = Puppet::Type::File.create(:name => @path) + end + + describe "when used with content and replace=>false" do + before do + @file[:content] = "foo" + @file[:replace] = false + end + + it "should be insync if the file exists and the content is different" do + File.open(@path, "w") do |f| f.puts "bar" end + @file.property(:content).insync?("bar").should be_true + end + + it "should be insync if the file exists and the content is right" do + File.open(@path, "w") do |f| f.puts "foo" end + @file.property(:content).insync?("foo").should be_true + end + + it "should not be insync if the file does not exist" do + @file.property(:content).insync?(:nil).should be_false + end + end + + describe "when retrieving remote files" do + before do + @filesource = Puppet::Type::File::FileSource.new + @filesource.server = mock 'fileserver' + + @file.stubs(:uri2obj).returns(@filesource) + + @file[:source] = "puppet:///test" + end + + it "should fail without writing if it cannot retrieve remote contents" do + # create the file, because we only get the problem when it starts + # out absent. + File.open(@file[:path], "w") { |f| f.puts "a" } + @file.expects(:write).never + + @filesource.server.stubs(:describe).returns("493\tfile\t100\t0\t{md5}3f5fef3bddbc4398c46a7bd7ba7b3af7") + @filesource.server.stubs(:retrieve).raises(RuntimeError) + @file.property(:source).retrieve + lambda { @file.property(:source).sync }.should raise_error(Puppet::Error) + end + end + + describe "when managing links" do + require 'puppettest/support/assertions' + include PuppetTest + require 'tempfile' + + before do + @basedir = tempfile + Dir.mkdir(@basedir) + @file = File.join(@basedir, "file") + @link = File.join(@basedir, "link") + + File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } + File.symlink(@file, @link) + + @resource = Puppet.type(:file).create( + :path => @link, + :mode => "755" + ) + @catalog = Puppet::Node::Catalog.new + @catalog.add_resource @resource + end + + after do + remove_tmp_files + end + + it "should default to managing the link" do + @catalog.apply + # I convert them to strings so they display correctly if there's an error. + ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 + end + + it "should be able to follow links" do + @resource[:links] = :follow + @catalog.apply + + ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 + end + end + + after do + Puppet::Type::File.clear + end +end diff --git a/spec/unit/type/interface.rb b/spec/unit/type/interface.rb new file mode 100755 index 000000000..2e0176152 --- /dev/null +++ b/spec/unit/type/interface.rb @@ -0,0 +1,95 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +interface = Puppet::Type.type(:interface) + +describe interface do + before do + @class = Puppet::Type.type(:interface) + + @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 interface_type property" do + @class.attrtype(:interface_type).should == :property + end + it "should support :loopback as an interface_type value" do + proc { @class.create(:name => "whev", :interface_type => :loopback) }.should_not raise_error + end + it "should support :alias as an interface_type value" do + proc { @class.create(:name => "whev", :interface_type => :alias) }.should_not raise_error + end + it "should support :normal as an interface_type value" do + proc { @class.create(:name => "whev", :interface_type => :normal) }.should_not raise_error + end + it "should alias :dummy to the :loopback interface_type value" do + int = @class.create(:name => "whev", :interface_type => :dummy) + int.should(:interface_type).should == :loopback + end + + it "should not support values other than :loopback, :alias, :normal, and :dummy in the interface_type" do + proc { @class.create(:name => "whev", :interface_type => :something) }.should raise_error(Puppet::Error) + end + + it "should have an interface_desc parameter" do + @class.attrtype(:interface_desc).should == :param + end + + it "should have an onboot property" do + @class.attrtype(:onboot).should == :property + end + it "should support :true as an onboot value" do + proc { @class.create(:name => "whev", :onboot => :true) }.should_not raise_error + end + it "should support :false as an onboot value" do + proc { @class.create(:name => "whev", :onboot => :false) }.should_not raise_error + end + + it "should have an ifnum property" do + @class.attrtype(:ifnum).should == :property + end + + it "should have a netmask property" do + @class.attrtype(:netmask).should == :property + end + + it "should have an ifopts property" do + @class.attrtype(:ifopts).should == :property + end + + it "should have a target parameter" do + @class.attrtype(:target).should == :param + end + + after { @class.clear } +end diff --git a/spec/unit/type/mount.rb b/spec/unit/type/mount.rb new file mode 100755 index 000000000..8fa2e6f7c --- /dev/null +++ b/spec/unit/type/mount.rb @@ -0,0 +1,192 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/mount' + +describe Puppet::Type::Mount do + it "should have a :refreshable feature that requires the :remount method" do + Puppet::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.should(:ensure).should be_nil + end + + after { Puppet::Type::Mount.clear } +end + +describe Puppet::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 + 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 + end + end +end + +describe Puppet::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) + end + + it "should support :present as a value to :ensure" do + Puppet::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.should(:ensure).should == :present + end + + it "should support :absent as a value to :ensure" do + Puppet::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) + end + + after { Puppet::Type::Mount.clear } +end + +describe Puppet::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) + + @ensure = @mount.property(:ensure) + end + + after :each do + Puppet::Type::Mount.clear + end + + def mount_stub(params) + Puppet::Type::Mount.validproperties.each do |prop| + unless params[prop] + params[prop] = :absent + @mount[prop] = :absent + end + end + + params.each do |param, value| + @provider.stubs(param).returns(value) + end + end + + describe Puppet::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) + @ensure.retrieve.should == :absent + end + + it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do + @provider.expects(:ensure).returns(:present) + @provider.expects(:mounted?).returns(true) + @ensure.retrieve.should == :mounted + end + + it "should return :present if the provider indicates it is not mounted and the value is not :absent" do + @provider.expects(:ensure).returns(:present) + @provider.expects(:mounted?).returns(false) + @ensure.retrieve.should == :present + end + end + + describe Puppet::Type::Mount::Ensure, "when changing the host" do + + it "should destroy itself if it should be absent" do + @provider.stubs(:mounted?).returns(false) + @provider.expects(:destroy) + @ensure.should = :absent + @ensure.sync + end + + it "should unmount itself before destroying if it is mounted and should be absent" do + @provider.expects(:mounted?).returns(true) + @provider.expects(:unmount) + @provider.expects(:destroy) + @ensure.should = :absent + @ensure.sync + end + + it "should create itself if it is absent and should be present" do + @provider.stubs(:mounted?).returns(false) + @provider.expects(:create) + @ensure.should = :present + @ensure.sync + end + + it "should unmount itself if it is mounted and should be present" do + @provider.stubs(:mounted?).returns(true) + + # The interface here is just too much work to test right now. + @ensure.stubs(:syncothers) + @provider.expects(:unmount) + @ensure.should = :present + @ensure.sync + end + + it "should create and mount itself if it does not exist and should be mounted" do + @provider.stubs(:ensure).returns(:absent) + @provider.stubs(:mounted?).returns(false) + @provider.expects(:create) + @ensure.stubs(:syncothers) + @provider.expects(:mount) + @ensure.should = :mounted + @ensure.sync + end + + it "should mount itself if it is present and should be mounted" do + @provider.stubs(:ensure).returns(:present) + @provider.stubs(:mounted?).returns(false) + @ensure.stubs(:syncothers) + @provider.expects(:mount) + @ensure.should = :mounted + @ensure.sync + end + + it "should create but not mount itself if it is absent and mounted and should be mounted" do + @provider.stubs(:ensure).returns(:absent) + @provider.stubs(:mounted?).returns(true) + @ensure.stubs(:syncothers) + @provider.expects(:create) + @ensure.should = :mounted + @ensure.sync + end + end + + describe Puppet::Type::Mount, "when responding to events" do + + it "should remount if it is currently mounted" do + @provider.expects(:mounted?).returns(true) + @provider.expects(:remount) + + @mount.refresh + end + + it "should not remount if it is not currently mounted" do + @provider.expects(:mounted?).returns(false) + @provider.expects(:remount).never + + @mount.refresh + end + + it "should not remount swap filesystems" do + @mount[:fstype] = "swap" + @provider.expects(:remount).never + + @mount.refresh + end + end +end diff --git a/spec/unit/type/nagios.rb b/spec/unit/type/nagios.rb new file mode 100755 index 000000000..35f00b0e5 --- /dev/null +++ b/spec/unit/type/nagios.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/external/nagios' + +describe "Nagios resource types" do + Nagios::Base.eachtype do |name, nagios_type| + puppet_type = Puppet::Type.type("nagios_" + name.to_s) + + it "should have a valid type for #{name}" do + puppet_type.should_not be_nil + end + + next unless puppet_type + + describe puppet_type do + it "should be defined as a Puppet resource type" do + puppet_type.should_not be_nil + end + + it "should have documentation" do + puppet_type.instance_variable_get("@doc").should_not == "" + end + + it "should have %s as its namevar" % nagios_type.namevar do + puppet_type.namevar.should == nagios_type.namevar + end + + it "should have documentation for its %s parameter" % nagios_type.namevar do + puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil + end + + it "should have an ensure property" do + puppet_type.should be_validproperty(:ensure) + end + + it "should have a target property" do + puppet_type.should be_validproperty(:target) + end + + it "should have documentation for its target property" do + puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil + end + + nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| + it "should have a %s property" % param do + puppet_type.should be_validproperty(param) + end + + it "should have documentation for its %s property" % param do + puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil + end + end + + nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| + it "should have not have a %s property" % param do + puppet_type.should_not be_validproperty(:param) + end + end + end + end +end diff --git a/spec/unit/type/noop_metaparam.rb b/spec/unit/type/noop_metaparam.rb new file mode 100755 index 000000000..0cbed3714 --- /dev/null +++ b/spec/unit/type/noop_metaparam.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/metatype/metaparams' + +describe Puppet::Type.type(:file).attrclass(:noop) do + before do + @file = Puppet::Type.newfile :path => "/what/ever" + end + + after { Puppet::Type::File.clear } + + it "should accept true as a value" do + lambda { @file[:noop] = true }.should_not raise_error + end + + it "should accept false as a value" do + lambda { @file[:noop] = false }.should_not raise_error + end + + describe "when set on a resource" do + it "should default to the :noop setting" do + Puppet.settings.expects(:value).with(:noop).returns "myval" + @file.noop.should == "myval" + end + + it "should prefer true values from the attribute" do + @file[:noop] = true + @file.noop.should be_true + end + + it "should prefer false values from the attribute" do + @file[:noop] = false + @file.noop.should be_false + end + end +end diff --git a/spec/unit/type/package.rb b/spec/unit/type/package.rb new file mode 100755 index 000000000..5d96dc4ae --- /dev/null +++ b/spec/unit/type/package.rb @@ -0,0 +1,247 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/package' + +describe Puppet::Type::Package do + it "should have an :installable feature that requires the :install method" do + Puppet::Type::Package.provider_feature(:installable).methods.should == [:install] + end + + it "should have an :uninstallable feature that requires the :uninstall method" do + Puppet::Type::Package.provider_feature(:uninstallable).methods.should == [:uninstall] + end + + it "should have an :upgradeable feature that requires :update and :latest methods" do + Puppet::Type::Package.provider_feature(:upgradeable).methods.should == [:update, :latest] + end + + it "should have a :purgeable feature that requires the :purge latest method" do + Puppet::Type::Package.provider_feature(:purgeable).methods.should == [:purge] + end + + it "should have a :versionable feature" do + Puppet::Type::Package.provider_feature(:versionable).should_not be_nil + end + + it "should default to being installed" do + pkg = Puppet::Type::Package.create(:name => "yay") + pkg.should(:ensure).should == :present + end + + after { Puppet::Type::Package.clear } +end + +describe Puppet::Type::Package, "when validating attributes" do + [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| + it "should have a #{param} parameter" do + Puppet::Type::Package.attrtype(param).should == :param + end + end + + it "should have an ensure property" do + Puppet::Type::Package.attrtype(:ensure).should == :property + end +end + +describe Puppet::Type::Package, "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil + Puppet::Type::Package.defaultprovider.expects(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type::Package.create(:name => "yay", :ensure => :present) + end + + it "should alias :installed to :present as a value to :ensure" do + pkg = Puppet::Type::Package.create(:name => "yay", :ensure => :installed) + pkg.should(:ensure).should == :present + end + + it "should support :absent as a value to :ensure" do + Puppet::Type::Package.create(:name => "yay", :ensure => :absent) + end + + it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do + @provider.expects(:satisfies?).with(:purgeable).returns(true) + Puppet::Type::Package.create(:name => "yay", :ensure => :purged) + end + + it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do + @provider.expects(:satisfies?).with(:purgeable).returns(false) + proc { Puppet::Type::Package.create(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) + end + + it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do + @provider.expects(:satisfies?).with(:upgradeable).returns(true) + Puppet::Type::Package.create(:name => "yay", :ensure => :latest) + end + + it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do + @provider.expects(:satisfies?).with(:upgradeable).returns(false) + proc { Puppet::Type::Package.create(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) + end + + it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do + @provider.expects(:satisfies?).with(:versionable).returns(true) + Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") + end + + it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do + @provider.expects(:satisfies?).with(:versionable).returns(false) + proc { Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) + end + + it "should accept any string as an argument to :source" do + proc { Puppet::Type::Package.create(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) + end + + after { Puppet::Type::Package.clear } +end + +module PackageEvaluationTesting + def setprops(properties) + @provider.stubs(:properties).returns(properties) + end +end + +describe Puppet::Type::Package do + before :each do + @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock + Puppet::Type::Package.defaultprovider.stubs(:new).returns(@provider) + @package = Puppet::Type::Package.create(:name => "yay") + + @catalog = Puppet::Node::Catalog.new + @catalog.add_resource(@package) + end + + after :each do + @catalog.clear(true) + Puppet::Type::Package.clear + end + + + describe Puppet::Type::Package, "when it should be purged" do + include PackageEvaluationTesting + + before { @package[:ensure] = :purged } + + it "should do nothing if it is :purged" do + @provider.expects(:properties).returns(:ensure => :purged) + @catalog.apply + end + + [:absent, :installed, :present, :latest].each do |state| + it "should purge if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:purge) + @catalog.apply + end + end + end + + describe Puppet::Type::Package, "when it should be absent" do + include PackageEvaluationTesting + + before { @package[:ensure] = :absent } + + [:purged, :absent].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @catalog.apply + end + end + + [:installed, :present, :latest].each do |state| + it "should uninstall if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:uninstall) + @catalog.apply + end + end + end + + describe Puppet::Type::Package, "when it should be present" do + include PackageEvaluationTesting + + before { @package[:ensure] = :present } + + [:present, :latest, "1.0"].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @catalog.apply + end + end + + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @catalog.apply + end + end + end + + describe Puppet::Type::Package, "when it should be latest" do + include PackageEvaluationTesting + + before { @package[:ensure] = :latest } + + [:purged, :absent].each do |state| + it "should upgrade if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:update) + @catalog.apply + end + end + + it "should upgrade if the current version is not equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("2.0") + @provider.expects(:update) + @catalog.apply + end + + it "should do nothing if it is equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @catalog.apply + end + + it "should do nothing if the provider returns :present as the latest version" do + @provider.stubs(:properties).returns(:ensure => :present) + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @catalog.apply + end + end + + describe Puppet::Type::Package, "when it should be a specific version" do + include PackageEvaluationTesting + + before { @package[:ensure] = "1.0" } + + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @catalog.apply + end + end + + it "should do nothing if the current version is equal to the desired version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.expects(:install).never + @catalog.apply + end + + it "should install if the current version is not equal to the specified version" do + @provider.stubs(:properties).returns(:ensure => "2.0") + @provider.expects(:install) + @catalog.apply + end + end +end diff --git a/spec/unit/type/schedule.rb b/spec/unit/type/schedule.rb new file mode 100755 index 000000000..4e9840c34 --- /dev/null +++ b/spec/unit/type/schedule.rb @@ -0,0 +1,341 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/schedule' + +module ScheduleTesting + + def format(time) + time.strftime("%H:%M:%S") + end + + def diff(unit, incr, method, count) + diff = Time.now.to_i.send(method, incr * count) + Time.at(diff) + end + + def month(method, count) + diff(:hour, 3600 * 24 * 30, method, count) + end + + def week(method, count) + diff(:hour, 3600 * 24 * 7, method, count) + end + + def day(method, count) + diff(:hour, 3600 * 24, method, count) + end + + def hour(method, count) + diff(:hour, 3600, method, count) + end + + def min(method, count) + diff(:min, 60, method, count) + end + + def sec(method, count) + diff(:sec, 1, method, count) + end + +end + +describe Puppet::Type::Schedule do + + before :each do + Puppet.settings.stubs(:value).with(:ignoreschedules).returns(false) + + @schedule = Puppet::Type::Schedule.create(:name => "testing") + end + + after :each do + Puppet::Type::Schedule.clear + end + + + describe Puppet::Type::Schedule do + include ScheduleTesting + + it "should default to :distance for period-matching" do + @schedule[:periodmatch].should == :distance + end + + it "should default to a :repeat of 1" do + @schedule[:repeat].should == 1 + end + + it "should never match when the period is :never" do + @schedule[:period] = :never + @schedule.match?.should be_false + end + end + + describe Puppet::Type::Schedule, "when producing default schedules" do + include ScheduleTesting + + %w{hourly daily weekly monthly never}.each do |period| + period = period.to_sym + it "should produce a #{period} schedule with the period set appropriately" do + schedules = Puppet::Type::Schedule.mkdefaultschedules + schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.should be_instance_of(Puppet::Type::Schedule) + end + end + + it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do + schedules = Puppet::Type::Schedule.mkdefaultschedules + schedules.find { |s| + s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 + }.should be_instance_of(Puppet::Type::Schedule) + end + end + + describe Puppet::Type::Schedule, "when matching ranges" do + include ScheduleTesting + + it "should match when the start time is before the current time and the end time is after the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now + 10)] + @schedule.match?.should be_true + end + + it "should not match when the start time is after the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now + 5), format(Time.now + 10)] + @schedule.match?.should be_false + end + + it "should not match when the end time is previous to the current time" do + @schedule[:range] = "%s - %s" % [format(Time.now - 10), format(Time.now - 5)] + @schedule.match?.should be_false + end + end + + describe Puppet::Type::Schedule, "when matching hourly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :hourly + @schedule[:periodmatch] = :distance + end + + it "should match an hour ago" do + @schedule.match?(hour("-", 1)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match 59 minutes ago" do + @schedule.match?(min("-", 59)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching daily by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:periodmatch] = :distance + end + + it "should match when the previous time was one day ago" do + @schedule.match?(day("-", 1)).should be_true + end + + it "should not match when the previous time is now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match when the previous time was 23 hours ago" do + @schedule.match?(hour("-", 23)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching weekly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :weekly + @schedule[:periodmatch] = :distance + end + + it "should match seven days ago" do + @schedule.match?(day("-", 7)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match six days ago" do + @schedule.match?(day("-", 6)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching monthly by distance" do + include ScheduleTesting + + before do + @schedule[:period] = :monthly + @schedule[:periodmatch] = :distance + end + + it "should match 32 days ago" do + @schedule.match?(day("-", 32)).should be_true + end + + it "should not match now" do + @schedule.match?(Time.now).should be_false + end + + it "should not match 27 days ago" do + @schedule.match?(day("-", 27)).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching hourly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :hourly + @schedule[:periodmatch] = :number + end + + it "should match if the times are one minute apart and the current minute is 0" do + current = Time.now + + # Subtract an hour, reset the minute to zero, then add 59 minutes, so we're the previous hour plus 59 minutes. + previous = (current - 3600 - (current.min * 60) + (59 * 60)) + + # Now set the "current" time to the zero minute of the current hour. + now = (current - (current.min * 60)) + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_true + end + + it "should not match if the times are 58 minutes apart and the current minute is 59" do + current = Time.now + + # reset the minute to zero + previous = current - (current.min * 60) + + # Now set the "current" time to the 59th minute of the current hour. + now = (current - (current.min * 60) + (59 * 60)) + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching daily by number" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:periodmatch] = :number + end + + it "should match if the times are one minute apart and the current minute and hour are 0" do + zero = Time.now + + # Reset the current time to X:00:00 + current = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec + + # Now set the previous time to one minute before that + previous = current - 60 + + Time.stubs(:now).returns(current) + @schedule.match?(previous).should be_true + end + + it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do + zero = Time.now + + # Reset the previous time to 00:00:00 + previous = zero - (zero.hour * 3600) - (zero.min * 60) - zero.sec + + # Set the current time to 23:59 + now = previous + (23 * 3600) + (59 * 60) + + Time.stubs(:now).returns(now) + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching weekly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :weekly + @schedule[:periodmatch] = :number + end + + it "should match if the previous time is prior to the most recent Sunday" do + now = Time.now + + # Subtract the number days we've progressed into the week, plus one because we're zero-indexed. + previous = now - (3600 * 24 * (now.wday + 1)) + + @schedule.match?(previous).should be_true + end + + it "should not match if the previous time is after the most recent Saturday" do + now = Time.now + + # Subtract the number days we've progressed into the week + previous = now - (3600 * 24 * now.wday) + + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching monthly by number" do + include ScheduleTesting + + before do + @schedule[:period] = :monthly + @schedule[:periodmatch] = :number + end + + it "should match when the previous time is prior to the first day of this month" do + now = Time.now + + # Subtract the number days we've progressed into the month + previous = now - (3600 * 24 * now.day) + + @schedule.match?(previous).should be_true + end + + it "should not match when the previous time is after the last day of last month" do + now = Time.now + + # Subtract the number days we've progressed into the month, minus one + previous = now - (3600 * 24 * (now.day - 1)) + + @schedule.match?(previous).should be_false + end + end + + describe Puppet::Type::Schedule, "when matching with a repeat greater than one" do + include ScheduleTesting + + before do + @schedule[:period] = :daily + @schedule[:repeat] = 2 + end + + it "should fail if the periodmatch is 'number'" do + @schedule[:periodmatch] = :number + proc { @schedule[:repeat] = 2 }.should raise_error(Puppet::Error) + end + + it "should match if the previous run was further away than the distance divided by the repeat" do + previous = Time.now - (3600 * 13) + @schedule.match?(previous).should be_true + end + + it "should not match if the previous run was closer than the distance divided by the repeat" do + previous = Time.now - (3600 * 11) + @schedule.match?(previous).should be_false + end + end +end diff --git a/spec/unit/type/service.rb b/spec/unit/type/service.rb new file mode 100755 index 000000000..0f00992fa --- /dev/null +++ b/spec/unit/type/service.rb @@ -0,0 +1,256 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/service' + +describe Puppet::Type::Service do + it "should have an :enableable feature that requires the :enable, :disable, and :enabled? methods" do + Puppet::Type::Service.provider_feature(:enableable).methods.should == [:disable, :enable, :enabled?] + end + + it "should have a :refreshable feature that requires the :restart method" do + Puppet::Type::Service.provider_feature(:refreshable).methods.should == [:restart] + end +end + +describe Puppet::Type::Service, "when validating attributes" do + [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| + it "should have a #{param} parameter" do + Puppet::Type::Service.attrtype(param).should == :param + end + end + + [:ensure, :enable].each do |param| + it "should have an #{param} property" do + Puppet::Type::Service.attrtype(param).should == :property + end + end +end + +describe Puppet::Type::Service, "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type::Service.defaultprovider, :clear => nil, :controllable? => false + Puppet::Type::Service.defaultprovider.stubs(:new).returns(@provider) + end + + it "should support :running as a value to :ensure" do + Puppet::Type::Service.create(:name => "yay", :ensure => :running) + end + + it "should support :stopped as a value to :ensure" do + Puppet::Type::Service.create(:name => "yay", :ensure => :stopped) + end + + it "should alias the value :true to :running in :ensure" do + svc = Puppet::Type::Service.create(:name => "yay", :ensure => true) + svc.should(:ensure).should == :running + end + + it "should alias the value :false to :stopped in :ensure" do + svc = Puppet::Type::Service.create(:name => "yay", :ensure => false) + svc.should(:ensure).should == :stopped + end + + it "should support :true as a value to :enable" do + Puppet::Type::Service.create(:name => "yay", :enable => :true) + end + + it "should support :false as a value to :enable" do + Puppet::Type::Service.create(:name => "yay", :enable => :false) + end + + it "should support :true as a value to :hasstatus" do + Puppet::Type::Service.create(:name => "yay", :hasstatus => :true) + end + + it "should support :false as a value to :hasstatus" do + Puppet::Type::Service.create(:name => "yay", :hasstatus => :false) + end + + it "should support :true as a value to :hasrestart" do + Puppet::Type::Service.create(:name => "yay", :hasrestart => :true) + end + + it "should support :false as a value to :hasrestart" do + Puppet::Type::Service.create(:name => "yay", :hasrestart => :false) + end + + it "should allow setting the :enable parameter if the provider has the :enableable feature" do + Puppet::Type::Service.defaultprovider.stubs(:supports_parameter?).returns(true) + Puppet::Type::Service.defaultprovider.expects(:supports_parameter?).with(Puppet::Type::Service.attrclass(:enable)).returns(true) + svc = Puppet::Type::Service.create(:name => "yay", :enable => true) + svc.should(:enable).should == :true + end + + it "should not allow setting the :enable parameter if the provider is missing the :enableable feature" do + Puppet::Type::Service.defaultprovider.stubs(:supports_parameter?).returns(true) + Puppet::Type::Service.defaultprovider.expects(:supports_parameter?).with(Puppet::Type::Service.attrclass(:enable)).returns(false) + svc = Puppet::Type::Service.create(:name => "yay", :enable => true) + svc.should(:enable).should be_nil + end + + it "should discard paths that do not exist" do + FileTest.stubs(:exist?).returns(false) + FileTest.stubs(:directory?).returns(false) + svc = Puppet::Type::Service.create(:name => "yay", :path => "/one/two") + svc[:path].should be_empty + end + + it "should discard paths that are not directories" do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(false) + svc = Puppet::Type::Service.create(:name => "yay", :path => "/one/two") + svc[:path].should be_empty + end + + it "should split paths on ':'" do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + svc = Puppet::Type::Service.create(:name => "yay", :path => "/one/two:/three/four") + svc[:path].should == %w{/one/two /three/four} + end + + it "should accept arrays of paths joined by ':'" do + FileTest.stubs(:exist?).returns(true) + FileTest.stubs(:directory?).returns(true) + svc = Puppet::Type::Service.create(:name => "yay", :path => ["/one:/two", "/three:/four"]) + svc[:path].should == %w{/one /two /three /four} + end + + after { Puppet::Type::Service.clear } +end + +describe Puppet::Type::Service, "when setting default attribute values" do + it "should default to the provider's default path if one is available" do + FileTest.stubs(:directory?).returns(true) + FileTest.stubs(:exist?).returns(true) + + Puppet::Type::Service.defaultprovider.stubs(:respond_to?).returns(true) + Puppet::Type::Service.defaultprovider.stubs(:defpath).returns("testing") + svc = Puppet::Type::Service.create(:name => "other") + svc[:path].should == ["testing"] + end + + it "should default 'pattern' to the binary if one is provided" do + svc = Puppet::Type::Service.create(:name => "other", :binary => "/some/binary") + svc[:pattern].should == "/some/binary" + end + + it "should default 'pattern' to the name if no pattern is provided" do + svc = Puppet::Type::Service.create(:name => "other") + svc[:pattern].should == "other" + end + + it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do + provider = stub 'provider', :controllable? => true, :class => Puppet::Type::Service.defaultprovider, :clear => nil + Puppet::Type::Service.defaultprovider.stubs(:new).returns(provider) + svc = Puppet::Type::Service.create(:name => "nfs.client") + svc[:control].should == "NFS_CLIENT_START" + end + + after { Puppet::Type::Service.clear } +end + +describe Puppet::Type::Service, "when retrieving the host's current state" do + before do + @service = Puppet::Type::Service.create(:name => "yay") + end + + it "should use the provider's status to determine whether the service is running" do + @service.provider.expects(:status).returns(:yepper) + @service[:ensure] = :running + @service.property(:ensure).retrieve.should == :yepper + end + + it "should ask the provider whether it is enabled" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service.provider.expects(:enabled?).returns(:yepper) + @service[:enable] = true + @service.property(:enable).retrieve.should == :yepper + end + + after { Puppet::Type::Service.clear } +end + +describe Puppet::Type::Service, "when changing the host" do + before do + @service = Puppet::Type::Service.create(:name => "yay") + end + + it "should start the service if it is supposed to be running" do + @service[:ensure] = :running + @service.provider.expects(:start) + @service.property(:ensure).sync + end + + it "should stop the service if it is supposed to be stopped" do + @service[:ensure] = :stopped + @service.provider.expects(:stop) + @service.property(:ensure).sync + end + + it "should enable the service if it is supposed to be enabled" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service[:enable] = true + @service.provider.expects(:enable) + @service.property(:enable).sync + end + + it "should disable the service if it is supposed to be disabled" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service[:enable] = false + @service.provider.expects(:disable) + @service.property(:enable).sync + end + + it "should sync the service's enable state when changing the state of :ensure if :enable is being managed" do + @service.provider.class.stubs(:supports_parameter?).returns(true) + @service[:enable] = false + @service[:ensure] = :stopped + + @service.property(:enable).expects(:retrieve).returns("whatever") + @service.property(:enable).expects(:insync?).returns(false) + @service.property(:enable).expects(:sync) + + @service.provider.stubs(:stop) + + @service.property(:ensure).sync + end + + after { Puppet::Type::Service.clear } +end + +describe Puppet::Type::Service, "when refreshing the service" do + before do + @service = Puppet::Type::Service.create(:name => "yay") + end + + it "should restart the service if it is running" do + @service[:ensure] = :running + @service.provider.expects(:status).returns(:running) + @service.provider.expects(:restart) + @service.refresh + end + + it "should restart the service if it is running, even if it is supposed to stopped" do + @service[:ensure] = :stopped + @service.provider.expects(:status).returns(:running) + @service.provider.expects(:restart) + @service.refresh + end + + it "should not restart the service if it is not running" do + @service[:ensure] = :running + @service.provider.expects(:status).returns(:stopped) + @service.refresh + end + + it "should add :ensure as a property if it is not being managed" do + @service.provider.expects(:status).returns(:running) + @service.provider.expects(:restart) + @service.refresh + end + + after { Puppet::Type::Service.clear } +end diff --git a/spec/unit/type/ssh_authorized_key.rb b/spec/unit/type/ssh_authorized_key.rb new file mode 100755 index 000000000..d27cb9f25 --- /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/type/user.rb b/spec/unit/type/user.rb new file mode 100755 index 000000000..b697426dc --- /dev/null +++ b/spec/unit/type/user.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +module UserTestFunctions + def mkuser(name) + user = nil; + lambda { + user = Puppet::Type.type(:user).create( + :name => name, + :comment => "Puppet Testing User", + :gid => Puppet::Util::SUIDManager.gid, + :shell => "/bin/sh", + :home => "/home/%s" % name + ) }.should_not raise_error + user.should_not be_nil + user + end + + def test_provider_class(klass) + klass.should_not be_nil + klass.should be_an_instance_of(Class) + superclasses = [] + while klass = klass.superclass + superclasses << klass + end + superclasses.should include(Puppet::Provider) + end +end + +describe Puppet::Type.type(:user) do + + include UserTestFunctions + + it "should have a default provider inheriting from Puppet::Provider" do + test_provider_class Puppet::Type.type(:user).defaultprovider + end + + it "should be able to create a instance" do + mkuser "123testuser1" + end +end + +describe Puppet::Type.type(:user), "instances" do + + include UserTestFunctions + + it "should have a valid provider" do + user = mkuser "123testuser2" + user.provider.should_not be_nil + test_provider_class user.provider.class + end + +end + + -- cgit From ac79a79c8d7713068243209f782bf16077dd3d37 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 19:34:21 -0500 Subject: Duh, fixing all of the paths being loaded for spec in the moved tests. --- spec/integration/type/package.rb | 2 +- spec/unit/provider/interface/redhat.rb | 2 +- spec/unit/provider/interface/sunos.rb | 2 +- spec/unit/provider/mount.rb | 2 +- spec/unit/provider/mount/parsed.rb | 2 +- spec/unit/provider/ssh_authorized_key/parsed.rb | 2 +- spec/unit/type.rb | 2 +- spec/unit/type/exec.rb | 2 +- spec/unit/type/file.rb | 2 +- spec/unit/type/interface.rb | 2 +- spec/unit/type/mount.rb | 52 ++++++++++++------------- spec/unit/type/nagios.rb | 2 +- spec/unit/type/noop_metaparam.rb | 2 +- spec/unit/type/package.rb | 2 +- spec/unit/type/schedule.rb | 2 +- spec/unit/type/service.rb | 2 +- spec/unit/type/ssh_authorized_key.rb | 2 +- spec/unit/type/user.rb | 2 +- test/ral/providers/provider.rb | 12 ++++++ 19 files changed, 54 insertions(+), 44 deletions(-) diff --git a/spec/integration/type/package.rb b/spec/integration/type/package.rb index 20567629d..c244fa1cd 100755 --- a/spec/integration/type/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/provider/interface/redhat.rb b/spec/unit/provider/interface/redhat.rb index 9bf1b9722..5a7a8dfcd 100755 --- a/spec/unit/provider/interface/redhat.rb +++ b/spec/unit/provider/interface/redhat.rb @@ -3,7 +3,7 @@ # 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) diff --git a/spec/unit/provider/interface/sunos.rb b/spec/unit/provider/interface/sunos.rb index 7b9f462e6..6a7bd19c1 100755 --- a/spec/unit/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/mount.rb b/spec/unit/provider/mount.rb index 0b90d53c9..41abcd424 100755 --- a/spec/unit/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/provider/mount/parsed.rb b/spec/unit/provider/mount/parsed.rb index 21276d911..ba65b70a4 100755 --- a/spec/unit/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 index 459001cb5..c35ddc513 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../../../spec_helper' +require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppettest' require 'puppettest/support/utils' diff --git a/spec/unit/type.rb b/spec/unit/type.rb index 5980167d6..9815ed32d 100755 --- a/spec/unit/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/type/exec.rb b/spec/unit/type/exec.rb index 260804227..cf0e02929 100755 --- a/spec/unit/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/type/file.rb b/spec/unit/type/file.rb index e1a597434..12b806d88 100755 --- a/spec/unit/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/type/interface.rb b/spec/unit/type/interface.rb index 2e0176152..27f34b7e0 100755 --- a/spec/unit/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/type/mount.rb b/spec/unit/type/mount.rb index 8fa2e6f7c..a9b78672e 100755 --- a/spec/unit/type/mount.rb +++ b/spec/unit/type/mount.rb @@ -1,77 +1,75 @@ #!/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 - after { Puppet::Type::Mount.clear } + after { Puppet::Type.type(:mount).clear } 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 - after { Puppet::Type::Mount.clear } + after { Puppet::Type.type(:mount).clear } 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 after :each do - Puppet::Type::Mount.clear + Puppet::Type.type(:mount).clear 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 @@ -83,7 +81,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) @@ -103,7 +101,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) @@ -166,7 +164,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/type/nagios.rb b/spec/unit/type/nagios.rb index 35f00b0e5..563c82c2f 100755 --- a/spec/unit/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/type/noop_metaparam.rb b/spec/unit/type/noop_metaparam.rb index 0cbed3714..2a3e0160d 100755 --- a/spec/unit/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/type/package.rb b/spec/unit/type/package.rb index 5d96dc4ae..335910c63 100755 --- a/spec/unit/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/type/schedule.rb b/spec/unit/type/schedule.rb index 4e9840c34..da38f68a9 100755 --- a/spec/unit/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/type/service.rb b/spec/unit/type/service.rb index 0f00992fa..e8358cb22 100755 --- a/spec/unit/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 index d27cb9f25..0e869747d 100755 --- a/spec/unit/type/ssh_authorized_key.rb +++ b/spec/unit/type/ssh_authorized_key.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../../spec_helper' +require File.dirname(__FILE__) + '/../../spec_helper' ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) diff --git a/spec/unit/type/user.rb b/spec/unit/type/user.rb index b697426dc..d16d752f9 100755 --- a/spec/unit/type/user.rb +++ b/spec/unit/type/user.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../../spec_helper' +require File.dirname(__FILE__) + '/../../spec_helper' module UserTestFunctions def mkuser(name) diff --git a/test/ral/providers/provider.rb b/test/ral/providers/provider.rb index 2196fafce..349a56d5e 100755 --- a/test/ral/providers/provider.rb +++ b/test/ral/providers/provider.rb @@ -82,6 +82,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.expects(: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 -- cgit From c9757a689a2ecdcd044ca0355e1ebfa30bb1978f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 22:14:02 -0500 Subject: Moving the 'confine' handling to separate classes. --- lib/puppet/provider/confine.rb | 76 +++++++++++++++++ lib/puppet/provider/confiner.rb | 41 +++++++++ spec/unit/provider/confine.rb | 182 ++++++++++++++++++++++++++++++++++++++++ spec/unit/provider/confiner.rb | 106 +++++++++++++++++++++++ 4 files changed, 405 insertions(+) create mode 100644 lib/puppet/provider/confine.rb create mode 100644 lib/puppet/provider/confiner.rb create mode 100755 spec/unit/provider/confine.rb create mode 100755 spec/unit/provider/confiner.rb diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb new file mode 100644 index 000000000..e55ba02ec --- /dev/null +++ b/lib/puppet/provider/confine.rb @@ -0,0 +1,76 @@ +# The class that handles testing whether our providers +# actually work or not. +class Puppet::Provider::Confine + attr_reader :test, :values, :fact + + def exists?(value) + value and FileTest.exist?(value) + end + + # 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 false?(value) + ! value + end + + def initialize(test, values) + values = [values] unless values.is_a?(Array) + @values = values + + if %w{exists false true}.include?(test.to_s) + @test = test + @method = @test.to_s + "?" + else + @fact = test + @test = :facter + @method = "match?" + end + end + + def match?(value) + facter_value == value.to_s.downcase + end + + # Collect the results of all of them. + def result + values.collect { |value| send(@method, value) } + end + + def true?(value) + # Double negate, so we only get true or false. + ! ! value + end + + # Test whether our confine matches. + def valid? + values.each do |value| + unless send(@method, value) + msg = case test + when :false: "false value" + when :true: "true value" + when :exists: "file %s does not exist" % value + when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] + end + Puppet.debug msg + return false + end + end + + return true + ensure + # 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/confiner.rb b/lib/puppet/provider/confiner.rb new file mode 100644 index 000000000..b88f1f01b --- /dev/null +++ b/lib/puppet/provider/confiner.rb @@ -0,0 +1,41 @@ +# Manage a collection of confines, returning a boolean or +# helpful information. +require 'puppet/provider/confine' + +class Puppet::Provider::Confiner + def confine(hash) + hash.each do |test, values| + @confines << Puppet::Provider::Confine.new(test, values) + end + end + + def initialize + @confines = [] + end + + # Return a hash of the whole confine set, used for the Provider + # reference. + def result + defaults = { + :false => 0, + :true => 0, + :exists => [], + :facter => {} + } + missing = Hash.new { |hash, key| hash[key] = defaults[key] } + @confines.each do |confine| + case confine.test + when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length + when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length + when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } + when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) + end + end + + missing + end + + def valid? + ! @confines.detect { |c| ! c.valid? } + end +end diff --git a/spec/unit/provider/confine.rb b/spec/unit/provider/confine.rb new file mode 100755 index 000000000..0e87ccdfb --- /dev/null +++ b/spec/unit/provider/confine.rb @@ -0,0 +1,182 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/confine' + +describe Puppet::Provider::Confine do + it "should require a test" do + lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) + end + + it "should require a value" do + lambda { Puppet::Provider::Confine.new(:exists) }.should raise_error(ArgumentError) + end + + it "should have a test" do + Puppet::Provider::Confine.new(:exists, "/some/file").test.should == :exists + end + + it "should always convert values to an array" do + Puppet::Provider::Confine.new(:exists, "/some/file").values.should be_instance_of(Array) + end + + it "should have an accessor for its fact" do + Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:fact) + end + + describe "when testing values" do + before { @confine = Puppet::Provider::Confine.new("eh", "foo") } + + describe "and the test is 'false'" do + it "should use the 'false?' method to test validity" do + @confine = Puppet::Provider::Confine.new(:false, "foo") + @confine.expects(:false?).with("foo") + @confine.valid? + end + + it "should return true if the value is false" do + @confine.false?(false).should be_true + end + + it "should return false if the value is not false" do + @confine.false?("else").should be_false + end + + it "should log that a value is false" do + @confine = Puppet::Provider::Confine.new(:false, "foo") + Puppet.expects(:debug).with { |l| l.include?("false") } + @confine.valid? + end + end + + describe "and the test is 'true'" do + it "should use the 'true?' method to test validity" do + @confine = Puppet::Provider::Confine.new(:true, "foo") + @confine.expects(:true?).with("foo") + @confine.valid? + end + + it "should return true if the value is not false" do + @confine.true?("else").should be_true + end + + it "should return false if the value is false" do + @confine.true?(nil).should be_false + end + end + + describe "and the test is 'exists'" do + it "should use the 'exists?' method to test validity" do + @confine = Puppet::Provider::Confine.new(:exists, "foo") + @confine.expects(:exists?).with("foo") + @confine.valid? + end + + it "should return false if the value is false" do + @confine.exists?(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.exists?("/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.exists?("/my/file").should be_true + end + + it "should log that a value is true" do + @confine = Puppet::Provider::Confine.new(:true, nil) + Puppet.expects(:debug).with { |l| l.include?("true") } + @confine.valid? + end + end + + describe "and the test is not 'true', 'false', or 'exists'" do + it "should use the 'match?' method to test validity" do + @confine = Puppet::Provider::Confine.new("yay", "foo") + @confine.expects(:match?).with("foo") + @confine.valid? + end + + it "should return true if the value matches the facter value" do + Facter.expects(:value).returns("foo") + + @confine.match?("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.match?("foo").should be_false + end + + it "should be case insensitive" do + Facter.expects(:value).returns("FOO") + + @confine.match?("foo").should be_true + end + + it "should not care whether the value is a string or symbol" do + Facter.expects(:value).returns("FOO") + + @confine.match?(:foo).should be_true + end + + it "should cache the fact during testing" do + Facter.expects(:value).once.returns("FOO") + + @confine.match?(:foo) + @confine.match?(:foo) + end + + it "should log that the fact value is not correct" do + @confine = Puppet::Provider::Confine.new("foo", ["bar", "bee"]) + Facter.expects(:value).with("foo").returns "yayness" + Puppet.expects(:debug).with { |l| l.include?("facter") and l.include?("bar,bee") } + @confine.valid? + end + end + end + + describe "when testing all values" do + before { @confine = Puppet::Provider::Confine.new(:true, %w{a b c}) } + + it "should be invalid if any values fail" do + @confine.stubs(:true?).returns true + @confine.expects(:true?).with("b").returns false + @confine.should_not be_valid + end + + it "should be valid if all values pass" do + @confine.stubs(:true?).returns true + @confine.should be_valid + end + + it "should short-cut at the first failing value" do + @confine.expects(:true?).once.returns false + @confine.valid? + end + + it "should remove the cached facter value" do + @confine = Puppet::Provider::Confine.new(:foo, :bar) + Facter.expects(:value).with(:foo).times(2).returns "eh" + @confine.valid? + @confine.valid? + end + end + + describe "when testing the result of the values" do + before { @confine = Puppet::Provider::Confine.new(:true, %w{a b c d}) } + + it "should return an array with the result of the test for each value" do + @confine.stubs(:true?).returns true + @confine.expects(:true?).with("b").returns false + @confine.expects(:true?).with("d").returns false + + @confine.result.should == [true, false, true, false] + end + end +end diff --git a/spec/unit/provider/confiner.rb b/spec/unit/provider/confiner.rb new file mode 100755 index 000000000..b80255c95 --- /dev/null +++ b/spec/unit/provider/confiner.rb @@ -0,0 +1,106 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/provider/confiner' + +describe Puppet::Provider::Confiner do + it "should be able to add confines" do + Puppet::Provider::Confiner.new.should respond_to(:confine) + end + + it "should create a Confine instance for every confine call" do + Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns "eh" + Puppet::Provider::Confine.expects(:new).with(:baz, :bee).returns "eh" + Puppet::Provider::Confiner.new.confine :foo => :bar, :baz => :bee + end + + it "should be valid if no confines are present" do + Puppet::Provider::Confiner.new.should be_valid + end + + it "should be valid if all confines are valid" do + c1 = mock 'c1', :valid? => true + c2 = mock 'c2', :valid? => true + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::Confiner.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.should be_valid + end + + it "should not be valid if any confines are valid" do + c1 = mock 'c1', :valid? => true + c2 = mock 'c2', :valid? => false + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::Confiner.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.should_not be_valid + end + + describe "when providing a complete result" do + before do + @confiner = Puppet::Provider::Confiner.new + end + + it "should return a hash" do + @confiner.result.should be_instance_of(Hash) + end + + it "should return an empty hash if the confiner is valid" do + @confiner.result.should == {} + end + + it "should contain the number of incorrectly false values" do + c1 = stub 'c1', :result => [true, false, true], :test => :true + c2 = stub 'c2', :result => [false, true, false], :test => :true + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::Confiner.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.result[:true].should == 3 + end + + it "should contain the number of incorrectly true values" do + c1 = stub 'c1', :result => [true, false, true], :test => :false + c2 = stub 'c2', :result => [false, true, false], :test => :false + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::Confiner.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.result[:false].should == 3 + end + + it "should contain the missing files" do + FileTest.stubs(:exist?).returns true + FileTest.expects(:exist?).with("/two").returns false + FileTest.expects(:exist?).with("/four").returns false + + confiner = Puppet::Provider::Confiner.new + confiner.confine :exists => %w{/one /two} + confiner.confine :exists => %w{/three /four} + + confiner.result[:exists].should == %w{/two /four} + end + + it "should contain a hash of facts and the allowed values" do + Facter.expects(:value).with(:foo).returns "yay" + Facter.expects(:value).with(:bar).returns "boo" + confiner = Puppet::Provider::Confiner.new + confiner.confine :foo => "yes", :bar => "boo" + + result = confiner.result + result[:facter][:foo].should == %w{yes} + result[:facter][:bar].should be_nil + end + end +end -- cgit From 995991d8740baff52cee057752c428d0259e2be1 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 22:21:50 -0500 Subject: Switching the Provider class to use the new Confiner class. --- lib/puppet/provider.rb | 79 ++++++------------------------------------ test/ral/providers/provider.rb | 23 ++++++++---- 2 files changed, 27 insertions(+), 75 deletions(-) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index e73bb0cb6..e0de9d61d 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -5,6 +5,8 @@ class Puppet::Provider include Puppet::Util::Warnings extend Puppet::Util::Warnings + require 'puppet/provider/confiner' + Puppet::Util.logmethods(self, true) class << self @@ -55,13 +57,14 @@ class Puppet::Provider end def self.confine(hash) - hash.each do |p,v| - if v.is_a? Array - @confines[p] += v - else - @confines[p] << v - end + confiner.confine(hash) + end + + def self.confiner + unless defined?(@confiner) + @confiner = Puppet::Provider::Confiner.new end + @confiner end # Is the provided feature a declared feature? @@ -109,9 +112,6 @@ class Puppet::Provider @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 @@ -210,65 +210,8 @@ class Puppet::Provider # 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 + return confiner.valid? if short + return confiner.result end # Does this provider support the specified parameter? diff --git a/test/ral/providers/provider.rb b/test/ral/providers/provider.rb index 349a56d5e..f284e9125 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 -- cgit From a1409d73b4bb8acbf5db2f8d7a244c2bca81db14 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Thu, 15 May 2008 23:09:58 -0500 Subject: Moving all confine code out of the Provider class, and fixing #1197. I created a Confiner module for the Provider class methods, and then I enhanced the interface between it and the Confine class to make sure binary paths are searched for fresh each time. This fixes #1197, which was a result of binary paths being searched for at startup, rather than at execution. --- lib/puppet/provider.rb | 43 ++---------- lib/puppet/provider/confine.rb | 23 ++++-- lib/puppet/provider/confine_collection.rb | 48 +++++++++++++ lib/puppet/provider/confiner.rb | 43 +++--------- spec/unit/provider/confine.rb | 43 ++++++++++++ spec/unit/provider/confine_collection.rb | 113 ++++++++++++++++++++++++++++++ spec/unit/provider/confiner.rb | 110 +++++++++-------------------- test/ral/providers/provider.rb | 2 +- 8 files changed, 274 insertions(+), 151 deletions(-) create mode 100644 lib/puppet/provider/confine_collection.rb create mode 100755 spec/unit/provider/confine_collection.rb diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index e0de9d61d..c02e15029 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -7,6 +7,8 @@ class Puppet::Provider require 'puppet/provider/confiner' + extend Puppet::Provider::Confiner + Puppet::Util.logmethods(self, true) class << self @@ -42,31 +44,16 @@ 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 + confine :exists => path, :for_binary => true end end - def self.confine(hash) - confiner.confine(hash) - end - - def self.confiner - unless defined?(@confiner) - @confiner = Puppet::Provider::Confiner.new - end - @confiner - end - # Is the provided feature a declared feature? def self.declared_feature?(name) defined?(@declared_features) and @declared_features.include?(name) @@ -111,7 +98,6 @@ class Puppet::Provider def self.initvars @defaults = {} @commands = {} - @origcommands = {} 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,12 +185,6 @@ class Puppet::Provider @source end - # Check whether this implementation is suitable for our platform. - def self.suitable?(short = true) - return confiner.valid? if short - return confiner.result - end - # Does this provider support the specified parameter? def self.supports_parameter?(param) if param.is_a?(Class) @@ -252,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 index e55ba02ec..227c923e6 100644 --- a/lib/puppet/provider/confine.rb +++ b/lib/puppet/provider/confine.rb @@ -1,9 +1,22 @@ # The class that handles testing whether our providers # actually work or not. +require 'puppet/util' + class Puppet::Provider::Confine + include Puppet::Util + attr_reader :test, :values, :fact + # Mark that this confine is used for testing binary existence. + attr_accessor :for_binary + def for_binary? + for_binary + end + def exists?(value) + if for_binary? + return false unless value = binary(value) + end value and FileTest.exist?(value) end @@ -57,11 +70,11 @@ class Puppet::Provider::Confine values.each do |value| unless send(@method, value) msg = case test - when :false: "false value" - when :true: "true value" - when :exists: "file %s does not exist" % value - when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] - end + when :false: "false value when expecting true" + when :true: "true value when expecting false" + when :exists: "file %s does not exist" % value + when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] + end Puppet.debug msg return false end diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/provider/confine_collection.rb new file mode 100644 index 000000000..f38035521 --- /dev/null +++ b/lib/puppet/provider/confine_collection.rb @@ -0,0 +1,48 @@ +# 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| + @confines << Puppet::Provider::Confine.new(test, values) + @confines[-1].for_binary = true if for_binary + end + end + + def initialize + @confines = [] + end + + # Return a hash of the whole confine set, used for the Provider + # reference. + def result + defaults = { + :false => 0, + :true => 0, + :exists => [], + :facter => {} + } + missing = Hash.new { |hash, key| hash[key] = defaults[key] } + @confines.each do |confine| + case confine.test + when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length + when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length + when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } + when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) + end + end + + missing + end + + def valid? + ! @confines.detect { |c| ! c.valid? } + end +end diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb index b88f1f01b..3e406873b 100644 --- a/lib/puppet/provider/confiner.rb +++ b/lib/puppet/provider/confiner.rb @@ -1,41 +1,20 @@ -# Manage a collection of confines, returning a boolean or -# helpful information. -require 'puppet/provider/confine' +require 'puppet/provider/confine_collection' -class Puppet::Provider::Confiner +module Puppet::Provider::Confiner def confine(hash) - hash.each do |test, values| - @confines << Puppet::Provider::Confine.new(test, values) - end - end - - def initialize - @confines = [] + confine_collection.confine(hash) end - # Return a hash of the whole confine set, used for the Provider - # reference. - def result - defaults = { - :false => 0, - :true => 0, - :exists => [], - :facter => {} - } - missing = Hash.new { |hash, key| hash[key] = defaults[key] } - @confines.each do |confine| - case confine.test - when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length - when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length - when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } - when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) - end + def confine_collection + unless defined?(@confine_collection) + @confine_collection = Puppet::Provider::ConfineCollection.new end - - missing + @confine_collection end - def valid? - ! @confines.detect { |c| ! c.valid? } + # Check whether this implementation is suitable for our platform. + def suitable?(short = true) + return confine_collection.valid? if short + return confine_collection.result end end diff --git a/spec/unit/provider/confine.rb b/spec/unit/provider/confine.rb index 0e87ccdfb..bb2e751d6 100755 --- a/spec/unit/provider/confine.rb +++ b/spec/unit/provider/confine.rb @@ -25,6 +25,24 @@ describe Puppet::Provider::Confine do Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:fact) end + it "should be possible to mark the confine as a binary test" do + Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:for_binary=) + end + + it "should have a boolean method to indicate it's a binary confine" do + Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:for_binary?) + end + + it "should indicate it's a boolean confine if it has been marked that way" do + confine = Puppet::Provider::Confine.new(:foo, :bar) + confine.for_binary = true + confine.should be_for_binary + end + + it "should have a method for returning a binary's path" do + Puppet::Provider::Confine.new(:foo, :bar).private_methods.should be_include("binary") + end + describe "when testing values" do before { @confine = Puppet::Provider::Confine.new("eh", "foo") } @@ -92,6 +110,31 @@ describe Puppet::Provider::Confine do Puppet.expects(:debug).with { |l| l.include?("true") } @confine.valid? 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.exists?("/my/file") + end + + it "should return false if no binary can be found" do + @confine.expects(:binary).with("/my/file").returns nil + @confine.exists?("/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.exists?("/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.exists?("/my/file").should be_true + end + end end describe "and the test is not 'true', 'false', or 'exists'" do diff --git a/spec/unit/provider/confine_collection.rb b/spec/unit/provider/confine_collection.rb new file mode 100755 index 000000000..3430d604f --- /dev/null +++ b/spec/unit/provider/confine_collection.rb @@ -0,0 +1,113 @@ +#!/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 + + it "should create a Confine instance for every confine call" do + Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns "eh" + Puppet::Provider::Confine.expects(:new).with(:baz, :bee).returns "eh" + Puppet::Provider::ConfineCollection.new.confine :foo => :bar, :baz => :bee + end + + it "should mark each confine as a binary confine if :for_binary => true is included" do + confine = mock 'confine' + confine.expects(:for_binary=).with true + Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns confine + Puppet::Provider::ConfineCollection.new.confine :foo => :bar, :for_binary => true + 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 are valid" do + c1 = mock 'c1', :valid? => true + c2 = mock 'c2', :valid? => true + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::ConfineCollection.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.should be_valid + end + + it "should not be valid if any confines are valid" do + c1 = mock 'c1', :valid? => true + c2 = mock 'c2', :valid? => false + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::ConfineCollection.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.should_not be_valid + end + + describe "when providing a complete result" do + before do + @confiner = Puppet::Provider::ConfineCollection.new + end + + it "should return a hash" do + @confiner.result.should be_instance_of(Hash) + end + + it "should return an empty hash if the confiner is valid" do + @confiner.result.should == {} + end + + it "should contain the number of incorrectly false values" do + c1 = stub 'c1', :result => [true, false, true], :test => :true + c2 = stub 'c2', :result => [false, true, false], :test => :true + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::ConfineCollection.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.result[:true].should == 3 + end + + it "should contain the number of incorrectly true values" do + c1 = stub 'c1', :result => [true, false, true], :test => :false + c2 = stub 'c2', :result => [false, true, false], :test => :false + + Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + + confiner = Puppet::Provider::ConfineCollection.new + confiner.confine :foo => :bar, :baz => :bee + + confiner.result[:false].should == 3 + end + + it "should contain the missing files" do + FileTest.stubs(:exist?).returns true + FileTest.expects(:exist?).with("/two").returns false + FileTest.expects(:exist?).with("/four").returns false + + confiner = Puppet::Provider::ConfineCollection.new + confiner.confine :exists => %w{/one /two} + confiner.confine :exists => %w{/three /four} + + confiner.result[:exists].should == %w{/two /four} + end + + it "should contain a hash of facts and the allowed values" do + Facter.expects(:value).with(:foo).returns "yay" + Facter.expects(:value).with(:bar).returns "boo" + confiner = Puppet::Provider::ConfineCollection.new + confiner.confine :foo => "yes", :bar => "boo" + + result = confiner.result + result[:facter][:foo].should == %w{yes} + result[:facter][:bar].should be_nil + end + end +end diff --git a/spec/unit/provider/confiner.rb b/spec/unit/provider/confiner.rb index b80255c95..38fffc102 100755 --- a/spec/unit/provider/confiner.rb +++ b/spec/unit/provider/confiner.rb @@ -5,102 +5,58 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/provider/confiner' describe Puppet::Provider::Confiner do - it "should be able to add confines" do - Puppet::Provider::Confiner.new.should respond_to(:confine) + before do + @object = Object.new + @object.extend(Puppet::Provider::Confiner) end - it "should create a Confine instance for every confine call" do - Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns "eh" - Puppet::Provider::Confine.expects(:new).with(:baz, :bee).returns "eh" - Puppet::Provider::Confiner.new.confine :foo => :bar, :baz => :bee + it "should have a method for defining confines" do + @object.should respond_to(:confine) end - it "should be valid if no confines are present" do - Puppet::Provider::Confiner.new.should be_valid + it "should have a method for returning its confine collection" do + @object.should respond_to(:confine_collection) end - it "should be valid if all confines are valid" do - c1 = mock 'c1', :valid? => true - c2 = mock 'c2', :valid? => true - - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::Confiner.new - confiner.confine :foo => :bar, :baz => :bee - - confiner.should be_valid + it "should have a method for testing suitability" do + @object.should respond_to(:suitable?) end - it "should not be valid if any confines are valid" do - c1 = mock 'c1', :valid? => true - c2 = mock 'c2', :valid? => false - - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + 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 - confiner = Puppet::Provider::Confiner.new - confiner.confine :foo => :bar, :baz => :bee + 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 - confiner.should_not be_valid + it "should reuse the confine collection" do + @object.confine_collection.should equal(@object.confine_collection) end - describe "when providing a complete result" do + describe "when testing suitability" do before do - @confiner = Puppet::Provider::Confiner.new + @coll = mock 'collection' + @object.stubs(:confine_collection).returns @coll end - it "should return a hash" do - @confiner.result.should be_instance_of(Hash) + it "should return true if the confine collection is valid" do + @coll.expects(:valid?).returns true + @object.should be_suitable end - it "should return an empty hash if the confiner is valid" do - @confiner.result.should == {} - end - - it "should contain the number of incorrectly false values" do - c1 = stub 'c1', :result => [true, false, true], :test => :true - c2 = stub 'c2', :result => [false, true, false], :test => :true - - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::Confiner.new - confiner.confine :foo => :bar, :baz => :bee - - confiner.result[:true].should == 3 + it "should return false if the confine collection is invalid" do + @coll.expects(:valid?).returns false + @object.should_not be_suitable end - it "should contain the number of incorrectly true values" do - c1 = stub 'c1', :result => [true, false, true], :test => :false - c2 = stub 'c2', :result => [false, true, false], :test => :false - - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::Confiner.new - confiner.confine :foo => :bar, :baz => :bee - - confiner.result[:false].should == 3 - end - - it "should contain the missing files" do - FileTest.stubs(:exist?).returns true - FileTest.expects(:exist?).with("/two").returns false - FileTest.expects(:exist?).with("/four").returns false - - confiner = Puppet::Provider::Confiner.new - confiner.confine :exists => %w{/one /two} - confiner.confine :exists => %w{/three /four} - - confiner.result[:exists].should == %w{/two /four} - end - - it "should contain a hash of facts and the allowed values" do - Facter.expects(:value).with(:foo).returns "yay" - Facter.expects(:value).with(:bar).returns "boo" - confiner = Puppet::Provider::Confiner.new - confiner.confine :foo => "yes", :bar => "boo" - - result = confiner.result - result[:facter][:foo].should == %w{yes} - result[:facter][:bar].should be_nil + it "should return the result of the confine collection if a long result is asked for" do + @coll.expects(:result).returns "myresult" + @object.suitable?(false).should == "myresult" end end end diff --git a/test/ral/providers/provider.rb b/test/ral/providers/provider.rb index f284e9125..70f606a37 100755 --- a/test/ral/providers/provider.rb +++ b/test/ral/providers/provider.rb @@ -97,7 +97,7 @@ class TestProvider < Test::Unit::TestCase provider.commands :testing => "/no/such/path" - provider.expects(:binary).returns "/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") -- cgit From 38545d9a81274c16a43f7f19889cd12e52871d76 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 16 May 2008 16:49:22 +1000 Subject: Crontab provider: fix a parse error when a line begins with a space character Tests for Bug #1216 Updated CHANGELOG --- CHANGELOG | 14 +++++++++++--- lib/puppet/provider/cron/crontab.rb | 2 +- test/ral/providers/cron/crontab.rb | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a0c5a1213..870afb92b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,14 @@ - 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; + 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. + + 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. 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/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 -- cgit From 2d6a914e6071681b83e482b40448d9219d6e4d76 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Fri, 16 May 2008 17:17:34 +1000 Subject: Fix for latest method in rpm provider (fixes #1224) --- lib/puppet/provider/package/rpm.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit From 390db8061ae81174ee9d42e26a6ebfe7182ea529 Mon Sep 17 00:00:00 2001 From: James Turnbull Date: Sat, 17 May 2008 01:06:18 +1000 Subject: Updated puppetd documentation which fixes ticket #1227 --- bin/puppetd | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/puppetd b/bin/puppetd index f652e6b08..96d0e5ee8 100755 --- a/bin/puppetd +++ b/bin/puppetd @@ -10,7 +10,7 @@ # # puppetd [-D|--daemonize|--no-daemonize] [-d|--debug] [--disable] [--enable] # [-h|--help] [--fqdn ] [-l|--logdest syslog||console] -# [-o|--onetime] [--serve ] [-t|--test] +# [-o|--onetime] [--serve ] [-t|--test] [--noop] # [-V|--version] [-v|--verbose] [-w|--waitforcert ] # # = Description @@ -57,7 +57,7 @@ # parameter, so you can specify '--server ' 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. # -- cgit From a02c6bbe7ccac05593c9e07e9f0215ab6ceda8ad Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 16 May 2008 11:22:26 -0500 Subject: Fixing a mock in the redhat interface test. It mocked :exists? instead of :exist?, and my provider work changed the method call because :exists? is going away in ruby. --- spec/unit/provider/interface/redhat.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/provider/interface/redhat.rb b/spec/unit/provider/interface/redhat.rb index 5a7a8dfcd..99ac50f01 100755 --- a/spec/unit/provider/interface/redhat.rb +++ b/spec/unit/provider/interface/redhat.rb @@ -9,12 +9,12 @@ 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 -- cgit From 8008bbc447764e0ca28169284f4d6df3a86dcdd6 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 16 May 2008 15:42:20 -0500 Subject: Modified the 'factpath' setting to automatically configure Facter to load facts there if a new enough version of Facter is used. --- CHANGELOG | 4 ++++ lib/puppet/defaults.rb | 8 +++++--- spec/integration/defaults.rb | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100755 spec/integration/defaults.rb diff --git a/CHANGELOG b/CHANGELOG index f076f3784..b0fbf7e4c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ + Modified the 'factpath' setting to automatically configure + Facter to load facts there if a new enough version of + Facter is used. + Added ldap providers for users and groups. Added support for the --all option to puppetca --clean. If diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index df2fb9425..57299b7e7 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -505,9 +505,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."], diff --git a/spec/integration/defaults.rb b/spec/integration/defaults.rb new file mode 100755 index 000000000..b14a141fb --- /dev/null +++ b/spec/integration/defaults.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'puppet/defaults' + +describe "Puppet defaults" do + describe "when setting the :factpath" 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 +end -- cgit From c5da40122493eacf158458f0d79e335e2d0c4c9d Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sat, 17 May 2008 00:54:07 -0700 Subject: Add unit tests for Puppet::Util::Storage --- spec/unit/util/storage.rb | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 spec/unit/util/storage.rb diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb new file mode 100755 index 000000000..38e445127 --- /dev/null +++ b/spec/unit/util/storage.rb @@ -0,0 +1,72 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'yaml' +require 'sync' +require 'tempfile' + +describe Puppet::Util::Storage do + + before(:all) do + Puppet[:statedir] = Dir.tmpdir() + end + + it "it should re-initialize to a clean state when the clear() method is called" do + end + + it "it should use the main settings section if the state dir is not a directory" do + FileTest.expects(:directory?).with(Puppet[:statedir]).returns(false) + Puppet.settings.expects(:use).with(:main) + Puppet::Util::Storage.load() + end + + it "it should initialize with an empty state when the state file does not exist" do + File.expects(:exists?).with(Puppet[:statefile]).returns(false) + Puppet::Util::Storage.load() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end + + describe "when the state file exists" do + it "it should attempt to get a read lock on the file" do + File.expects(:exists?).with(Puppet[:statefile]).returns(true) + Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() + Puppet::Util.expects(:readlock).with(Puppet[:statefile]) + Puppet::Util::Storage.load() + end + + describe "and the file contents are valid" do + it "it should initialize with the correct state from the state file" do + File.expects(:exists?).with(Puppet[:statefile]).returns(true) + Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() + Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) + test_yaml = {'File["/root"]'=>{"name"=>{:a=>:b,:c=>:d}}} + YAML.expects(:load).returns(test_yaml) + + Puppet::Util::Storage.load() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + end + + describe "and the file contents are invalid" do + # Commented out because the previous test's existence causes this one to fail. +# it "it should not initialize from the state file" do +# File.expects(:exists?).with(Puppet[:statefile]).returns(true) +# Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() +# Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) +# YAML.expects(:load).raises(YAML::Error) +# File.expects(:rename).with(Puppet[:statefile], Puppet[:statefile] + ".bad").returns(0) + +# Puppet::Util::Storage.load() +# Puppet::Util::Storage.stateinspect().should == {}.inspect() +# end + + it "it should attempt to rename the state file" do + + end + + end + + end + +end -- cgit From d7f25ff5715a0d17eca8a590df6d9dd8b93ae443 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sat, 17 May 2008 04:26:54 -0700 Subject: Rewritten tests for Puppet::Util::Storage. --- spec/unit/util/storage.rb | 255 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 211 insertions(+), 44 deletions(-) diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index 38e445127..58a1549c8 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -7,66 +7,233 @@ require 'sync' require 'tempfile' describe Puppet::Util::Storage do - before(:all) do Puppet[:statedir] = Dir.tmpdir() + @file_test = Puppet.type(:file).create(:name => "/yayness", :check => %w{checksum type}) + @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") + @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end - it "it should re-initialize to a clean state when the clear() method is called" do + before(:each) do + Puppet::Util::Storage.clear() end - it "it should use the main settings section if the state dir is not a directory" do - FileTest.expects(:directory?).with(Puppet[:statedir]).returns(false) - Puppet.settings.expects(:use).with(:main) - Puppet::Util::Storage.load() + it "it should return an empty hash when caching a symbol" do + Puppet::Util::Storage.cache(:yayness).should == {} + Puppet::Util::Storage.cache(:more_yayness).should == {} end - - it "it should initialize with an empty state when the state file does not exist" do - File.expects(:exists?).with(Puppet[:statefile]).returns(false) - Puppet::Util::Storage.load() + it "it should add the symbol to it's internal state when caching a symbol" do + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() + end + it "it should return an empty hash when caching a Puppet::Type" do + Puppet::Util::Storage.cache(@file_test).should == {} + Puppet::Util::Storage.cache(@exec_test).should == {} + end + it "it should add the resource ref to it's internal state when caching a Puppet::Type" do Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() end - describe "when the state file exists" do - it "it should attempt to get a read lock on the file" do - File.expects(:exists?).with(Puppet[:statefile]).returns(true) - Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() - Puppet::Util.expects(:readlock).with(Puppet[:statefile]) - Puppet::Util::Storage.load() + it "it should raise an ArgumentError when caching invalid objects" do + @bogus_objects.each do |object| + proc { Puppet::Util::Storage.cache(object) }.should raise_error() end - - describe "and the file contents are valid" do - it "it should initialize with the correct state from the state file" do - File.expects(:exists?).with(Puppet[:statefile]).returns(true) - Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() - Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) - test_yaml = {'File["/root"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) - - Puppet::Util::Storage.load() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + it "it should not add anything to it's internal state when caching invalid objects" do + @bogus_objects.each do |object| + begin + Puppet::Util::Storage.cache(object) + rescue + Puppet::Util::Storage.stateinspect().should == {}.inspect() end end + end - describe "and the file contents are invalid" do - # Commented out because the previous test's existence causes this one to fail. -# it "it should not initialize from the state file" do -# File.expects(:exists?).with(Puppet[:statefile]).returns(true) -# Puppet::Util.expects(:benchmark).with(:debug, "Loaded state").yields() -# Puppet::Util.expects(:readlock).with(Puppet[:statefile]).yields(0) -# YAML.expects(:load).raises(YAML::Error) -# File.expects(:rename).with(Puppet[:statefile], Puppet[:statefile] + ".bad").returns(0) - -# Puppet::Util::Storage.load() -# Puppet::Util::Storage.stateinspect().should == {}.inspect() -# end - - it "it should attempt to rename the state file" do - - end + it "it should clear it's internal state when clear() is called" do + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.stateinspect().should == {"Exec[/bin/ls /yayness]"=>{}, :bubblyness=>{}}.inspect() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end - end + it "it should not fail to load if Puppet[:statedir] does not exist" do + transient = Tempfile.new('storage_test') + path = transient.path() + transient.close!() + FileTest.exists?(path).should be_false() + Puppet[:statedir] = path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + + it "it should not fail to load if Puppet[:statefile] does not exist" do + transient = Tempfile.new('storage_test') + path = transient.path() + transient.close!() + FileTest.exists?(path).should be_false() + Puppet[:statefile] = path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end + it "it should not lose it's internal state if load() is called and Puppet[:statefile] does not exist" do + transient = Tempfile.new('storage_test') + path = transient.path() + transient.close!() + FileTest.exists?(path).should be_false() + + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + + Puppet[:statefile] = path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() end + it "it should overwrite it's internal state if load() is called and Puppet[:statefile] exists" do + # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? + state_file = Tempfile.new('storage_test') + + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + + Puppet[:statefile] = state_file.path() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.stateinspect().should == {}.inspect() + + state_file.close!() + end + + it "it should restore it's internal state from Puppet[:statefile] if the file contains valid YAML" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + 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.stateinspect().should == test_yaml.inspect() + + state_file.close!() + end + + it "it should initialize with a clear internal state if the state file does not contain valid YAML" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.write(:booness) + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + + state_file.close!() + end + + it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.write(:booness) + File.chmod(0000, state_file.path()) + + proc { Puppet::Util::Storage.load() }.should raise_error() + + state_file.close!() + end + + it "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 + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + YAML.expects(:load).raises(Puppet::Error) + File.expects(:rename).at_least_once + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + state_file.close!() + end + + it "it should fail gracefully on load() if Puppet[:statefile] is not a regular file" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + 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 "it should fail gracefully on load() if it cannot get a read lock on Puppet[:statefile]" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + 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.stateinspect().should == test_yaml.inspect() + + state_file.close!() + end + + it "it should raise an exception on store() if Puppet[:statefile] is not a regular file" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + state_file.close!() + Dir.mkdir(Puppet[:statefile]) + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should raise_error() + + Dir.rmdir(Puppet[:statefile]) + end + + it "it should raise an exception on store() if it cannot get a write lock on Puppet[:statefile]" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + Puppet::Util.expects(:writelock).yields(false) + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + + proc { Puppet::Util::Storage.store() }.should raise_error() + + state_file.close!() + end + + it "it should load() the same information that it store()s" do + state_file = Tempfile.new('storage_test') + Puppet[:statefile] = state_file.path() + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.cache(:yayness) + + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + proc { Puppet::Util::Storage.store() }.should_not raise_error() + + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + + state_file.close!() + end + + after(:all) do + @bogus_objects.last.close!() + end end -- cgit From ee041293d99605e4283235cb3ee5286447ffadd4 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sat, 17 May 2008 23:51:25 -0700 Subject: Refactored tests based on feedback from Luke. --- spec/unit/util/storage.rb | 345 +++++++++++++++++++++++----------------------- 1 file changed, 169 insertions(+), 176 deletions(-) diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index 58a1549c8..309e5a200 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -9,231 +9,224 @@ require 'tempfile' describe Puppet::Util::Storage do before(:all) do Puppet[:statedir] = Dir.tmpdir() - @file_test = Puppet.type(:file).create(:name => "/yayness", :check => %w{checksum type}) - @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") - @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end before(:each) do Puppet::Util::Storage.clear() end - it "it should return an empty hash when caching a symbol" do - Puppet::Util::Storage.cache(:yayness).should == {} - Puppet::Util::Storage.cache(:more_yayness).should == {} - end - it "it should add the symbol to it's internal state when caching a symbol" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() - Puppet::Util::Storage.cache(:bubblyness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() - end - it "it should return an empty hash when caching a Puppet::Type" do - Puppet::Util::Storage.cache(@file_test).should == {} - Puppet::Util::Storage.cache(@exec_test).should == {} - end - it "it should add the resource ref to it's internal state when caching a Puppet::Type" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() - Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() - end - - it "it should raise an ArgumentError when caching invalid objects" do - @bogus_objects.each do |object| - proc { Puppet::Util::Storage.cache(object) }.should raise_error() + describe "when caching a symbol" do + it "it should return an empty hash" do + Puppet::Util::Storage.cache(:yayness).should == {} + Puppet::Util::Storage.cache(:more_yayness).should == {} end - end - it "it should not add anything to it's internal state when caching invalid objects" do - @bogus_objects.each do |object| - begin - Puppet::Util::Storage.cache(object) - rescue - Puppet::Util::Storage.stateinspect().should == {}.inspect() - end + + it "it should add the symbol to its internal state" do + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.cache(:bubblyness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() end end - it "it should clear it's internal state when clear() is called" do - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() - Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() - Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.cache(:bubblyness) - Puppet::Util::Storage.stateinspect().should == {"Exec[/bin/ls /yayness]"=>{}, :bubblyness=>{}}.inspect() - Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() - 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 "it should not fail to load if Puppet[:statedir] does not exist" do - transient = Tempfile.new('storage_test') - path = transient.path() - transient.close!() - FileTest.exists?(path).should be_false() - Puppet[:statedir] = path - proc { Puppet::Util::Storage.load() }.should_not raise_error() - end + it "it should return an empty hash" do + Puppet::Util::Storage.cache(@file_test).should == {} + Puppet::Util::Storage.cache(@exec_test).should == {} + end - it "it should not fail to load if Puppet[:statefile] does not exist" do - transient = Tempfile.new('storage_test') - path = transient.path() - transient.close!() - FileTest.exists?(path).should be_false() - Puppet[:statefile] = path - proc { Puppet::Util::Storage.load() }.should_not raise_error() + it "it should add the resource ref to its internal state" do + Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.cache(@file_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() + Puppet::Util::Storage.cache(@exec_test) + Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() + end end - it "it should not lose it's internal state if load() is called and Puppet[:statefile] does not exist" do - transient = Tempfile.new('storage_test') - path = transient.path() - transient.close!() - FileTest.exists?(path).should be_false() - - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + describe "when caching invalid objects" do + before(:all) do + @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] + end - Puppet[:statefile] = path - proc { Puppet::Util::Storage.load() }.should_not raise_error() + it "it should raise an ArgumentError" do + @bogus_objects.each do |object| + proc { Puppet::Util::Storage.cache(object) }.should raise_error() + end + end - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + it "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.stateinspect().should == {}.inspect() + end + end + end end - it "it should overwrite it's internal state if load() is called and Puppet[:statefile] exists" do - # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? - state_file = Tempfile.new('storage_test') - - Puppet::Util::Storage.cache(@file_test) + it "it should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() - - Puppet[:statefile] = state_file.path() - proc { Puppet::Util::Storage.load() }.should_not raise_error() - + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.clear() Puppet::Util::Storage.stateinspect().should == {}.inspect() - - state_file.close!() end - it "it should restore it's internal state from Puppet[:statefile] if the file contains valid YAML" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) + 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 "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 "it should not lose its internal state when load() is called" do + FileTest.exists?(@path).should be_false() - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + Puppet::Util::Storage.cache(:yayness) + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + + Puppet[:statefile] = @path + proc { Puppet::Util::Storage.load() }.should_not raise_error() + + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + end + end - state_file.close!() - end - - it "it should initialize with a clear internal state if the state file does not contain valid YAML" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.write(:booness) + 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 - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + it "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.stateinspect().should == {:yayness=>{}}.inspect() - state_file.close!() - end + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end - it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.write(:booness) - File.chmod(0000, state_file.path()) + it "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 raise_error() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + + it "it should initialize with a clear internal state if the state file does not contain valid YAML" do + @state_file.write(:booness) - state_file.close!() - end + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + end - it "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 - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - YAML.expects(:load).raises(Puppet::Error) - File.expects(:rename).at_least_once + it "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_not raise_error() + proc { Puppet::Util::Storage.load() }.should raise_error() + end - state_file.close!() - end + it "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 - it "it should fail gracefully on load() if Puppet[:statefile] is not a regular file" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.close!() - Dir.mkdir(Puppet[:statefile]) - File.expects(:rename).returns(0) + proc { Puppet::Util::Storage.load() }.should_not raise_error() + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() + it "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) - Dir.rmdir(Puppet[:statefile]) - end + proc { Puppet::Util::Storage.load() }.should_not raise_error() - it "it should fail gracefully on load() if it cannot get a read lock on Puppet[:statefile]" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - Puppet::Util.expects(:readlock).yields(false) - test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} - YAML.expects(:load).returns(test_yaml) + Dir.rmdir(Puppet[:statefile]) + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + it "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) - state_file.close!() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + end + + after(:each) do + @state_file.close!() + Puppet[:statefile] = @saved_statefile + end + end end - it "it should raise an exception on store() if Puppet[:statefile] is not a regular file" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - state_file.close!() - Dir.mkdir(Puppet[:statefile]) - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) - - proc { Puppet::Util::Storage.store() }.should raise_error() + 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 - Dir.rmdir(Puppet[:statefile]) - end + it "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) - it "it should raise an exception on store() if it cannot get a write lock on Puppet[:statefile]" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - Puppet::Util.expects(:writelock).yields(false) - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) + proc { Puppet::Util::Storage.store() }.should_not raise_error() + FileTest.exists?(Puppet[:statefile]).should be_true() + end - proc { Puppet::Util::Storage.store() }.should raise_error() + it "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) - state_file.close!() - end + proc { Puppet::Util::Storage.store() }.should raise_error() - it "it should load() the same information that it store()s" do - state_file = Tempfile.new('storage_test') - Puppet[:statefile] = state_file.path() - Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.cache(:yayness) + Dir.rmdir(Puppet[:statefile]) + end - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() - proc { Puppet::Util::Storage.store() }.should_not raise_error() + it "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) - Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + proc { Puppet::Util::Storage.store() }.should raise_error() + end - proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, :yayness=>{}}.inspect() + it "it should load() the same information that it store()s" do + Puppet::Util::Storage.cache(:yayness) - state_file.close!() - end + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + proc { Puppet::Util::Storage.store() }.should_not raise_error() + Puppet::Util::Storage.clear() + Puppet::Util::Storage.stateinspect().should == {}.inspect() + proc { Puppet::Util::Storage.load() }.should_not raise_error() + Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + end - after(:all) do - @bogus_objects.last.close!() + after(:each) do + @state_file.close!() + Puppet[:statefile] = @saved_statefile + end end end -- cgit From 6a6a1d99d7cd5186b3d778007abaa54a27d5e373 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sun, 18 May 2008 15:30:13 -0700 Subject: Another refactor based on feedback from Luke. This includes adding an accessor for @@state to make testing a bit cleaner. --- lib/puppet/util/storage.rb | 4 ++++ spec/unit/util/storage.rb | 42 +++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 19 deletions(-) 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/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index 309e5a200..a1f868cf2 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -22,11 +22,15 @@ describe Puppet::Util::Storage do end it "it should add the symbol to its internal state" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} + end + + it "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.stateinspect().should == {:yayness=>{},:bubblyness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{},:bubblyness=>{}} end end @@ -42,11 +46,11 @@ describe Puppet::Util::Storage do end it "it should add the resource ref to its internal state" do - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} Puppet::Util::Storage.cache(@file_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}}.inspect() + Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}} Puppet::Util::Storage.cache(@exec_test) - Puppet::Util::Storage.stateinspect().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}}.inspect() + Puppet::Util::Storage.state().should == {"File[/yayness]"=>{}, "Exec[/bin/ls /yayness]"=>{}} end end @@ -66,7 +70,7 @@ describe Puppet::Util::Storage do begin Puppet::Util::Storage.cache(object) rescue - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end end end @@ -74,9 +78,9 @@ describe Puppet::Util::Storage do it "it should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end describe "when loading from the state file" do @@ -99,12 +103,12 @@ describe Puppet::Util::Storage do FileTest.exists?(@path).should be_false() Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} Puppet[:statefile] = @path proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} end end @@ -118,10 +122,10 @@ describe Puppet::Util::Storage do it "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.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end it "it should restore its internal state if the state file contains valid YAML" do @@ -129,14 +133,14 @@ describe Puppet::Util::Storage do YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + Puppet::Util::Storage.state().should == test_yaml end it "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.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} end it "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do @@ -170,7 +174,7 @@ describe Puppet::Util::Storage do YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == test_yaml.inspect() + Puppet::Util::Storage.state().should == test_yaml end after(:each) do @@ -216,12 +220,12 @@ describe Puppet::Util::Storage do it "it should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} proc { Puppet::Util::Storage.store() }.should_not raise_error() Puppet::Util::Storage.clear() - Puppet::Util::Storage.stateinspect().should == {}.inspect() + Puppet::Util::Storage.state().should == {} proc { Puppet::Util::Storage.load() }.should_not raise_error() - Puppet::Util::Storage.stateinspect().should == {:yayness=>{}}.inspect() + Puppet::Util::Storage.state().should == {:yayness=>{}} end after(:each) do -- cgit From 0820819e0f7e81f2e68893258145877f756b5395 Mon Sep 17 00:00:00 2001 From: Paul Lathrop Date: Sun, 18 May 2008 16:51:35 -0700 Subject: Minor cosmetic changes to cleanup some style elements and get rid of some cruft. --- spec/unit/util/storage.rb | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/spec/unit/util/storage.rb b/spec/unit/util/storage.rb index a1f868cf2..55d1d1f61 100755 --- a/spec/unit/util/storage.rb +++ b/spec/unit/util/storage.rb @@ -3,9 +3,10 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'yaml' -require 'sync' require 'tempfile' +require 'puppet/util/storage' + describe Puppet::Util::Storage do before(:all) do Puppet[:statedir] = Dir.tmpdir() @@ -16,17 +17,17 @@ describe Puppet::Util::Storage do end describe "when caching a symbol" do - it "it should return an empty hash" do + it "should return an empty hash" do Puppet::Util::Storage.cache(:yayness).should == {} Puppet::Util::Storage.cache(:more_yayness).should == {} end - it "it should add the symbol to its internal state" do + it "should add the symbol to its internal state" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} end - it "it should not clobber existing state when caching additional objects" do + 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) @@ -40,12 +41,12 @@ describe Puppet::Util::Storage do @exec_test = Puppet.type(:exec).create(:name => "/bin/ls /yayness") end - it "it should return an empty hash" do + it "should return an empty hash" do Puppet::Util::Storage.cache(@file_test).should == {} Puppet::Util::Storage.cache(@exec_test).should == {} end - it "it should add the resource ref to its internal state" do + 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]"=>{}} @@ -59,13 +60,13 @@ describe Puppet::Util::Storage do @bogus_objects = [ {}, [], "foo", 42, nil, Tempfile.new('storage_test') ] end - it "it should raise an ArgumentError" do + it "should raise an ArgumentError" do @bogus_objects.each do |object| proc { Puppet::Util::Storage.cache(object) }.should raise_error() end end - it "it should not add anything to its internal state" do + it "should not add anything to its internal state" do @bogus_objects.each do |object| begin Puppet::Util::Storage.cache(object) @@ -76,7 +77,7 @@ describe Puppet::Util::Storage do end end - it "it should clear its internal state when clear() is called" do + 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() @@ -91,7 +92,7 @@ describe Puppet::Util::Storage do transient.close!() end - it "it should not fail to load()" do + 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() @@ -99,7 +100,7 @@ describe Puppet::Util::Storage do proc { Puppet::Util::Storage.load() }.should_not raise_error() end - it "it should not lose its internal state when load() is called" do + it "should not lose its internal state when load() is called" do FileTest.exists?(@path).should be_false() Puppet::Util::Storage.cache(:yayness) @@ -119,7 +120,7 @@ describe Puppet::Util::Storage do Puppet[:statefile] = @state_file.path() end - it "it should overwrite its internal state if load() is called" do + 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=>{}} @@ -128,7 +129,7 @@ describe Puppet::Util::Storage do Puppet::Util::Storage.state().should == {} end - it "it should restore its internal state if the state file contains valid YAML" do + 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) @@ -136,21 +137,21 @@ describe Puppet::Util::Storage do Puppet::Util::Storage.state().should == test_yaml end - it "it should initialize with a clear internal state if the state file does not contain valid YAML" do + 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 "it should raise an error if the state file does not contain valid YAML and cannot be renamed" do + 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 "it should attempt to rename the state file if the file is corrupted" do + 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 @@ -158,7 +159,7 @@ describe Puppet::Util::Storage do proc { Puppet::Util::Storage.load() }.should_not raise_error() end - it "it should fail gracefully on load() if the state file is not a regular file" do + 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) @@ -168,7 +169,7 @@ describe Puppet::Util::Storage do Dir.rmdir(Puppet[:statefile]) end - it "it should fail gracefully on load() if it cannot get a read lock on the state file" do + 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) @@ -191,7 +192,7 @@ describe Puppet::Util::Storage do Puppet[:statefile] = @state_file.path() end - it "it should create the state file if it does not exist" do + 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) @@ -200,7 +201,7 @@ describe Puppet::Util::Storage do FileTest.exists?(Puppet[:statefile]).should be_true() end - it "it should raise an exception if the state file is not a regular file" do + 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) @@ -210,14 +211,14 @@ describe Puppet::Util::Storage do Dir.rmdir(Puppet[:statefile]) end - it "it should raise an exception if it cannot get a write lock on the state file" do + 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 "it should load() the same information that it store()s" do + it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state().should == {:yayness=>{}} -- cgit From 77ee4ec6ca56973ce315358864722a152557857f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 02:05:19 -0500 Subject: Refactoring how the provider confine tests work, again. Now each of the test types is a separate subclass of Confine, so that they can have all of their own logging and summarizing behaviour. Also, added a 'feature' type, which can test for the availability of Puppet features (and log their absence more usefully). --- lib/puppet/provider/confine.rb | 88 ++++++------- lib/puppet/provider/confine/exists.rb | 22 ++++ lib/puppet/provider/confine/facter.rb | 37 ++++++ lib/puppet/provider/confine/false.rb | 19 +++ lib/puppet/provider/confine/feature.rb | 17 +++ lib/puppet/provider/confine/true.rb | 20 +++ lib/puppet/provider/confine_collection.rb | 37 +++--- lib/puppet/provider/confiner.rb | 2 +- lib/puppet/reference/providers.rb | 2 + spec/unit/provider/confine.rb | 200 ++++-------------------------- spec/unit/provider/confine/exists.rb | 80 ++++++++++++ spec/unit/provider/confine/facter.rb | 86 +++++++++++++ spec/unit/provider/confine/false.rb | 52 ++++++++ spec/unit/provider/confine/feature.rb | 59 +++++++++ spec/unit/provider/confine/true.rb | 50 ++++++++ spec/unit/provider/confine_collection.rb | 121 +++++++++--------- spec/unit/provider/confiner.rb | 4 +- 17 files changed, 589 insertions(+), 307 deletions(-) create mode 100644 lib/puppet/provider/confine/exists.rb create mode 100644 lib/puppet/provider/confine/facter.rb create mode 100644 lib/puppet/provider/confine/false.rb create mode 100644 lib/puppet/provider/confine/feature.rb create mode 100644 lib/puppet/provider/confine/true.rb create mode 100755 spec/unit/provider/confine/exists.rb create mode 100755 spec/unit/provider/confine/facter.rb create mode 100755 spec/unit/provider/confine/false.rb create mode 100755 spec/unit/provider/confine/feature.rb create mode 100755 spec/unit/provider/confine/true.rb diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb index 227c923e6..35b80fdcf 100644 --- a/lib/puppet/provider/confine.rb +++ b/lib/puppet/provider/confine.rb @@ -5,85 +5,73 @@ require 'puppet/util' class Puppet::Provider::Confine include Puppet::Util - attr_reader :test, :values, :fact + @tests = {} - # Mark that this confine is used for testing binary existence. - attr_accessor :for_binary - def for_binary? - for_binary + class << self + attr_accessor :name end - def exists?(value) - if for_binary? - return false unless value = binary(value) - end - value and FileTest.exist?(value) - 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 - # Are we a facter comparison? - def facter? - defined?(@facter) + @tests[name] = klass end - # Retrieve the value from facter - def facter_value - unless defined?(@facter_value) and @facter_value - @facter_value = Facter.value(@fact).to_s.downcase + 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 - @facter_value + return @tests[name] end - def false?(value) - ! value + 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(test, values) + def initialize(values) values = [values] unless values.is_a?(Array) @values = values - - if %w{exists false true}.include?(test.to_s) - @test = test - @method = @test.to_s + "?" - else - @fact = test - @test = :facter - @method = "match?" - end end - def match?(value) - facter_value == value.to_s.downcase + # 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| send(@method, value) } - end - - def true?(value) - # Double negate, so we only get true or false. - ! ! value + values.collect { |value| pass?(value) } end # Test whether our confine matches. def valid? values.each do |value| - unless send(@method, value) - msg = case test - when :false: "false value when expecting true" - when :true: "true value when expecting false" - when :exists: "file %s does not exist" % value - when :facter: "facter value '%s' for '%s' not in required list '%s'" % [value, @fact, values.join(",")] - end - Puppet.debug msg + unless pass?(value) + Puppet.debug message(value) return false end end return true ensure - # Reset the cache. We want to cache it during a given - # run, but across runs. - @facter_value = nil + 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 index f38035521..0c80086c9 100644 --- a/lib/puppet/provider/confine_collection.rb +++ b/lib/puppet/provider/confine_collection.rb @@ -11,8 +11,14 @@ class Puppet::Provider::ConfineCollection for_binary = false end hash.each do |test, values| - @confines << Puppet::Provider::Confine.new(test, values) - @confines[-1].for_binary = true if for_binary + 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 @@ -22,24 +28,17 @@ class Puppet::Provider::ConfineCollection # Return a hash of the whole confine set, used for the Provider # reference. - def result - defaults = { - :false => 0, - :true => 0, - :exists => [], - :facter => {} - } - missing = Hash.new { |hash, key| hash[key] = defaults[key] } - @confines.each do |confine| - case confine.test - when :false: missing[confine.test] += confine.result.find_all { |v| v == false }.length - when :true: missing[confine.test] += confine.result.find_all { |v| v == true }.length - when :exists: confine.result.zip(confine.values).each { |val, f| missing[:exists] << f unless val } - when :facter: missing[:facter][confine.fact] = confine.values if confine.result.include?(false) - end - end + 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 - missing + end + result end def valid? diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb index 3e406873b..4605523e8 100644 --- a/lib/puppet/provider/confiner.rb +++ b/lib/puppet/provider/confiner.rb @@ -15,6 +15,6 @@ module Puppet::Provider::Confiner # Check whether this implementation is suitable for our platform. def suitable?(short = true) return confine_collection.valid? if short - return confine_collection.result + return confine_collection.summary 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/spec/unit/provider/confine.rb b/spec/unit/provider/confine.rb index bb2e751d6..6a9214e26 100755 --- a/spec/unit/provider/confine.rb +++ b/spec/unit/provider/confine.rb @@ -5,219 +5,61 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/provider/confine' describe Puppet::Provider::Confine do - it "should require a test" do - lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) - end - it "should require a value" do - lambda { Puppet::Provider::Confine.new(:exists) }.should raise_error(ArgumentError) - end - - it "should have a test" do - Puppet::Provider::Confine.new(:exists, "/some/file").test.should == :exists + lambda { Puppet::Provider::Confine.new() }.should raise_error(ArgumentError) end it "should always convert values to an array" do - Puppet::Provider::Confine.new(:exists, "/some/file").values.should be_instance_of(Array) + Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array) end - it "should have an accessor for its fact" do - Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:fact) + it "should have a 'true' test" do + Puppet::Provider::Confine.test(:true).should be_instance_of(Class) end - it "should be possible to mark the confine as a binary test" do - Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:for_binary=) + it "should have a 'false' test" do + Puppet::Provider::Confine.test(:false).should be_instance_of(Class) end - it "should have a boolean method to indicate it's a binary confine" do - Puppet::Provider::Confine.new(:foo, :bar).should respond_to(:for_binary?) + it "should have a 'feature' test" do + Puppet::Provider::Confine.test(:feature).should be_instance_of(Class) end - it "should indicate it's a boolean confine if it has been marked that way" do - confine = Puppet::Provider::Confine.new(:foo, :bar) - confine.for_binary = true - confine.should be_for_binary + it "should have an 'exists' test" do + Puppet::Provider::Confine.test(:exists).should be_instance_of(Class) end - it "should have a method for returning a binary's path" do - Puppet::Provider::Confine.new(:foo, :bar).private_methods.should be_include("binary") - end - - describe "when testing values" do - before { @confine = Puppet::Provider::Confine.new("eh", "foo") } - - describe "and the test is 'false'" do - it "should use the 'false?' method to test validity" do - @confine = Puppet::Provider::Confine.new(:false, "foo") - @confine.expects(:false?).with("foo") - @confine.valid? - end - - it "should return true if the value is false" do - @confine.false?(false).should be_true - end - - it "should return false if the value is not false" do - @confine.false?("else").should be_false - end - - it "should log that a value is false" do - @confine = Puppet::Provider::Confine.new(:false, "foo") - Puppet.expects(:debug).with { |l| l.include?("false") } - @confine.valid? - end - end - - describe "and the test is 'true'" do - it "should use the 'true?' method to test validity" do - @confine = Puppet::Provider::Confine.new(:true, "foo") - @confine.expects(:true?).with("foo") - @confine.valid? - end - - it "should return true if the value is not false" do - @confine.true?("else").should be_true - end - - it "should return false if the value is false" do - @confine.true?(nil).should be_false - end - end - - describe "and the test is 'exists'" do - it "should use the 'exists?' method to test validity" do - @confine = Puppet::Provider::Confine.new(:exists, "foo") - @confine.expects(:exists?).with("foo") - @confine.valid? - end - - it "should return false if the value is false" do - @confine.exists?(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.exists?("/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.exists?("/my/file").should be_true - end - - it "should log that a value is true" do - @confine = Puppet::Provider::Confine.new(:true, nil) - Puppet.expects(:debug).with { |l| l.include?("true") } - @confine.valid? - 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.exists?("/my/file") - end - - it "should return false if no binary can be found" do - @confine.expects(:binary).with("/my/file").returns nil - @confine.exists?("/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.exists?("/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.exists?("/my/file").should be_true - end - end - end - - describe "and the test is not 'true', 'false', or 'exists'" do - it "should use the 'match?' method to test validity" do - @confine = Puppet::Provider::Confine.new("yay", "foo") - @confine.expects(:match?).with("foo") - @confine.valid? - end - - it "should return true if the value matches the facter value" do - Facter.expects(:value).returns("foo") - - @confine.match?("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.match?("foo").should be_false - end - - it "should be case insensitive" do - Facter.expects(:value).returns("FOO") - - @confine.match?("foo").should be_true - end - - it "should not care whether the value is a string or symbol" do - Facter.expects(:value).returns("FOO") - - @confine.match?(:foo).should be_true - end - - it "should cache the fact during testing" do - Facter.expects(:value).once.returns("FOO") - - @confine.match?(:foo) - @confine.match?(:foo) - end - - it "should log that the fact value is not correct" do - @confine = Puppet::Provider::Confine.new("foo", ["bar", "bee"]) - Facter.expects(:value).with("foo").returns "yayness" - Puppet.expects(:debug).with { |l| l.include?("facter") and l.include?("bar,bee") } - @confine.valid? - end - 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(:true, %w{a b c}) } + before { @confine = Puppet::Provider::Confine.new(%w{a b c}) } it "should be invalid if any values fail" do - @confine.stubs(:true?).returns true - @confine.expects(:true?).with("b").returns false + @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(:true?).returns true + @confine.stubs(:pass?).returns true @confine.should be_valid end it "should short-cut at the first failing value" do - @confine.expects(:true?).once.returns false - @confine.valid? - end - - it "should remove the cached facter value" do - @confine = Puppet::Provider::Confine.new(:foo, :bar) - Facter.expects(:value).with(:foo).times(2).returns "eh" - @confine.valid? + @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(:true, %w{a b c d}) } + 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(:true?).returns true - @confine.expects(:true?).with("b").returns false - @confine.expects(:true?).with("d").returns false + @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 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 index 3430d604f..da4b3fe72 100755 --- a/spec/unit/provider/confine_collection.rb +++ b/spec/unit/provider/confine_collection.rb @@ -9,105 +9,114 @@ describe Puppet::Provider::ConfineCollection do Puppet::Provider::ConfineCollection.new.should respond_to(:confine) end - it "should create a Confine instance for every confine call" do - Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns "eh" - Puppet::Provider::Confine.expects(:new).with(:baz, :bee).returns "eh" - Puppet::Provider::ConfineCollection.new.confine :foo => :bar, :baz => :bee - 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 - it "should mark each confine as a binary confine if :for_binary => true is included" do - confine = mock 'confine' - confine.expects(:for_binary=).with true - Puppet::Provider::Confine.expects(:new).with(:foo, :bar).returns confine - Puppet::Provider::ConfineCollection.new.confine :foo => :bar, :for_binary => true + 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 are valid" do + it "should be valid if all confines pass" do c1 = mock 'c1', :valid? => true c2 = mock 'c2', :valid? => true - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + 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 :foo => :bar, :baz => :bee + confiner.confine :true => :bar, :false => :bee confiner.should be_valid end - it "should not be valid if any confines are valid" do - c1 = mock 'c1', :valid? => true - c2 = mock 'c2', :valid? => false + it "should not be valid if any confines fail" do + c1 = stub 'c1', :valid? => true + c2 = stub 'c2', :valid? => false - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) + 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 :foo => :bar, :baz => :bee + confiner.confine :true => :bar, :false => :bee confiner.should_not be_valid end - describe "when providing a complete result" do + describe "when providing a summary" do before do @confiner = Puppet::Provider::ConfineCollection.new end it "should return a hash" do - @confiner.result.should be_instance_of(Hash) + @confiner.summary.should be_instance_of(Hash) end it "should return an empty hash if the confiner is valid" do - @confiner.result.should == {} + @confiner.summary.should == {} end - it "should contain the number of incorrectly false values" do - c1 = stub 'c1', :result => [true, false, true], :test => :true - c2 = stub 'c2', :result => [false, true, false], :test => :true - - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => :bar, :baz => :bee + 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.result[:true].should == 3 + @confiner.summary.should == {:true => :tsumm, :false => :fsumm} end - it "should contain the number of incorrectly true values" do - c1 = stub 'c1', :result => [true, false, true], :test => :false - c2 = stub 'c2', :result => [false, true, false], :test => :false + 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 - Puppet::Provider::Confine.expects(:new).times(2).returns(c1).then.returns(c2) - - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => :bar, :baz => :bee - - confiner.result[:false].should == 3 + @confiner.summary.should == {:false => :fsumm} end - it "should contain the missing files" do - FileTest.stubs(:exist?).returns true - FileTest.expects(:exist?).with("/two").returns false - FileTest.expects(:exist?).with("/four").returns false - - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :exists => %w{/one /two} - confiner.confine :exists => %w{/three /four} + 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.result[:exists].should == %w{/two /four} + @confiner.summary.should == {:false => :fsumm} end - it "should contain a hash of facts and the allowed values" do - Facter.expects(:value).with(:foo).returns "yay" - Facter.expects(:value).with(:bar).returns "boo" - confiner = Puppet::Provider::ConfineCollection.new - confiner.confine :foo => "yes", :bar => "boo" + 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 - result = confiner.result - result[:facter][:foo].should == %w{yes} - result[:facter][:bar].should be_nil + @confiner.summary.should == {:false => :fsumm} end end end diff --git a/spec/unit/provider/confiner.rb b/spec/unit/provider/confiner.rb index 38fffc102..078fc4420 100755 --- a/spec/unit/provider/confiner.rb +++ b/spec/unit/provider/confiner.rb @@ -54,8 +54,8 @@ describe Puppet::Provider::Confiner do @object.should_not be_suitable end - it "should return the result of the confine collection if a long result is asked for" do - @coll.expects(:result).returns "myresult" + 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 -- cgit From b8ce6a1a8c5dc370cae86cc3a40450d472e6843c Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 22:10:51 -0500 Subject: Mocking Facter in an integration test, so it works with no networking --- spec/integration/node/catalog.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/integration/node/catalog.rb b/spec/integration/node/catalog.rb index ca14c2ea8..941d2cc6c 100755 --- a/spec/integration/node/catalog.rb +++ b/spec/integration/node/catalog.rb @@ -7,6 +7,12 @@ require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Node::Catalog do describe "when using the indirector" do + 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 + after { Puppet::Node::Catalog.indirection.clear_cache } it "should be able to delegate to the :yaml terminus" do -- cgit From ee4be4f78f7c904dbe5873ff7b44993d1440da41 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 19 May 2008 22:16:13 -0500 Subject: Removing an unused file. Closes #1229. --- lib/puppet/util/variables.rb | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 lib/puppet/util/variables.rb 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 - -- cgit