diff options
author | Markus Roberts <Markus@reality.com> | 2011-02-01 16:22:06 -0800 |
---|---|---|
committer | Markus Roberts <Markus@reality.com> | 2011-02-01 16:22:06 -0800 |
commit | adb8c7994ea7d2d30aaea698fbe08727ed553fef (patch) | |
tree | 3f8f7b605dc9ad999409353266e7e3130667e697 | |
parent | 517c6794606e9adde7f2912d3b949cfcc18a446a (diff) | |
parent | 443eba9b7a85cffa6b8513a32a620c80917be768 (diff) | |
download | puppet-adb8c7994ea7d2d30aaea698fbe08727ed553fef.tar.gz puppet-adb8c7994ea7d2d30aaea698fbe08727ed553fef.tar.xz puppet-adb8c7994ea7d2d30aaea698fbe08727ed553fef.zip |
Merge branch '2.6.next' of git://github.com/puppetlabs/puppet into 2.6.next
54 files changed, 715 insertions, 232 deletions
diff --git a/lib/puppet/indirector/resource/ral.rb b/lib/puppet/indirector/resource/ral.rb index 1c2ab14ae..bc41d14ae 100644 --- a/lib/puppet/indirector/resource/ral.rb +++ b/lib/puppet/indirector/resource/ral.rb @@ -34,12 +34,17 @@ class Puppet::Resource::Ral < Puppet::Indirector::Code private + # {type,resource}_name: the resource name may contain slashes: + # File["/etc/hosts"]. To handle, assume the type name does + # _not_ have any slashes in it, and split only on the first. + def type_name( request ) - request.key.split('/')[0] + request.key.split('/', 2)[0] end def resource_name( request ) - request.key.split('/')[1] + name = request.key.split('/', 2)[1] + name unless name == "" end def type( request ) diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index c60e1d4fb..fdabd05c9 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -139,12 +139,23 @@ class Puppet::Parser::Compiler def evaluate_classes(classes, scope, lazy_evaluate = true) raise Puppet::DevError, "No source for scope passed to evaluate_classes" unless scope.source found = [] + param_classes = nil + # if we are a param class, save the classes hash + # and transform classes to be the keys + if classes.class == Hash + param_classes = classes + classes = classes.keys + end classes.each do |name| # If we can find the class, then make a resource that will evaluate it. if klass = scope.find_hostclass(name) - found << name and next if scope.class_scope(klass) - resource = klass.ensure_in_catalog(scope) + if param_classes + resource = klass.ensure_in_catalog(scope, param_classes[name] || {}) + else + found << name and next if scope.class_scope(klass) + resource = klass.ensure_in_catalog(scope) + end # If they've disabled lazy evaluation (which the :include function does), # then evaluate our resource immediately. @@ -432,7 +443,11 @@ class Puppet::Parser::Compiler @resources = [] # Make sure any external node classes are in our class list - @catalog.add_class(*@node.classes) + if @node.classes.class == Hash + @catalog.add_class(*@node.classes.keys) + else + @catalog.add_class(*@node.classes) + end end # Set the node's parameters into the top-scope as variables. diff --git a/lib/puppet/parser/functions/defined.rb b/lib/puppet/parser/functions/defined.rb index 90632af2f..2aeaa9ba0 100644 --- a/lib/puppet/parser/functions/defined.rb +++ b/lib/puppet/parser/functions/defined.rb @@ -1,10 +1,32 @@ # Test whether a given class or definition is defined -Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :doc => "Determine whether a given - 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 - dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals| +Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :doc => "Determine whether + a given class or resource type is defined. This function can also determine whether a + specific resource has been declared. Returns true or false. Accepts class names, + type names, and resource references. + + The `defined` function checks both native and defined types, including types + provided as plugins via modules. Types and classes are both checked using their names: + + defined(\"file\") + defined(\"customtype\") + defined(\"foo\") + defined(\"foo::bar\") + + Resource declarations are checked using resource references, e.g. + `defined( File['/tmp/myfile'] )`. Checking whether a given resource + has been declared is, unfortunately, dependent on the parse order of + the configuration, and the following code will not work: + + if defined(File['/tmp/foo']) { + notify(\"This configuration includes the /tmp/foo file.\") + } + file {\"/tmp/foo\": + ensure => present, + } + + However, this order requirement refers to parse order only, and ordering of + resources in the configuration graph (e.g. with `before` or `require`) does not + affect the behavior of `defined`.") do |vals| result = false vals = [vals] unless vals.is_a?(Array) vals.each do |val| diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 84e1a0360..12f496a6e 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -152,9 +152,24 @@ class Puppet::Property < Puppet::Parameter # since we cannot fix it. Otherwise, we expect our should value # to be an array, and if @is matches any of those values, then # we consider it to be in-sync. - def insync?(is) + # + # Don't override this method. + def safe_insync?(is) + # If there is no @should value, consider the property to be in sync. return true unless @should + # Otherwise delegate to the (possibly derived) insync? method. + insync?(is) + end + + def self.method_added(sym) + raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync? + end + + # This method should be overridden by derived classes if necessary + # to provide extra logic to determine whether the property is in + # sync. + def insync?(is) self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array) # an empty array is analogous to no should values diff --git a/lib/puppet/property/keyvalue.rb b/lib/puppet/property/keyvalue.rb index 0181708f9..57d0ea2d9 100644 --- a/lib/puppet/property/keyvalue.rb +++ b/lib/puppet/property/keyvalue.rb @@ -77,8 +77,6 @@ module Puppet end def insync?(is) - return true unless @should - return true unless is (is == self.should) diff --git a/lib/puppet/property/list.rb b/lib/puppet/property/list.rb index dcee85db7..b86dc87f2 100644 --- a/lib/puppet/property/list.rb +++ b/lib/puppet/property/list.rb @@ -66,8 +66,6 @@ module Puppet end def insync?(is) - return true unless @should - return true unless is (prepare_is_for_comparison(is) == self.should) diff --git a/lib/puppet/provider/file/posix.rb b/lib/puppet/provider/file/posix.rb index 6cbf98e9a..f7b8c9797 100644 --- a/lib/puppet/provider/file/posix.rb +++ b/lib/puppet/provider/file/posix.rb @@ -27,9 +27,7 @@ Puppet::Type.type(:file).provide :posix do end end - def insync?(current, should) - return true unless should - + def is_owner_insync?(current, should) should.each do |value| if value =~ /^\d+$/ uid = Integer(value) diff --git a/lib/puppet/provider/file/win32.rb b/lib/puppet/provider/file/win32.rb index 8ead69a89..21e7ca974 100644 --- a/lib/puppet/provider/file/win32.rb +++ b/lib/puppet/provider/file/win32.rb @@ -14,9 +14,7 @@ Puppet::Type.type(:file).provide :microsoft_windows do id end - def insync?(current, should) - return true unless should - + def is_owner_insync?(current, should) should.each do |value| if value =~ /^\d+$/ uid = Integer(value) diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index 8c7b24bd4..c979f742f 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -43,6 +43,8 @@ module Puppet::Provider::Mount line =~ / on #{name} / or line =~ %r{ on /private/var/automount#{name}} when "Solaris", "HP-UX" line =~ /^#{name} on / + when "AIX" + line.split(/\s+/)[1] == name else line =~ / on #{name} / end diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb index 965a2aa60..b01880360 100644 --- a/lib/puppet/provider/nameservice/directoryservice.rb +++ b/lib/puppet/provider/nameservice/directoryservice.rb @@ -442,7 +442,7 @@ class DirectoryService < Puppet::Provider::NameService def remove_unwanted_members(current_members, new_members) current_members.each do |member| - if not new_members.include?(member) + if not new_members.flatten.include?(member) cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]] begin execute(cmd) @@ -454,7 +454,7 @@ class DirectoryService < Puppet::Provider::NameService end def add_members(current_members, new_members) - new_members.each do |new_member| + new_members.flatten.each do |new_member| if current_members.nil? or not current_members.include?(new_member) cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", new_member, @resource[:name]] begin diff --git a/lib/puppet/provider/package/freebsd.rb b/lib/puppet/provider/package/freebsd.rb index 2f012a4ed..e10a20b04 100755 --- a/lib/puppet/provider/package/freebsd.rb +++ b/lib/puppet/provider/package/freebsd.rb @@ -20,11 +20,11 @@ Puppet::Type.type(:package).provide :freebsd, :parent => :openbsd do if @resource[:source] =~ /\/$/ if @resource[:source] =~ /^(ftp|https?):/ - withenv :PACKAGESITE => @resource[:source] do + Puppet::Util::Execution::withenv :PACKAGESITE => @resource[:source] do pkgadd "-r", @resource[:name] end else - withenv :PKG_PATH => @resource[:source] do + Puppet::Util::Execution::withenv :PKG_PATH => @resource[:source] do pkgadd @resource[:name] end end diff --git a/lib/puppet/provider/zone/solaris.rb b/lib/puppet/provider/zone/solaris.rb index c11444993..f46337b14 100644 --- a/lib/puppet/provider/zone/solaris.rb +++ b/lib/puppet/provider/zone/solaris.rb @@ -40,7 +40,7 @@ Puppet::Type.type(:zone).provide(:solaris) do # Then perform all of our configuration steps. It's annoying # that we need this much internal info on the resource. @resource.send(:properties).each do |property| - str += property.configtext + "\n" if property.is_a? ZoneConfigProperty and ! property.insync?(properties[property.name]) + str += property.configtext + "\n" if property.is_a? ZoneConfigProperty and ! property.safe_insync?(properties[property.name]) end str += "commit\n" diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb index e6a8dc20f..c8ff145ba 100644 --- a/lib/puppet/reference/configuration.rb +++ b/lib/puppet/reference/configuration.rb @@ -94,11 +94,11 @@ The file follows INI-style formatting. Here is an example of a very simple Note that boolean parameters must be explicitly specified as `true` or `false` as seen above. -If you need to change file parameters (e.g., reset the mode or owner), do +If you need to change file or directory parameters (e.g., reset the mode or owner), do so within curly braces on the same line: [main] - myfile = /tmp/whatever {owner = root, mode = 644} + vardir = /new/vardir {owner = root, mode = 644} If you're starting out with a fresh configuration, you may wish to let the executable generate a template configuration file for you by invoking diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index d40adc145..34fddf135 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -143,18 +143,26 @@ class Puppet::Resource::Type # classes and nodes. No parameters are be supplied--if this is a # parameterized class, then all parameters take on their default # values. - def ensure_in_catalog(scope) + def ensure_in_catalog(scope, parameters=nil) type == :definition and raise ArgumentError, "Cannot create resources for defined resource types" resource_type = type == :hostclass ? :class : :node # Do nothing if the resource already exists; this makes sure we don't # get multiple copies of the class resource, which helps provide the # singleton nature of classes. - if resource = scope.catalog.resource(resource_type, name) + # we should not do this for classes with parameters + # if parameters are passed, we should still try to create the resource + # even if it exists so that we can fail + # this prevents us from being able to combine param classes with include + if resource = scope.catalog.resource(resource_type, name) and !parameters return resource end - resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self) + if parameters + parameters.each do |k,v| + resource.set_parameter(k,v) + end + end instantiate_resource(scope, resource) scope.compiler.add_resource(scope, resource) resource @@ -222,6 +230,19 @@ class Puppet::Resource::Type set[param] = true end + if @type == :hostclass + scope.setvar("title", resource.title.to_s.downcase) unless set.include? :title + scope.setvar("name", resource.name.to_s.downcase ) unless set.include? :name + else + scope.setvar("title", resource.title ) unless set.include? :title + scope.setvar("name", resource.name ) unless set.include? :name + end + scope.setvar("module_name", module_name) if module_name and ! set.include? :module_name + + if caller_name = scope.parent_module_name and ! set.include?(:caller_module_name) + scope.setvar("caller_module_name", caller_name) + end + scope.class_set(self.name,scope) if hostclass? or node? # Verify that all required arguments are either present or # have been provided with defaults. arguments.each do |param, default| @@ -238,19 +259,6 @@ class Puppet::Resource::Type resource[param] = value end - if @type == :hostclass - scope.setvar("title", resource.title.to_s.downcase) unless set.include? :title - scope.setvar("name", resource.name.to_s.downcase ) unless set.include? :name - else - scope.setvar("title", resource.title ) unless set.include? :title - scope.setvar("name", resource.name ) unless set.include? :name - end - scope.setvar("module_name", module_name) if module_name and ! set.include? :module_name - - if caller_name = scope.parent_module_name and ! set.include?(:caller_module_name) - scope.setvar("caller_module_name", caller_name) - end - scope.class_set(self.name,scope) if hostclass? or node? end # Create a new subscope in which to evaluate our code. diff --git a/lib/puppet/transaction/resource_harness.rb b/lib/puppet/transaction/resource_harness.rb index c1b980632..4a3d35e0d 100644 --- a/lib/puppet/transaction/resource_harness.rb +++ b/lib/puppet/transaction/resource_harness.rb @@ -52,13 +52,13 @@ class Puppet::Transaction::ResourceHarness # Update the machine state & create logs/events events = [] ensure_param = resource.parameter(:ensure) - if desired_values[:ensure] && !ensure_param.insync?(current_values[:ensure]) + if desired_values[:ensure] && !ensure_param.safe_insync?(current_values[:ensure]) events << apply_parameter(ensure_param, current_values[:ensure], audited_params.include?(:ensure), historical_values[:ensure]) synced_params << :ensure elsif current_values[:ensure] != :absent work_order = resource.properties # Note: only the resource knows what order to apply changes in work_order.each do |param| - if desired_values[param.name] && !param.insync?(current_values[param.name]) + if desired_values[param.name] && !param.safe_insync?(current_values[param.name]) events << apply_parameter(param, current_values[param.name], audited_params.include?(param.name), historical_values[param.name]) synced_params << param.name end @@ -175,22 +175,4 @@ class Puppet::Transaction::ResourceHarness return nil unless name = resource[:schedule] resource.catalog.resource(:schedule, name) || resource.fail("Could not find schedule #{name}") end - - private - - def absent_and_not_being_created?(current, param) - current[:ensure] == :absent and param.should.nil? - end - - def ensure_is_insync?(current, param) - param.insync?(current[:ensure]) - end - - def ensure_should_be_absent?(current, param) - param.should == :absent - end - - def param_is_insync?(current, param) - param.insync?(current[param.name]) - end end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index ea3944b4e..e03650b54 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -648,7 +648,7 @@ class Type "The is value is not in the is array for '#{property.name}'" end ensureis = is[property] - if property.insync?(ensureis) and property.should == :absent + if property.safe_insync?(ensureis) and property.should == :absent return true end end @@ -660,7 +660,7 @@ class Type end propis = is[property] - unless property.insync?(propis) + unless property.safe_insync?(propis) property.debug("Not in sync: #{propis.inspect} vs #{property.should.inspect}") insync = false #else diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index 76399d693..4b18e71f9 100755 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -54,11 +54,7 @@ Puppet::Type.newtype(:cron) do # We have to override the parent method, because we consume the entire # "should" array def insync?(is) - if @should - self.is_to_s(is) == self.should_to_s - else - true - end + self.is_to_s(is) == self.should_to_s end # A method used to do parameter input handling. Converts integers diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 606888c75..8019af67e 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -9,41 +9,11 @@ module Puppet commands is to use the checks like `creates` to avoid running the command unless some condition is met. - Note also that you can restrict an `exec` to only run when it receives + Note that you can restrict an `exec` to only run when it receives events by using the `refreshonly` parameter; this is a useful way to have your configuration respond to events with arbitrary commands. - It is worth noting that `exec` is special, in that it is not - currently considered an error to have multiple `exec` instances - with the same name. This was done purely because it had to be this - way in order to get certain functionality, but it complicates things. - In particular, you will not be able to use `exec` instances that - share their commands with other instances as a dependency, since - Puppet has no way of knowing which instance you mean. - - For example: - - # defined in the production class - exec { \"make\": - cwd => \"/prod/build/dir\", - path => \"/usr/bin:/usr/sbin:/bin\" - } - - . etc. . - - # defined in the test class - exec { \"make\": - cwd => \"/test/build/dir\", - path => \"/usr/bin:/usr/sbin:/bin\" - } - - Any other type would throw an error, complaining that you had - the same instance being managed in multiple places, but these are - obviously different images, so `exec` had to be treated specially. - - It is recommended to avoid duplicate names whenever possible. - - Note that if an `exec` receives an event from another resource, + Note also that if an `exec` receives an event from another resource, it will get executed again (or execute the command specified in `refresh`, if there is one). There is a strong tendency to use `exec` to do whatever work Puppet diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index eee948cd5..cbb51bbed 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -34,7 +34,7 @@ Puppet::Type.newtype(:file) do validate do |value| # accept various path syntaxes: lone slash, posix, win32, unc - unless (Puppet.features.posix? and (value =~ /^\/$/ or value =~ /^\/[^\/]/)) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) + unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) fail Puppet::Error, "File paths must be fully qualified, not '#{value}'" end end @@ -271,17 +271,24 @@ Puppet::Type.newtype(:file) do end CREATORS = [:content, :source, :target] + SOURCE_ONLY_CHECKSUMS = [:none, :ctime, :mtime] validate do - count = 0 + creator_count = 0 CREATORS.each do |param| - count += 1 if self.should(param) + creator_count += 1 if self.should(param) end - count += 1 if @parameters.include?(:source) - self.fail "You cannot specify more than one of #{CREATORS.collect { |p| p.to_s}.join(", ")}" if count > 1 + creator_count += 1 if @parameters.include?(:source) + self.fail "You cannot specify more than one of #{CREATORS.collect { |p| p.to_s}.join(", ")}" if creator_count > 1 self.fail "You cannot specify a remote recursion without a source" if !self[:source] and self[:recurse] == :remote + self.fail "You cannot specify source when using checksum 'none'" if self[:checksum] == :none && !self[:source].nil? + + SOURCE_ONLY_CHECKSUMS.each do |checksum_type| + self.fail "You cannot specify content when using checksum '#{checksum_type}'" if self[:checksum] == checksum_type && !self[:content].nil? + end + self.warning "Possible error: recurselimit is set but not recurse, no recursion will happen" if !self[:recurse] and self[:recurselimit] end @@ -290,25 +297,8 @@ Puppet::Type.newtype(:file) do super(path.gsub(/\/+/, '/').sub(/\/$/, '')) end - # List files, but only one level deep. - def self.instances(base = "/") - return [] unless FileTest.directory?(base) - - files = [] - Dir.entries(base).reject { |e| - e == "." or e == ".." - }.each do |name| - path = File.join(base, name) - if obj = self[path] - obj[:audit] = :all - files << obj - else - files << self.new( - :name => path, :audit => :all - ) - end - end - files + def self.instances(base = '/') + return self.new(:name => base, :recurse => true, :recurselimit => 1, :audit => :all).recurse_local.values end @depthfirst = false @@ -779,7 +769,7 @@ Puppet::Type.newtype(:file) do # Make sure we get a new stat objct expire currentvalue = thing.retrieve - thing.sync unless thing.insync?(currentvalue) + thing.sync unless thing.safe_insync?(currentvalue) end end end diff --git a/lib/puppet/type/file/checksum.rb b/lib/puppet/type/file/checksum.rb index 732460738..5586b1383 100755 --- a/lib/puppet/type/file/checksum.rb +++ b/lib/puppet/type/file/checksum.rb @@ -9,7 +9,7 @@ Puppet::Type.type(:file).newparam(:checksum) do The default checksum parameter, if checksums are enabled, is md5." - newvalues "md5", "md5lite", "timestamp", "mtime", "time", "none" + newvalues "md5", "md5lite", "mtime", "ctime", "none" defaultto :md5 diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index b8f30a9c7..cf924f371 100755 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -96,7 +96,6 @@ module Puppet end return true if ! @resource.replace? - return true unless self.should result = super @@ -168,6 +167,8 @@ module Puppet def each_chunk_from(source_or_content) if source_or_content.is_a?(String) yield source_or_content + elsif source_or_content.nil? && resource.parameter(:ensure) && [:present, :file].include?(resource.parameter(:ensure).value) + yield '' elsif source_or_content.nil? yield read_file_from_filebucket elsif self.class.standalone? diff --git a/lib/puppet/type/file/owner.rb b/lib/puppet/type/file/owner.rb index d473da20e..483cc7fce 100755 --- a/lib/puppet/type/file/owner.rb +++ b/lib/puppet/type/file/owner.rb @@ -6,7 +6,7 @@ module Puppet @event = :file_changed def insync?(current) - provider.insync?(current, @should) + provider.is_owner_insync?(current, @should) end # We want to print names, not numbers diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index 7d03de2b0..bc464e1c3 100755 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -169,7 +169,6 @@ module Puppet checks.delete(:checksum) resource[:audit] = checks - resource[:checksum] = :md5 unless resource.property(:checksum) end def local? diff --git a/lib/puppet/type/file/target.rb b/lib/puppet/type/file/target.rb index 9e7229dda..b9fe9213b 100644 --- a/lib/puppet/type/file/target.rb +++ b/lib/puppet/type/file/target.rb @@ -14,7 +14,7 @@ module Puppet # Only call mklink if ensure didn't call us in the first place. currentensure = @resource.property(:ensure).retrieve - mklink if @resource.property(:ensure).insync?(currentensure) + mklink if @resource.property(:ensure).safe_insync?(currentensure) end # Create our link. diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb index 8ab750459..2666e50ae 100755 --- a/lib/puppet/type/host.rb +++ b/lib/puppet/type/host.rb @@ -66,7 +66,7 @@ module Puppet newproperty(:target) do desc "The file in which to store service information. Only used by - those providers that write to disk." + those providers that write to disk. On most systems this defaults to `/etc/hosts`." defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) @resource.class.defaultprovider.default_target diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb index d048c90f1..da9a70bdf 100755 --- a/lib/puppet/type/mount.rb +++ b/lib/puppet/type/mount.rb @@ -89,7 +89,7 @@ module Puppet if prop.name == :ensure false else - ! prop.insync?(currentvalues[prop]) + ! prop.safe_insync?(currentvalues[prop]) end end.each { |prop| prop.sync }.length @resource.flush if oos > 0 @@ -200,7 +200,7 @@ module Puppet newvalues(:true, :false) defaultto do case Facter.value(:operatingsystem) - when "FreeBSD", "Darwin" + when "FreeBSD", "Darwin", "AIX" false else true diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index 51a866332..d73d90dff 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -109,8 +109,6 @@ module Puppet # Override the parent method, because we've got all kinds of # funky definitions of 'in sync'. def insync?(is) - @should ||= [] - @latest ||= nil @lateststamp ||= (Time.now.to_i - 1000) # Iterate across all of the should values, and see how they diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb index c00f02789..0d09c3d5d 100644 --- a/lib/puppet/type/service.rb +++ b/lib/puppet/type/service.rb @@ -73,7 +73,7 @@ module Puppet if property = @resource.property(:enable) val = property.retrieve - property.sync unless property.insync?(val) + property.sync unless property.safe_insync?(val) end event @@ -144,10 +144,16 @@ module Puppet specified." end newparam(:status) do - desc "Specify a *status* command manually. If left - unspecified, the status method will be determined - automatically, usually by looking for the service in the - process table." + desc "Specify a *status* command manually. This command must + return 0 if the service is running and a nonzero value otherwise. + Ideally, these return codes should conform to + [the LSB's specification for init script status actions](http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html), + but puppet only considers the difference between 0 and nonzero + to be relevant. + + If left unspecified, the status method will be determined + automatically, usually by looking for the service in the process + table." end newparam(:stop) do diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index 761d5d71b..e7389a0d1 100755 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -112,8 +112,6 @@ module Puppet end def insync?(is) - return true unless self.should - # We know the 'is' is a number, so we need to convert the 'should' to a number, # too. @should.each do |value| @@ -177,7 +175,7 @@ module Puppet end validate do |value| - if value.to_s !~ /^\d+$/ + if value.to_s !~ /^-?\d+$/ raise ArgumentError, "Password minimum age must be provided as a number" end end @@ -196,7 +194,7 @@ module Puppet end validate do |value| - if value.to_s !~ /^\d+$/ + if value.to_s !~ /^-?\d+$/ raise ArgumentError, "Password maximum age must be provided as a number" end end diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb index 160b2164d..9b4c79428 100644 --- a/lib/puppet/type/yumrepo.rb +++ b/lib/puppet/type/yumrepo.rb @@ -7,14 +7,14 @@ module Puppet class IniProperty < Puppet::Property def insync?(is) # A should property of :absent is the same as nil - if is.nil? && (should.nil? || should == :absent) + if is.nil? && should == :absent return true end super(is) end def sync - if insync?(retrieve) + if safe_insync?(retrieve) result = nil else result = set(self.should) diff --git a/lib/puppet/type/zpool.rb b/lib/puppet/type/zpool.rb index 49cce552a..df06522e8 100755 --- a/lib/puppet/type/zpool.rb +++ b/lib/puppet/type/zpool.rb @@ -8,8 +8,6 @@ module Puppet end def insync?(is) - return true unless self.should - return @should == [:absent] if is == :absent flatten_and_sort(is) == flatten_and_sort(@should) @@ -18,8 +16,6 @@ module Puppet class MultiVDev < VDev def insync?(is) - return true unless self.should - return @should == [:absent] if is == :absent return false unless is.length == @should.length diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb index 6fdf14ecf..e129301e6 100644 --- a/lib/puppet/util/checksums.rb +++ b/lib/puppet/util/checksums.rb @@ -68,7 +68,9 @@ module Puppet::Util::Checksums nil end - alias :ctime_stream :mtime_stream + def mtime(content) + "" + end # Calculate a checksum using Digest::SHA1. def sha1(content) @@ -108,6 +110,12 @@ module Puppet::Util::Checksums File.stat(filename).send(:ctime) end + alias :ctime_stream :mtime_stream + + def ctime(content) + "" + end + # Return a "no checksum" def none_file(filename) "" @@ -119,6 +127,10 @@ module Puppet::Util::Checksums "" end + def none(content) + "" + end + private # Perform an incremental checksum on a file. diff --git a/lib/puppet/util/command_line/puppetd b/lib/puppet/util/command_line/puppetd index cb8589c5f..7d78ce90f 100755 --- a/lib/puppet/util/command_line/puppetd +++ b/lib/puppet/util/command_line/puppetd @@ -150,7 +150,8 @@ # # test:: # Enable the most common options used for testing. These are +onetime+, -# +verbose+, +ignorecache, +no-daemonize+, and +no-usecacheonfailure+. +# +verbose+, +ignorecache, +no-daemonize+, +no-usecacheonfailure+, +# +detailed-exit-codes+, +no-splay+, and +show_diff+. # # noop:: # Use +noop+ mode where the daemon runs in a no-op or dry-run mode. This is useful diff --git a/lib/puppet/util/command_line/puppetmasterd b/lib/puppet/util/command_line/puppetmasterd index baf8a7581..445169820 100755 --- a/lib/puppet/util/command_line/puppetmasterd +++ b/lib/puppet/util/command_line/puppetmasterd @@ -9,6 +9,7 @@ # # puppet master [-D|--daemonize|--no-daemonize] [-d|--debug] [-h|--help] # [-l|--logdest <file>|console|syslog] [-v|--verbose] [-V|--version] +# [--compile <nodename>] [--apply <catalog>] # # = Description # @@ -49,6 +50,14 @@ # version:: # Print the puppet version number and exit. # +# compile:: +# Capability to compile a catalogue and output it in JSON from the Puppet master. Uses +# facts contained in the $vardir/yaml/ directory to compile the catalog. +# +# apply:: +# Capability to apply JSON catalog (such as one generated with --compile). You can either specify +# a JSON file or pipe in JSON from standard input. +# # = Example # # puppet master diff --git a/lib/puppet/util/zaml.rb b/lib/puppet/util/zaml.rb index 9fda5ae3b..6ac956565 100644 --- a/lib/puppet/util/zaml.rb +++ b/lib/puppet/util/zaml.rb @@ -59,13 +59,12 @@ class ZAML @@previously_emitted_object = {} @@next_free_label_number = 0 end - def initialize(obj,indent) - @indent = indent + def initialize(obj) @this_label_number = nil @@previously_emitted_object[obj.object_id] = self end def to_s - @this_label_number ? ('&id%03d%s' % [@this_label_number, @indent]) : '' + @this_label_number ? ('&id%03d ' % @this_label_number) : '' end def reference @this_label_number ||= (@@next_free_label_number += 1) @@ -76,7 +75,7 @@ class ZAML end end def new_label_for(obj) - Label.new(obj,(Hash === obj || Array === obj) ? "#{@indent || "\n"} " : ' ') + Label.new(obj) end def first_time_only(obj) if label = Label.for(obj) diff --git a/spec/fixtures/unit/provider/mount/mount-output.aix.txt b/spec/fixtures/unit/provider/mount/mount-output.aix.txt new file mode 100644 index 000000000..54edb9c1c --- /dev/null +++ b/spec/fixtures/unit/provider/mount/mount-output.aix.txt @@ -0,0 +1,7 @@ +/dev/hd4 / jfs2 Nov 11 12:11 rw,log=/dev/hd8 +/dev/hd2 /usr jfs2 Nov 11 12:11 rw,log=/dev/hd8 +/dev/hd9var /var jfs2 Nov 11 12:11 rw,log=/dev/hd8 +/dev/hd3 /tmp jfs2 Nov 11 12:11 rw,log=/dev/hd8 +/dev/hd1 /home jfs2 Nov 11 12:11 rw,log=/dev/hd8 +/proc /proc procfs Nov 11 12:11 rw +/dev/hd10opt /opt jfs2 Nov 11 12:11 rw,log=/dev/hd8 diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index 95f3853e2..687f2ecb9 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -105,8 +105,15 @@ describe Puppet::Parser::Compiler do node.classes = %w{foo bar} compiler = Puppet::Parser::Compiler.new(node) - compiler.classlist.should include("foo") - compiler.classlist.should include("bar") + compiler.classlist.should =~ ['foo', 'bar'] + end + + it "should transform node class hashes into a class list" do + node = Puppet::Node.new("mynode") + node.classes = {'foo'=>{'one'=>'1'}, 'bar'=>{'two'=>'2'}} + compiler = Puppet::Parser::Compiler.new(node) + + compiler.classlist.should =~ ['foo', 'bar'] end it "should add a 'main' stage to the catalog" do @@ -185,6 +192,14 @@ describe Puppet::Parser::Compiler do @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } end + it "should evaluate any parameterized classes named in the node" do + classes = {'foo'=>{'1'=>'one'}, 'bar'=>{'2'=>'two'}} + @node.stubs(:classes).returns(classes) + @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) + @compiler.compile + end + + it "should evaluate the main class if it exists" do compile_stub(:evaluate_main) main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") @@ -532,7 +547,7 @@ describe Puppet::Parser::Compiler do @compiler.add_collection(coll) - lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources something' end it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do @@ -541,7 +556,7 @@ describe Puppet::Parser::Compiler do @compiler.add_collection(coll) - lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources one, two' end end @@ -566,6 +581,14 @@ describe Puppet::Parser::Compiler do @scope.expects(:find_hostclass).with("notfound").returns(nil) @compiler.evaluate_classes(%w{notfound}, @scope) end + # I wish it would fail + it "should log when it can't find class" do + klasses = {'foo'=>nil} + @node.classes = klasses + @compiler.topscope.stubs(:find_hostclass).with('foo').returns(nil) + Puppet.expects(:info).with('Could not find class foo for testnode') + @compiler.compile + end end describe "when evaluating found classes" do @@ -586,6 +609,68 @@ describe Puppet::Parser::Compiler do @compiler.evaluate_classes(%w{myclass}, @scope) end + it "should ensure each node class hash is in catalog and have appropriate parameters" do + klasses = {'foo'=>{'1'=>'one'}, 'bar::foo'=>{'2'=>'two'}, 'bar'=>{'1'=> [1,2,3], '2'=>{'foo'=>'bar'}}} + @node.classes = klasses + ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') + klasses.each do |name, params| + klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj}) + @compiler.topscope.known_resource_types.add klass + end + catalog = @compiler.compile + catalog.classes.should =~ ['foo', 'bar::foo', 'settings', 'bar'] + + r1 = catalog.resources.detect {|r| r.title == 'Foo' } + r1.to_hash.should == {:'1' => 'one', :'2' => 'foo'} + r1.tags. should =~ ['class', 'foo'] + + r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } + r2.to_hash.should == {:'1' => 'foo', :'2' => 'two'} + r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo'] + + r2 = catalog.resources.detect {|r| r.title == 'Bar' } + r2.to_hash.should == {:'1' => [1,2,3], :'2' => {'foo'=>'bar'}} + r2.tags.should =~ ['class', 'bar'] + end + + it "should ensure each node class is in catalog and has appropriate tags" do + klasses = ['bar::foo'] + @node.classes = klasses + ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') + klasses.each do |name| + klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj}) + @compiler.topscope.known_resource_types.add klass + end + catalog = @compiler.compile + + r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } + r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo'] + end + + it "should fail if required parameters are missing" do + klass = {'foo'=>{'1'=>'one'}} + @node.classes = klass + klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil}) + @compiler.topscope.known_resource_types.add klass + lambda { @compiler.compile }.should raise_error Puppet::ParseError, "Must pass 2 to Class[Foo]" + end + + it "should fail if invalid parameters are passed" do + klass = {'foo'=>{'3'=>'one'}} + @node.classes = klass + klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil}) + @compiler.topscope.known_resource_types.add klass + lambda { @compiler.compile }.should raise_error Puppet::ParseError, "Invalid parameter 3" + end + + it "should ensure class is in catalog without params" do + @node.classes = klasses = {'foo'=>nil} + foo = Puppet::Resource::Type.new(:hostclass, 'foo') + @compiler.topscope.known_resource_types.add foo + catalog = @compiler.compile + catalog.classes.should include 'foo' + end + it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @@ -759,7 +844,7 @@ describe Puppet::Parser::Compiler do it "should fail if the compile is finished and resource overrides have not been applied" do @compiler.add_override(@override) - lambda { @compiler.compile }.should raise_error(Puppet::ParseError) + lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Could not find resource(s) File[/foo] for overriding' end end end diff --git a/spec/unit/property/keyvalue_spec.rb b/spec/unit/property/keyvalue_spec.rb index 7666def56..a44d891d7 100644 --- a/spec/unit/property/keyvalue_spec.rb +++ b/spec/unit/property/keyvalue_spec.rb @@ -134,7 +134,7 @@ describe klass do end end - describe "when calling insync?" do + describe "when calling safe_insync?" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) @@ -142,26 +142,26 @@ describe klass do end it "should return true unless @should is defined and not nil" do - @property.insync?("foo") == true + @property.safe_insync?("foo") == true end it "should return true if the passed in values is nil" do @property.should = "foo" - @property.insync?(nil) == true + @property.safe_insync?(nil) == true end it "should return true if hashified should value == (retrieved) value passed in" do @provider.stubs(:prop_name).returns({ :foo => "baz", :bar => "boo" }) @property.should = ["foo=baz", "bar=boo"] @property.expects(:inclusive?).returns(true) - @property.insync?({ :foo => "baz", :bar => "boo" }).must == true + @property.safe_insync?({ :foo => "baz", :bar => "boo" }).must == true end it "should return false if prepared value != should value" do @provider.stubs(:prop_name).returns({ "foo" => "bee", "bar" => "boo" }) @property.should = ["foo=baz", "bar=boo"] @property.expects(:inclusive?).returns(true) - @property.insync?({ "foo" => "bee", "bar" => "boo" }).must == false + @property.safe_insync?({ "foo" => "bee", "bar" => "boo" }).must == false end end end diff --git a/spec/unit/property/list_spec.rb b/spec/unit/property/list_spec.rb index 3e8cc5402..c6c5db10e 100644 --- a/spec/unit/property/list_spec.rb +++ b/spec/unit/property/list_spec.rb @@ -118,39 +118,39 @@ describe list_class do end end - describe "when calling insync?" do + describe "when calling safe_insync?" do it "should return true unless @should is defined and not nil" do - @property.must be_insync("foo") + @property.must be_safe_insync("foo") end it "should return true unless the passed in values is not nil" do @property.should = "foo" - @property.must be_insync(nil) + @property.must be_safe_insync(nil) end it "should call prepare_is_for_comparison with value passed in and should" do @property.should = "foo" @property.expects(:prepare_is_for_comparison).with("bar") @property.expects(:should) - @property.insync?("bar") + @property.safe_insync?("bar") end it "should return true if 'is' value is array of comma delimited should values" do @property.should = "bar,foo" @property.expects(:inclusive?).returns(true) - @property.must be_insync(["bar","foo"]) + @property.must be_safe_insync(["bar","foo"]) end it "should return true if 'is' value is :absent and should value is empty string" do @property.should = "" @property.expects(:inclusive?).returns(true) - @property.must be_insync([]) + @property.must be_safe_insync([]) end it "should return false if prepared value != should value" do @property.should = "bar,baz,foo" @property.expects(:inclusive?).returns(true) - @property.must_not be_insync(["bar","foo"]) + @property.must_not be_safe_insync(["bar","foo"]) end end diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index b034214ee..f567a4a40 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -120,6 +120,14 @@ describe Puppet::Provider::Mount do @mounter.should be_mounted end + it "should match mounted devices if the operating system is AIX" do + Facter.stubs(:value).with("operatingsystem").returns("AIX") + mount_data = File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'unit', 'provider', 'mount', 'mount-output.aix.txt')) + @mounter.expects(:mountcmd).returns(mount_data) + + @mounter.should be_mounted + end + it "should match ' on <name>' if the operating system is not Darwin, Solaris, or HP-UX" 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") diff --git a/spec/unit/provider/nameservice/directoryservice_spec.rb b/spec/unit/provider/nameservice/directoryservice_spec.rb new file mode 100755 index 000000000..661899db9 --- /dev/null +++ b/spec/unit/provider/nameservice/directoryservice_spec.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +# We use this as a reasonable way to obtain all the support infrastructure. +[:user, :group].each do |type_for_this_round| + provider_class = Puppet::Type.type(type_for_this_round).provider(:directoryservice) + + describe provider_class do + before do + @resource = stub("resource") + @provider = provider_class.new(@resource) + end + + it "[#6009] should handle nested arrays of members" do + current = ["foo", "bar", "baz"] + desired = ["foo", ["quux"], "qorp"] + group = 'example' + + @resource.stubs(:[]).with(:name).returns(group) + @resource.stubs(:[]).with(:auth_membership).returns(true) + @provider.instance_variable_set(:@property_value_cache_hash, + { :members => current }) + + %w{bar baz}.each do |del| + @provider.expects(:execute).once. + with([:dseditgroup, '-o', 'edit', '-n', '.', '-d', del, group]) + end + + %w{quux qorp}.each do |add| + @provider.expects(:execute).once. + with([:dseditgroup, '-o', 'edit', '-n', '.', '-a', add, group]) + end + + expect { @provider.set(:members, desired) }.should_not raise_error + end + end +end diff --git a/spec/unit/provider/package/freebsd_spec.rb b/spec/unit/provider/package/freebsd_spec.rb new file mode 100755 index 000000000..0d38a16cf --- /dev/null +++ b/spec/unit/provider/package/freebsd_spec.rb @@ -0,0 +1,55 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +provider_class = Puppet::Type.type(:package).provider(:freebsd) + +describe provider_class do + before :each do + # Create a mock resource + @resource = stub 'resource' + + # A catch all; no parameters set + @resource.stubs(:[]).returns(nil) + + # But set name and source + @resource.stubs(:[]).with(:name).returns "mypackage" + @resource.stubs(:[]).with(:ensure).returns :installed + + @provider = provider_class.new + @provider.resource = @resource + end + + it "should have an install method" do + @provider = provider_class.new + @provider.should respond_to(:install) + end + + describe "when installing" do + before :each do + @resource.stubs(:should).with(:ensure).returns(:installed) + end + + it "should install a package from a path to a directory" do + # For better or worse, trailing '/' is needed. --daniel 2011-01-26 + path = '/path/to/directory/' + @resource.stubs(:[]).with(:source).returns(path) + Puppet::Util::Execution.expects(:withenv).once.with({:PKG_PATH => path}).yields + @provider.expects(:pkgadd).once.with("mypackage") + + expect { @provider.install }.should_not raise_error + end + + %w{http https ftp}.each do |protocol| + it "should install a package via #{protocol}" do + # For better or worse, trailing '/' is needed. --daniel 2011-01-26 + path = "#{protocol}://localhost/" + @resource.stubs(:[]).with(:source).returns(path) + Puppet::Util::Execution.expects(:withenv).once.with({:PACKAGESITE => path}).yields + @provider.expects(:pkgadd).once.with('-r', "mypackage") + + expect { @provider.install }.should_not raise_error + end + end + end +end diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index 7b240bb82..87b4ab420 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -261,6 +261,28 @@ describe Puppet::Resource::Type do @type = Puppet::Resource::Type.new(:hostclass, "foo") end + ['module_name', 'name', 'title'].each do |variable| + it "should allow #{variable} to be evaluated as param default" do + @type.module_name = "bar" + var = Puppet::Parser::AST::Variable.new({'value' => variable}) + @type.set_arguments :foo => var + @type.set_resource_parameters(@resource, @scope) + @scope.lookupvar('foo').should == 'bar' + end + end + + # this test is to clarify a crazy edge case + # if you specify these special names as params, the resource + # will override the special variables + it "resource should override defaults" do + @type.set_arguments :name => nil + @resource[:name] = 'foobar' + var = Puppet::Parser::AST::Variable.new({'value' => 'name'}) + @type.set_arguments :foo => var + @type.set_resource_parameters(@resource, @scope) + @scope.lookupvar('foo').should == 'foobar' + end + it "should set each of the resource's parameters as variables in the scope" do @type.set_arguments :foo => nil, :boo => nil @resource[:foo] = "bar" @@ -496,7 +518,7 @@ describe Puppet::Resource::Type do it "should evaluate the parent's resource" do @type.parent_type(@scope) - + @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil @@ -504,7 +526,7 @@ describe Puppet::Resource::Type do it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate - + @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @@ -545,7 +567,7 @@ describe Puppet::Resource::Type do it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate - + @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @@ -575,7 +597,7 @@ describe Puppet::Resource::Type do @code = Puppet::Resource::TypeCollection.new("env") @code.add @top @code.add @middle - + @node.environment.stubs(:known_resource_types).returns(@code) end @@ -601,12 +623,36 @@ describe Puppet::Resource::Type do @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end + it "should add specified parameters to the resource" do + @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'}) + @compiler.catalog.resource(:class, "top")['one'].should == '1' + @compiler.catalog.resource(:class, "top")['two'].should == '2' + end + + it "should not require params for a param class" do + @top.ensure_in_catalog(@scope, {}) + @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) + end + it "should evaluate the parent class if one exists" do @middle.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end + it "should evaluate the parent class if one exists" do + @middle.ensure_in_catalog(@scope, {}) + + @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) + end + + it "should fail if you try to create duplicate class resources" do + othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope ) + # add the same class resource to the catalog + @compiler.catalog.add_resource(othertop) + lambda { @top.ensure_in_catalog(@scope, {}) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) + end + it "should fail to evaluate if a parent class is defined but cannot be found" do othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" @code.add othertop diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index cde643fc8..9178c94bf 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -141,17 +141,20 @@ describe content do it "should return true if the resource shouldn't be a regular file" do @resource.expects(:should_be_file?).returns false - @content.must be_insync("whatever") + @content.should = "foo" + @content.must be_safe_insync("whatever") end it "should return false if the current content is :absent" do - @content.should_not be_insync(:absent) + @content.should = "foo" + @content.should_not be_safe_insync(:absent) end it "should return false if the file should be a file but is not present" do @resource.expects(:should_be_file?).returns true + @content.should = "foo" - @content.should_not be_insync(:absent) + @content.should_not be_safe_insync(:absent) end describe "and the file exists" do @@ -161,12 +164,12 @@ describe content do it "should return false if the current contents are different from the desired content" do @content.should = "some content" - @content.should_not be_insync("other content") + @content.should_not be_safe_insync("other content") end it "should return true if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" - @content.must be_insync("{md5}" + Digest::MD5.hexdigest("some content")) + @content.must be_safe_insync("{md5}" + Digest::MD5.hexdigest("some content")) end describe "and Puppet[:show_diff] is set" do @@ -179,14 +182,14 @@ describe content do @content.expects(:diff).returns("my diff").once @content.expects(:print).with("my diff").once - @content.insync?("other content") + @content.safe_insync?("other content") end it "should not display a diff if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.expects(:diff).never - @content.insync?("{md5}" + Digest::MD5.hexdigest("some content")) + @content.safe_insync?("{md5}" + Digest::MD5.hexdigest("some content")) end end end @@ -199,17 +202,18 @@ describe content do it "should be insync if the file exists and the content is different" do @resource.stubs(:stat).returns mock('stat') - @content.must be_insync("whatever") + @content.must be_safe_insync("whatever") end it "should be insync if the file exists and the content is right" do @resource.stubs(:stat).returns mock('stat') - @content.must be_insync("something") + @content.must be_safe_insync("something") end it "should not be insync if the file does not exist" do - @content.should_not be_insync(:absent) + @content.should = "foo" + @content.should_not be_safe_insync(:absent) end end end @@ -457,5 +461,54 @@ describe content do describe "from a filebucket" do end + + # These are testing the implementation rather than the desired behaviour; while that bites, there are a whole + # pile of other methods in the File type that depend on intimate details of this implementation and vice-versa. + # If these blow up, you are gonna have to review the callers to make sure they don't explode! --daniel 2011-02-01 + describe "each_chunk_from should work" do + before do + @content = content.new(:resource => @resource) + end + + it "when content is a string" do + @content.each_chunk_from('i_am_a_string') { |chunk| chunk.should == 'i_am_a_string' } + end + + it "when no content, source, but ensure present" do + @resource[:ensure] = :present + @content.each_chunk_from(nil) { |chunk| chunk.should == '' } + end + + it "when no content, source, but ensure file" do + @resource[:ensure] = :file + @content.each_chunk_from(nil) { |chunk| chunk.should == '' } + end + + it "when no content or source" do + @content.expects(:read_file_from_filebucket).once.returns('im_a_filebucket') + @content.each_chunk_from(nil) { |chunk| chunk.should == 'im_a_filebucket' } + end + + it "when running as puppet apply" do + @content.class.expects(:standalone?).returns true + source_or_content = stubs('source_or_content') + source_or_content.expects(:content).once.returns :whoo + @content.each_chunk_from(source_or_content) { |chunk| chunk.should == :whoo } + end + + it "when running from source with a local file" do + source_or_content = stubs('source_or_content') + source_or_content.expects(:local?).returns true + @content.expects(:chunk_file_from_disk).with(source_or_content).once.yields 'woot' + @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } + end + + it "when running from source with a remote file" do + source_or_content = stubs('source_or_content') + source_or_content.expects(:local?).returns false + @content.expects(:chunk_file_from_source).with(source_or_content).once.yields 'woot' + @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } + end + end end end diff --git a/spec/unit/type/file/ensure_spec.rb b/spec/unit/type/file/ensure_spec.rb index ec53ed85a..dbb3a1053 100755 --- a/spec/unit/type/file/ensure_spec.rb +++ b/spec/unit/type/file/ensure_spec.rb @@ -41,44 +41,45 @@ describe property do end it "should always be in sync if replace is 'false' unless the file is missing" do + @ensure.should = :file @resource.expects(:replace?).returns false - @ensure.insync?(:link).should be_true + @ensure.safe_insync?(:link).should be_true end it "should be in sync if :ensure is set to :absent and the file does not exist" do @ensure.should = :absent - @ensure.must be_insync(:absent) + @ensure.must be_safe_insync(:absent) end it "should not be in sync if :ensure is set to :absent and the file exists" do @ensure.should = :absent - @ensure.should_not be_insync(:file) + @ensure.should_not be_safe_insync(:file) end it "should be in sync if a normal file exists and :ensure is set to :present" do @ensure.should = :present - @ensure.must be_insync(:file) + @ensure.must be_safe_insync(:file) end it "should be in sync if a directory exists and :ensure is set to :present" do @ensure.should = :present - @ensure.must be_insync(:directory) + @ensure.must be_safe_insync(:directory) end it "should be in sync if a symlink exists and :ensure is set to :present" do @ensure.should = :present - @ensure.must be_insync(:link) + @ensure.must be_safe_insync(:link) end it "should not be in sync if :ensure is set to :file and a directory exists" do @ensure.should = :file - @ensure.should_not be_insync(:directory) + @ensure.should_not be_safe_insync(:directory) end end end diff --git a/spec/unit/type/file/group_spec.rb b/spec/unit/type/file/group_spec.rb index 2283b57fa..956cd57e7 100755 --- a/spec/unit/type/file/group_spec.rb +++ b/spec/unit/type/file/group_spec.rb @@ -56,19 +56,19 @@ describe property do describe "when determining if the file is in sync" do it "should directly compare the group values if the desired group is an integer" do @group.should = [10] - @group.must be_insync(10) + @group.must be_safe_insync(10) end it "should treat numeric strings as integers" do @group.should = ["10"] - @group.must be_insync(10) + @group.must be_safe_insync(10) end it "should convert the group name to an integer if the desired group is a string" do @group.expects(:gid).with("foo").returns 10 @group.should = %w{foo} - @group.must be_insync(10) + @group.must be_safe_insync(10) end it "should not validate that groups exist when a group is specified as an integer" do @@ -80,12 +80,12 @@ describe property do @group.expects(:gid).with("foo").returns nil @group.should = %w{foo} - lambda { @group.insync?(10) }.should raise_error(Puppet::Error) + lambda { @group.safe_insync?(10) }.should raise_error(Puppet::Error) end it "should return false if the groups are not equal" do @group.should = [10] - @group.should_not be_insync(20) + @group.should_not be_safe_insync(20) end end diff --git a/spec/unit/type/file/owner_spec.rb b/spec/unit/type/file/owner_spec.rb index 8e136a187..bcb8e07d6 100755 --- a/spec/unit/type/file/owner_spec.rb +++ b/spec/unit/type/file/owner_spec.rb @@ -68,7 +68,7 @@ describe property do @provider.expects(:warnonce) @owner.should = [10] - @owner.must be_insync(20) + @owner.must be_safe_insync(20) end end @@ -77,24 +77,24 @@ describe property do end it "should be in sync if 'should' is not provided" do - @owner.must be_insync(10) + @owner.must be_safe_insync(10) end it "should directly compare the owner values if the desired owner is an integer" do @owner.should = [10] - @owner.must be_insync(10) + @owner.must be_safe_insync(10) end it "should treat numeric strings as integers" do @owner.should = ["10"] - @owner.must be_insync(10) + @owner.must be_safe_insync(10) end it "should convert the owner name to an integer if the desired owner is a string" do @provider.expects(:uid).with("foo").returns 10 @owner.should = %w{foo} - @owner.must be_insync(10) + @owner.must be_safe_insync(10) end it "should not validate that users exist when a user is specified as an integer" do @@ -106,12 +106,12 @@ describe property do @provider.expects(:uid).with("foo").returns nil @owner.should = %w{foo} - lambda { @owner.insync?(10) }.should raise_error(Puppet::Error) + lambda { @owner.safe_insync?(10) }.should raise_error(Puppet::Error) end it "should return false if the owners are not equal" do @owner.should = [10] - @owner.should_not be_insync(20) + @owner.should_not be_safe_insync(20) end end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb index 1ca59e9e7..043471dec 100644 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -73,10 +73,10 @@ Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f @sel.sync end - it "should do nothing for insync? if no SELinux support" do + it "should do nothing for safe_insync? if no SELinux support" do @sel.should = %{newcontext} @sel.expects(:selinux_support?).returns false - @sel.insync?("oldcontext").should == true + @sel.safe_insync?("oldcontext").should == true end end end diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index 22921d85a..90f3daf09 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -194,6 +194,23 @@ describe Puppet::Type.type(:file) do file = Puppet::Type::File.new(:path => "/") file[:path].should == "/" end + + it "should accept a double-slash at the start of the path" do + expect { + file = Puppet::Type::File.new(:path => "//tmp/xxx") + # REVISIT: This should be wrong, later. See the next test. + # --daniel 2011-01-31 + file[:path].should == '/tmp/xxx' + }.should_not raise_error + end + + # REVISIT: This is pending, because I don't want to try and audit the + # entire codebase to make sure we get this right. POSIX treats two (and + # exactly two) '/' characters at the start of the path specially. + # + # See sections 3.2 and 4.11, which allow DomainOS to be all special like + # and still have the POSIX branding and all. --daniel 2011-01-31 + it "should preserve the double-slash at the start of the path" end describe "on Microsoft Windows systems" do @@ -806,6 +823,73 @@ describe Puppet::Type.type(:file) do end end + describe "when specifying both source, and content properties" do + before do + @file[:source] = '/one' + @file[:content] = 'file contents' + end + + it "should raise an exception" do + lambda {@file.validate }.should raise_error(/You cannot specify more than one of/) + end + end + + describe "when using source" do + before do + @file[:source] = '/one' + end + Puppet::Type::File::ParameterChecksum.value_collection.values.reject {|v| v == :none}.each do |checksum_type| + describe "with checksum '#{checksum_type}'" do + before do + @file[:checksum] = checksum_type + end + + it 'should validate' do + + lambda { @file.validate }.should_not raise_error + end + end + end + + describe "with checksum 'none'" do + before do + @file[:checksum] = :none + end + + it 'should raise an exception when validating' do + lambda { @file.validate }.should raise_error(/You cannot specify source when using checksum 'none'/) + end + end + end + + describe "when using content" do + before do + @file[:content] = 'file contents' + end + + (Puppet::Type::File::ParameterChecksum.value_collection.values - SOURCE_ONLY_CHECKSUMS).each do |checksum_type| + describe "with checksum '#{checksum_type}'" do + before do + @file[:checksum] = checksum_type + end + + it 'should validate' do + lambda { @file.validate }.should_not raise_error + end + end + end + + SOURCE_ONLY_CHECKSUMS.each do |checksum_type| + describe "with checksum '#{checksum_type}'" do + it 'should raise an exception when validating' do + @file[:checksum] = checksum_type + + lambda { @file.validate }.should raise_error(/You cannot specify content when using checksum '#{checksum_type}'/) + end + end + end + end + describe "when returning resources with :eval_generate" do before do @graph = stub 'graph', :add_edge => nil @@ -1065,4 +1149,45 @@ describe Puppet::Type.type(:file) do end end + describe "when auditing" do + it "should not fail if creating a new file if group is not set" do + File.exists?(@path).should == false + file = Puppet::Type::File.new(:name => @path, :audit => "all", :content => "content") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource(file) + + Puppet::Util::Storage.stubs(:store) # to prevent the catalog from trying to write state.yaml + transaction = catalog.apply + + transaction.report.resource_statuses["File[#{@path}]"].failed.should == false + File.exists?(@path).should == true + end + + it "should not log errors if creating a new file with ensure present and no content" do + File.exists?(@path).should == false + file = Puppet::Type::File.new(:name => @path, :audit => "content", :ensure => "present") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource(file) + + Puppet::Util::Storage.stubs(:store) # to prevent the catalog from trying to write state.yaml + + catalog.apply + @logs.reject {|l| l.level == :notice }.should be_empty + end + end + + describe "when specifying both source and checksum" do + it 'should use the specified checksum when source is first' do + @file[:source] = '/foo' + @file[:checksum] = :md5lite + + @file[:checksum].should be :md5lite + end + it 'should use the specified checksum when source is last' do + @file[:checksum] = :md5lite + @file[:source] = '/foo' + + @file[:checksum].should be :md5lite + end + end end diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb index ce82cb516..0d74042e3 100755 --- a/spec/unit/type/mount_spec.rb +++ b/spec/unit/type/mount_spec.rb @@ -184,22 +184,22 @@ describe Puppet::Type.type(:mount)::Ensure do it "should be insync if it is mounted and should be defined" do @ensure.should = :defined - @ensure.insync?(:mounted).should == true + @ensure.safe_insync?(:mounted).should == true end it "should be insync if it is unmounted and should be defined" do @ensure.should = :defined - @ensure.insync?(:unmounted).should == true + @ensure.safe_insync?(:unmounted).should == true end it "should be insync if it is mounted and should be present" do @ensure.should = :present - @ensure.insync?(:mounted).should == true + @ensure.safe_insync?(:mounted).should == true end it "should be insync if it is unmounted and should be present" do @ensure.should = :present - @ensure.insync?(:unmounted).should == true + @ensure.safe_insync?(:unmounted).should == true end end diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb index a0b435f80..666616c03 100755 --- a/spec/unit/type/ssh_authorized_key_spec.rb +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -134,7 +134,7 @@ describe ssh_authorized_key do it "should not raise spurious change events" do resource = @class.new(:name => "Test", :user => "root") target = File.expand_path("~root/.ssh/authorized_keys") - resource.property(:target).insync?(target).should == true + resource.property(:target).safe_insync?(target).should == true end end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index ccea9ee4c..297134446 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -201,21 +201,21 @@ describe user do it "should return true if no 'should' values are set" do @gid = user.attrclass(:gid).new(:resource => @resource) - @gid.must be_insync(500) + @gid.must be_safe_insync(500) end it "should return true if any of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 - @gid.must be_insync(500) + @gid.must be_safe_insync(500) end it "should return false if none of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 - @gid.should_not be_insync(700) + @gid.should_not be_safe_insync(700) end end @@ -245,6 +245,34 @@ describe user do end end + describe "when managing minimum password age" do + before do + @age = user.attrclass(:password_min_age).new(:resource => @resource) + end + + it "should accept a negative minimum age" do + expect { @age.should = -1 }.should_not raise_error + end + + it "should fail with an empty minimum age" do + expect { @age.should = '' }.should raise_error(Puppet::Error) + end + end + + describe "when managing maximum password age" do + before do + @age = user.attrclass(:password_max_age).new(:resource => @resource) + end + + it "should accept a negative maximum age" do + expect { @age.should = -1 }.should_not raise_error + end + + it "should fail with an empty maximum age" do + expect { @age.should = '' }.should raise_error(Puppet::Error) + end + end + describe "when managing passwords" do before do @password = user.attrclass(:password).new(:resource => @resource, :should => "mypass") diff --git a/spec/unit/type/zpool_spec.rb b/spec/unit/type/zpool_spec.rb index db12459ab..be8cb12ba 100755 --- a/spec/unit/type/zpool_spec.rb +++ b/spec/unit/type/zpool_spec.rb @@ -38,27 +38,27 @@ describe vdev_property do it "should be insync if the devices are the same" do @property.should = ["dev1 dev2"] - @property.insync?(["dev2 dev1"]).must be_true + @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the devices are not the same" do @property.should = ["dev1 dev3"] - @property.insync?(["dev2 dev1"]).must be_false + @property.safe_insync?(["dev2 dev1"]).must be_false end it "should be insync if the devices are the same and the should values are comma seperated" do @property.should = ["dev1", "dev2"] - @property.insync?(["dev2 dev1"]).must be_true + @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the device is absent and should has a value" do @property.should = ["dev1", "dev2"] - @property.insync?(:absent).must be_false + @property.safe_insync?(:absent).must be_false end it "should be insync if the device is absent and should is absent" do @property.should = [:absent] - @property.insync?(:absent).must be_true + @property.safe_insync?(:absent).must be_true end end @@ -73,38 +73,38 @@ describe multi_vdev_property do it "should be insync if the devices are the same" do @property.should = ["dev1 dev2"] - @property.insync?(["dev2 dev1"]).must be_true + @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the devices are not the same" do @property.should = ["dev1 dev3"] - @property.insync?(["dev2 dev1"]).must be_false + @property.safe_insync?(["dev2 dev1"]).must be_false end it "should be out of sync if the device is absent and should has a value" do @property.should = ["dev1", "dev2"] - @property.insync?(:absent).must be_false + @property.safe_insync?(:absent).must be_false end it "should be insync if the device is absent and should is absent" do @property.should = [:absent] - @property.insync?(:absent).must be_true + @property.safe_insync?(:absent).must be_true end describe "when there are multiple lists of devices" do it "should be in sync if each group has the same devices" do @property.should = ["dev1 dev2", "dev3 dev4"] - @property.insync?(["dev2 dev1", "dev3 dev4"]).must be_true + @property.safe_insync?(["dev2 dev1", "dev3 dev4"]).must be_true end it "should be out of sync if any group has the different devices" do @property.should = ["dev1 devX", "dev3 dev4"] - @property.insync?(["dev2 dev1", "dev3 dev4"]).must be_false + @property.safe_insync?(["dev2 dev1", "dev3 dev4"]).must be_false end it "should be out of sync if devices are in the wrong group" do @property.should = ["dev1 dev2", "dev3 dev4"] - @property.insync?(["dev2 dev3", "dev1 dev4"]).must be_false + @property.safe_insync?(["dev2 dev3", "dev1 dev4"]).must be_false end end end diff --git a/spec/unit/util/zaml_spec.rb b/spec/unit/util/zaml_spec.rb index f2bcefe01..59590c571 100755 --- a/spec/unit/util/zaml_spec.rb +++ b/spec/unit/util/zaml_spec.rb @@ -35,5 +35,26 @@ describe "Pure ruby yaml implementation" do lambda { YAML.load(o.to_yaml) }.should_not raise_error end } + + it "should handle references to Array in Hash values correctly" do + list = [1] + data = { "one" => list, "two" => list } + data.to_yaml.should == "--- \n two: &id001 \n - 1\n one: *id001" + expect { YAML.load(data.to_yaml).should == data }.should_not raise_error + end + + it "should handle references to Hash in Hash values correctly" do + hash = { 1 => 1 } + data = { "one" => hash, "two" => hash } + data.to_yaml.should == "--- \n two: &id001 \n 1: 1\n one: *id001" + expect { YAML.load(data.to_yaml).should == data }.should_not raise_error + end + + it "should handle references to Scalar in Hash" do + str = "hello" + data = { "one" => str, "two" => str } + data.to_yaml.should == "--- \n two: &id001 hello\n one: *id001" + expect { YAML.load(data.to_yaml).should == data }.should_not raise_error + end end |