diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-09-05 01:51:23 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-09-05 01:51:23 +0000 |
commit | 617fe58626aa8a13af10071ca87f66d6363cf058 (patch) | |
tree | db9974c675c3b73cae56a253386f716e83bc8b6f | |
parent | 8f39318ce46148c3bd483d790c965f277a4eb1c9 (diff) | |
download | puppet-617fe58626aa8a13af10071ca87f66d6363cf058.tar.gz puppet-617fe58626aa8a13af10071ca87f66d6363cf058.tar.xz puppet-617fe58626aa8a13af10071ca87f66d6363cf058.zip |
Removing all of the changes I made towards refactoring in the last couple of days. They have all been moved into the sync-retrieve-refactor branch. This branch will soon become 0.19.0, and will not include that refactoring.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1555 980ebf18-57e1-0310-9a29-db15c13687c0
53 files changed, 1800 insertions, 3325 deletions
diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index 04169e6e1..f51ea4450 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -139,7 +139,9 @@ class Puppet::Provider return true end + # Remove the reference to the model, so GC can clean up. def clear + @model = nil end # Retrieve a named command. @@ -155,11 +157,6 @@ class Puppet::Provider @model.name end - # Remove the reference to the model, so GC can clean up. - def remove - @model = nil - end - def to_s "%s(provider=%s)" % [@model.to_s, self.class.name] end diff --git a/lib/puppet/provider/cron/parsed.rb b/lib/puppet/provider/cron/parsed.rb deleted file mode 100755 index 1626162bd..000000000 --- a/lib/puppet/provider/cron/parsed.rb +++ /dev/null @@ -1,161 +0,0 @@ -require 'puppet/provider/parsedfile' - -Puppet::Type.type(:cron).provide :parsed, :parent => Puppet::Provider::ParsedFile do - @fields = [:minute, :hour, :monthday, :month, :weekday, :command] - - # XXX This should be switched to use providers or something similar on - # the backend. - def self.defaulttype - case Facter["operatingsystem"].value - when "Solaris": - return Puppet::FileType.filetype(:suntab) - else - return Puppet::FileType.filetype(:crontab) - end - end - - self.filetype = self.defaulttype() - - # We have to maintain separate file objects for every user, unfortunately. - def self.filetype(user) - @tabs ||= {} - @tabs[user] ||= @filetype.new(user) - - @tabs[user] - end - - # Parse a user's cron job into individual cron objects. - # - # Autogenerates names for any jobs that don't already have one; these - # names will get written back to the file. - # - # This method also stores existing comments, and it stores all cron - # jobs in order, mostly so that comments are retained in the order - # they were written and in proximity to the same jobs. - def self.parse(user, text) - count = 0 - - envs = [] - instances = [] - text.chomp.split("\n").each { |line| - hash = {} - case line - when /^# Puppet Name: (.+)$/ - hash[:name] = $1 - next - when /^#/: - # add other comments to the list as they are - instances << line - next - when /^\s*(\w+)\s*=\s*(.+)\s*$/: - # Match env settings. - if hash[:name] - envs << line - else - instances << line - end - next - when /^@(\w+)\s+(.+)/ # FreeBSD special cron crap - fields().each do |field| - next if field == :command - hash[field] = :absent - end - hash[:special] = $1 - hash[:command] = $2 - else - if match = /^(\S+) (\S+) (\S+) (\S+) (\S+) (.+)$/.match(line) - fields().zip(match.captures).each { |param, value| - if value == "*" - hash[param] = [:absent] - else - if param == :command - hash[param] = [value] - else - # We always want the 'is' value to be an - # array - hash[param] = value.split(",") - end - end - } - else - # Don't fail on unmatched lines, just warn on them - # and skip them. - Puppet.warning "Could not match '%s'" % line - next - end - end - - unless envs.empty? - hash[:environment] = envs - end - - hash[:user] = user - - instances << hash - - envs.clear - count += 1 - } - - return instances - end - - def self.retrieve(user) - text = fileobj(user).read - if text.nil? or text == "" - return [] - else - self.parse(user, text) - end - end - - # Another override. This will pretty much only ever have one user's instances, - def self.store(instances) - instances.find_all { |i| i.is_a? Hash }.collect { |i| i[:user] }.each do |user| - fileobj(user).write(self.to_file(instances)) - end - end - - # Convert the current object a cron-style string. Adds the cron name - # as a comment above the cron job, in the form '# Puppet Name: <name>'. - def self.to_record(hash) - hash = {} - - str = "" - - str = "# Puppet Name: %s\n" % hash[:name] - - if @states.include?(:environment) and - @states[:environment].should != :absent - envs = @states[:environment].should - unless envs.is_a? Array - envs = [envs] - end - - envs.each do |line| str += (line + "\n") end - end - - line = nil - if special = hash[:special] - line = str + "@%s %s" % - [special, hash[:command]] - else - line = str + self.class.fields.collect { |f| - if hash[f] and hash[f] != :absent - hash[f] - else - "*" - end - }.join(" ") - end - - return line - end - - # Override the mechanism for retrieving instances, because we're user-specific. - def allinstances - self.class.retrieve(@model[:user]) - end -end - -# $Id$ diff --git a/lib/puppet/provider/host/parsed.rb b/lib/puppet/provider/host/parsed.rb deleted file mode 100644 index c606562a2..000000000 --- a/lib/puppet/provider/host/parsed.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'puppet/provider/parsedfile' - -Puppet::Type.type(:host).provide :parsed, :parent => Puppet::Provider::ParsedFile do - @path = "/etc/hosts" - @filetype = Puppet::FileType.filetype(:flat) - - confine :exists => @path - - # Parse a host file - # - # This method also stores existing comments, and it stores all host - # jobs in order, mostly so that comments are retained in the order - # they were written and in proximity to the same jobs. - def self.parse(text) - count = 0 - instances = [] - text.chomp.split("\n").each { |line| - hash = {} - case line - when /^#/, /^\s*$/: - # add comments and blank lines to the list as they are - instances << line - else - if line.sub!(/^(\S+)\s+(\S+)\s*/, '') - hash[:ip] = $1 - hash[:name] = $2 - - unless line == "" - line.sub!(/\s*/, '') - line.sub!(/^([^#]+)\s*/) do |value| - aliases = $1 - unless aliases =~ /^\s*$/ - hash[:alias] = aliases.split(/\s+/) - end - - "" - end - end - else - raise Puppet::Error, "Could not match '%s'" % line - end - - if hash[:alias] == "" - hash.delete(:alias) - end - - instances << hash - - count += 1 - end - } - - return instances - end - - # Convert the current object into a host-style string. - def self.to_record(hash) - [:ip, :name].each do |n| - unless hash.has_key? n - raise ArgumentError, "%s is a required attribute for hosts" % n - end - end - - str = "%s\t%s" % [hash[:ip], hash[:name]] - - if hash.include? :alias - if hash[:alias].is_a? Array - str += "\t%s" % hash[:alias].join("\t") - else - raise ArgumentError, "Aliases must be specified as an array" - end - end - - str - end -end - -# $Id$ diff --git a/lib/puppet/provider/mount/parsed.rb b/lib/puppet/provider/mount/parsed.rb deleted file mode 100755 index b768d6c5e..000000000 --- a/lib/puppet/provider/mount/parsed.rb +++ /dev/null @@ -1,171 +0,0 @@ -require 'puppet/provider/parsedfile' - -Puppet::Type.type(:mount).provide :parsed, :parent => Puppet::Provider::ParsedFile do - @filetype = Puppet::FileType.filetype(:flat) - - commands :mount => "mount", :umount => "umount", :df => "df" - - def self.init - @platform = Facter["operatingsystem"].value - case @platform - when "Solaris": - @path = "/etc/vfstab" - @fields = [:device, :blockdevice, :path, :fstype, :pass, :atboot, - :options] - @defaults = [ nil ] * @fields.size - when "Darwin": - @filetype = Puppet::FileType.filetype(:netinfo) - @filetype.format = "fstab" - @path = "mounts" - @fields = [:device, :path, :fstype, :options, :dump, :pass] - @defaults = [ nil ] * @fields.size - - # How to map the dumped table to what we want - @fieldnames = { - "name" => :device, - "dir" => :path, - "dump_freq" => :dump, - "passno" => :pass, - "vfstype" => :fstype, - "opts" => :options - } - else - @path = "/etc/fstab" - @fields = [:device, :path, :fstype, :options, :dump, :pass] - @defaults = [ nil ] * 4 + [ "0", "2" ] - end - - # Allow Darwin to override the default filetype - unless defined? @filetype - @filetype = Puppet::FileType.filetype(:flat) - end - end - - init - - confine :exists => @path - - def self.clear - init - super - end - - # Parse a mount tab. - # - # This method also stores existing comments, and it stores all - # mounts in order, mostly so that comments are retained in the - # order they were written and in proximity to the same fses. - def self.parse(text) - # provide a single exception for darwin & netinfo - if @filetype == Puppet::FileType.filetype(:netinfo) - return parseninfo(text) - end - count = 0 - - instances = [] - text.chomp.split("\n").each { |line| - hash = {} - case line - when /^#/, /^\s*$/: - # add comments and blank lines to the list as they are - instances << line - comment(line) - else - values = line.split(/\s+/) - if @fields.length < values.length - raise Puppet::Error, "Could not parse line %s" % line - end - - values = @defaults.zip(values).collect { |d, v| v || d } - unless @fields.length == values.length - raise Puppet::Error, "Could not parse line %s" % line - end - - @fields.zip(values).each do |field, value| - hash[field] = value - end - - instances << hash - count += 1 - end - } - - return instances - end - - # Parse a netinfo table. - def self.parseninfo(text) - array = @fileobj.to_array(text) - - instances = [] - array.each do |record| - hash = {} - @fieldnames.each do |name, field| - if value = record[name] - if field == :options - hash[field] = value.join(",") - else - hash[field] = value[0] - end - else - raise ArgumentError, "Field %s was not provided" % [name] - end - end - - instances << hash - end - - return instances - end - - # Convert the current object into an fstab-style string. - def self.to_record(hash) - self.fields.collect do |field| - if value = hash[field] - value - else - raise Puppet::Error, - "Could not retrieve value for %s" % field - end - end.join("\t") - end - - # This only works when the mount point is synced to the fstab. - def mount - output = %x{#{command(:mount)} #{@model[:path]} 2>&1} - - unless $? == 0 - raise Puppet::Error, "Could not mount %s: %s" % [@model[:path], output] - end - end - - # This only works when the mount point is synced to the fstab. - def unmount - output = %x{#{command(:umount)} #{@model[:path]}} - - unless $? == 0 - raise Puppet::Error, "Could not unmount %s" % @model[:path] - end - end - - # Is the mount currently mounted? - def mounted? - platform = Facter["operatingsystem"].value - df = command(:df) - case Facter["operatingsystem"].value - # Solaris's df prints in a very weird format - when "Solaris": df = "#{command(:df)} -k" - end - %x{#{df}}.split("\n").find do |line| - fs = line.split(/\s+/)[-1] - if platform == "Darwin" - fs == "/private/var/automount" + @model[:path] or - fs == @model[:path] - else - fs == @model[:path] - end - end - end -end - -# $Id$ diff --git a/lib/puppet/provider/package/portage.rb b/lib/puppet/provider/package/portage.rb deleted file mode 100644 index 873f63f68..000000000 --- a/lib/puppet/provider/package/portage.rb +++ /dev/null @@ -1,124 +0,0 @@ -Puppet::Type.type(:package).provide :portage do - desc "Provides packaging support for Gentoo's portage system." - - commands :emerge => "emerge", :eix => "eix" - - defaultfor :operatingsystem => :gentoo - - def self.list - search_format = /(\S+) (\S+) \[(.*)\] \[(\S*)\] ([\S]*) (.*)/ - result_fields = [:category, :name, :version, :version_available, - :vendor, :description] - command = "#{command(:eix)} --format \"{<installedversions>}<category> <name> [<installedversions>] [<best>] <homepage> <description>{}\"" - - begin - search_output = execute( command ) - - packages = [] - search_output.each do |search_result| - match = search_format.match( search_result ) - - if( match ) - package = {:ensure => :present} - result_fields.zip( match.captures ) { |field, value| package[field] = value } - if self.is_a? Puppet::Type and type = @model[:type] - package[:type] = type - elsif self.is_a? Module and self.respond_to? :name - package[:type] = self.name - else - raise Puppet::DevError, "Cannot determine package type" - end - if package[:version] - package[:version] = package[:version].split.last - end - - packages.push( Puppet.type(:package).installedpkg(package) ) - end - end - - return packages - rescue Puppet::ExecutionFailure => detail - raise Puppet::PackageError.new(detail) - end - end - - def install - if @model[:version] - # We must install a specific version - package_name = "=#{@model[:name]}-#{@model[:version]}" - else - package_name = @model[:name] - end - command = "EMERGE_DEFAULT_OPTS=\"\" #{command(:emerge)} #{package_name}" - begin - output = execute( command ) - rescue Puppet::ExecutionFailure => detail - raise Puppet::PackageError.new(detail) - end - end - - def uninstall - if @model[:version] - # We must uninstall a specific version - package_name = "=#{@model[:name]}-#{@model[:version]}" - else - package_name = @model[:name] - end - command ="EMERGE_DEFAULT_OPTS=\"\" #{command(:emerge)} --unmerge #{package_name}" - begin - output = execute( command ) - rescue Puppet::ExecutionFailure => detail - raise Puppet::PackageError.new(detail) - end - end - - def update - self.install - end - - def query - search_format = /(\S+) (\S+) \[(.*)\] \[(\S*)\] ([\S]*) (.*)/ - result_fields = [:category, :name, :version, :version_available, :vendor, :description] - - search_field = @model[:name].include?( '/' ) ? "--category-name" : "--name" - command = "#{command(:eix)} --format \"<category> <name> [<installedversions>] [<best>] <homepage> <description>\" --exact #{search_field} #{@model[:name]}" - - begin - search_output = execute( command ) - - packages = [] - search_output.each do |search_result| - match = search_format.match( search_result ) - - if( match ) - package = {} - result_fields.zip( match.captures ) { |field, value| package[field] = value unless value.empty? } - package[:ensure] = package[:version] ? :present : :absent - package[:version] = package[:version].split.last if package[:version] - packages << package - end - end - - case packages.size - when 0 - return nil - when 1 - return packages[0] - else - self.fail "More than one package with the specified name [#{@model[:name]}], please use category/name to disambiguate" - end - rescue Puppet::ExecutionFailure => detail - raise Puppet::PackageError.new(detail) - end - end - - def latest - return self.query[:version_available] - end - - def versionable? - true - end -end - -# $Id$ diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb deleted file mode 100755 index f690c03a4..000000000 --- a/lib/puppet/provider/parsedfile.rb +++ /dev/null @@ -1,174 +0,0 @@ -require 'puppet' - -class Puppet::Provider::ParsedFile < Puppet::Provider - class << self - attr_accessor :filetype, :fields - attr_reader :path - attr_writer :fileobj - end - - # Override 'newstate' so that all states default to having the - # correct parent type - def self.newstate(name, options = {}, &block) - options[:parent] ||= Puppet::State::ParsedParam - super(name, options, &block) - end - - # Add another type var. - def self.initvars - @instances = [] - super - end - - # Add a non-object comment or whatever to our list of instances - def self.comment(line) - @instances << line - end - - # Override the default Puppet::Type method, because instances - # also need to be deleted from the @instances hash - def self.delete(child) - if @instances.include?(child) - @instances.delete(child) - end - super - end - - # Initialize the object if necessary. - def self.fileobj - @fileobj ||= @filetype.new(@path) - - @fileobj - end - - # Return the header placed at the top of each generated file, warning - # users that modifying this file manually is probably a bad idea. - def self.header -%{# HEADER: This file was autogenerated at #{Time.now} -# HEADER: by puppet. While it can still be managed manually, it -# HEADER: is definitely not recommended.\n} - end - - # Parse a file - # - # Subclasses must override this method. - def self.parse(text) - raise Puppet::DevError, "Parse was not overridden in %s" % - self.name - end - - # If they change the path, we need to get rid of our cache object - def self.path=(path) - @fileobj = nil - @path = path - end - - # Retrieve the text for the file. Returns nil in the unlikely - # event that it doesn't exist. - def self.retrieve - text = fileobj.read - if text.nil? or text == "" - # there is no file - return [] - else - self.parse(text) - end - end - - # Write out the file. - def self.store(instances) - if instances.empty? - Puppet.notice "No %s instances for %s" % [self.name, @path] - else - fileobj.write(self.to_file(instances)) - end - end - - # Collect all Host instances convert them into literal text. - def self.to_file(instances) - str = self.header() - unless instances.empty? - # Reject empty hashes and those with :ensure == :absent - str += instances.reject { |obj| - obj.is_a? Hash and (obj.empty? or obj[:ensure] == :absent) - }.collect { |obj| - # If it's a hash, convert it, otherwise just write it out - if obj.is_a?(Hash) - to_record(obj) - else - obj.to_s - end - }.join("\n") + "\n" - - return str - else - Puppet.notice "No %s instances" % self.name - return "" - end - end - - # A Simple wrapper method that subclasses can override, so there's more control - # over how instances are retrieved. - def allinstances - self.class.retrieve - end - - def clear - super - @instances = nil - end - - # Return a hash that maps to our info, if possible. - def hash - @instances = allinstances() - - if @instances and h = @instances.find do |o| - o.is_a? Hash and o[:name] == @model[:name] - end - @me = h - else - @me = {} - if @instances.empty? - @instances = [@me] - else - @instances << @me - end - end - - return @me - end - - def initialize(model) - super - - @instances = nil - end - - def store(hash = nil) - hash ||= self.model.to_hash - - unless @instances - self.hash - end - - if hash.empty? - @me.clear - else - hash.each do |name, value| - if @me[name] != hash[name] - @me[name] = hash[name] - end - end - - @me.each do |name, value| - unless hash.has_key? name - @me.delete(name) - end - end - end - - self.class.store(@instances) - end -end - -# $Id$ diff --git a/lib/puppet/provider/port/parsed.rb b/lib/puppet/provider/port/parsed.rb deleted file mode 100755 index d65a0777f..000000000 --- a/lib/puppet/provider/port/parsed.rb +++ /dev/null @@ -1,139 +0,0 @@ -require 'puppet/provider/parsedfile' - -Puppet::Type.type(:port).provide :parsed, :parent => Puppet::Provider::ParsedFile do - - @filetype = Puppet::FileType.filetype(:flat) - @path = "/etc/services" - - # Parse a services file - # - # This method also stores existing comments, and it stores all port - # info in order, mostly so that comments are retained in the order - # they were written and in proximity to the same ports. - def self.parse(text) - count = 0 - instances = [] - namehash = {} # For merging - text.chomp.split("\n").each { |line| - hash = {} - case line - when /^#/, /^\s*$/: - # add comments and blank lines to the list as they are - instances << line - else - if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') - hash[:name] = $1 - hash[:number] = $2 - hash[:protocols] = [$3] - - unless line == "" - line.sub!(/^([^#]+)\s*/) do |value| - aliases = $1 - - # Remove any trailing whitespace - aliases.strip! - unless aliases =~ /^\s*$/ - hash[:alias] = aliases.split(/\s+/) - end - - "" - end - - line.sub!(/^\s*#\s*(.+)$/) do |value| - desc = $1 - unless desc =~ /^\s*$/ - hash[:description] = desc.sub(/\s*$/, '') - end - - "" - end - end - else - if line =~ /^\s+\d+/ and - Facter["operatingsystem"].value == "Darwin" - #Puppet.notice "Skipping wonky OS X port entry %s" % - # line.inspect - next - end - raise Puppet::Error, "Could not match '%s'" % line - end - - # If there's already a service with this name, then check - # to see if the only difference is the proto; if so, just - # add our proto and walk away - if obj = namehash[hash[:name]] - if portmerge(obj, hash) - next - end - end - - instances << hash - namehash[hash[:name]] = hash - - count += 1 - end - } - - return instances - end - - def self.portmerge(base, hash) - unless base.has_key?(:protocols) - return false - end - - # This method is only called from parsing, so we only worry - # about 'is' values. - proto = base[:protocols] - - if proto.nil? or proto == :absent - # We are an unitialized object; we've got 'should' - # values but no 'is' values - return false - end - - # If this is happening, our object exists - base[:ensure] = :present - - if hash[:protocols] - # The protocol can be a symbol, so... - if proto.is_a?(Symbol) - proto = [] - end - # Check to see if it already includes our proto - unless proto.include?(hash[:protocols]) - # We are missing their proto - proto += hash[:protocols] - base[:protocols] = proto - end - end - - if hash.include?(:description) and ! base.include?(:description) - base[:description] = hash[:description] - end - - return true - end - - # Convert the current object into one or more services entry. - def self.to_record(hash) - hash[:protocols].collect { |proto| - str = "%s\t%s/%s" % [hash[:name], hash[:number], proto] - - if value = hash[:alias] and value != :absent - str += "\t%s" % value.join(" ") - else - str += "\t" - end - - if value = hash[:description] and value != :absent - str += "\t# %s" % value - else - str += "\t" - end - str - }.join("\n") - end -end - -# $Id$ diff --git a/lib/puppet/provider/sshkey/parsed.rb b/lib/puppet/provider/sshkey/parsed.rb deleted file mode 100755 index e1dbeaad6..000000000 --- a/lib/puppet/provider/sshkey/parsed.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'puppet/provider/parsedfile' - -Puppet::Type.type(:sshkey).provide :parsed, :parent => Puppet::Provider::ParsedFile do - @filetype = Puppet::FileType.filetype(:flat) - @path = "/etc/ssh/ssh_known_hosts" - @fields = [:name, :type, :key] - - # Parse an sshknownhosts file - # - # This method also stores existing comments, and it stores all host - # jobs in order, mostly so that comments are retained in the order - # they were written and in proximity to the same jobs. - def self.parse(text) - count = 0 - instances = [] - text.chomp.split("\n").each { |line| - hash = {} - case line - when /^#/, /^\s*$/: - # add comments and blank lines to the list as they are - instances << line - else - hash = {} - fields().zip(line.split(" ")).each { |param, value| - hash[param] = value - } - - if hash[:name] =~ /,/ - names = hash[:name].split(",") - hash[:name] = names.shift - hash[:alias] = names - end - - if hash[:alias] == "" - hash.delete(:alias) - end - - instances << hash - count += 1 - end - } - - return instances - end - - # Convert the current object into an entry for a known-hosts file. - def self.to_record(hash) - name = hash[:name] - if hash.include?(:alias) - name += "," + hash[:alias].join(",") - end - [name, hash[:type], hash[:key]].join(" ") - end -end - -# $Id$ diff --git a/lib/puppet/server/fileserver.rb b/lib/puppet/server/fileserver.rb index 93f3310d1..49af26c54 100755 --- a/lib/puppet/server/fileserver.rb +++ b/lib/puppet/server/fileserver.rb @@ -63,7 +63,11 @@ class Server desc = [] CHECKPARAMS.each { |check| if state = obj.state(check) - desc << state.retrieve + unless state.is + mount.debug "Manually retrieving info for %s" % check + state.retrieve + end + desc << state.is else if check == "checksum" and obj.state(:type).is == "file" mount.notice "File %s does not have data for %s" % diff --git a/lib/puppet/statechange.rb b/lib/puppet/statechange.rb index d11a985fe..4884026fe 100644 --- a/lib/puppet/statechange.rb +++ b/lib/puppet/statechange.rb @@ -11,55 +11,19 @@ module Puppet # The log file generated when this object was changed. attr_reader :report - # Switch the goals of the state, thus running the change in reverse. - def backward - @state.should = @is - @should = @is - @is = @state.retrieve - @state.is = @is - - unless defined? @transaction - raise Puppet::Error, - "StateChange '%s' tried to be executed outside of transaction" % - self - end - unless @state.insync? - @state.info "Backing %s" % self - return self.go - else - @state.debug "rollback is already in sync: %s vs. %s" % - [@state.is.inspect, @state.should.inspect] - return nil - end - end - - def forward - #@state.debug "moving change forward" + def initialize(state) + @state = state + @path = [state.path,"change"].flatten + @is = state.is - unless defined? @transaction - raise Puppet::Error, - "StateChange '%s' tried to be executed outside of transaction" % - self + if state.insync? + raise Puppet::Error.new( + "Tried to create a change for in-sync state %s" % state.name + ) end + @should = state.should - return self.go - end - - # Generate an appropriate event from the is/should values. - def genevent - tail = if @state.name == :ensure - if @should == :present - "created" - elsif @should == :absent - "deleted" - else - @should.to_s - end - else - "changed" - end - - [state.parent.class.name.to_s, tail].join("_").intern + @changed = false end # Perform the actual change. This method can go either forward or @@ -81,39 +45,28 @@ module Puppet # [@state.is.inspect, @state.should.inspect] # The transaction catches any exceptions here. - if @state.method(:sync).arity == 0 - @state.warnstamp :syncwoutvalue, "sync() should accept a value" - events = @state.sync - else - events = @state.sync(@should) + events = @state.sync + if events.nil? + return nil end - unless events.is_a? Array + if events.is_a?(Array) + if events.empty? + return nil + end + else events = [events] end + + return events.collect { |event| + # default to a simple event type + if ! event.is_a?(Symbol) + @state.warning("State '%s' returned invalid event '%s'; resetting to default" % + [@state.class,event]) - events = events.collect do |e| - if e.nil? - genevent() - else - if ! e.is_a?(Symbol) - @state.warning( - "State '%s' returned invalid event '%s'; resetting" % - [@state.class,e] - ) - genevent() - else - e - end + event = @state.parent.class.name.id2name + "_changed" end - end.reject { |e| e == :nochange } - - if events.empty? - return nil - end - - return events.collect { |event| # i should maybe include object type, but the event type # should basically point to that, right? #:state => @state, @@ -129,25 +82,36 @@ module Puppet } end - def initialize(state, is = nil) - @state = state - @path = [state.path,"change"].flatten + def forward + #@state.debug "moving change forward" - if is - @is = is - else - state.warning "did not pass 'is' to statechange" - @is = state.is + unless defined? @transaction + raise Puppet::Error, + "StateChange '%s' tried to be executed outside of transaction" % + self end - if state.insync? - raise Puppet::Error.new( - "Tried to create a change for in-sync state %s" % state.name - ) - end - @should = state.should + return self.go + end - @changed = false + # Switch the goals of the state, thus running the change in reverse. + def backward + @state.should = @is + @state.retrieve + + unless defined? @transaction + raise Puppet::Error, + "StateChange '%s' tried to be executed outside of transaction" % + self + end + unless @state.insync? + @state.info "Backing %s" % self + return self.go + else + @state.debug "rollback is already in sync: %s vs. %s" % + [@state.is.inspect, @state.should.inspect] + return nil + end end def noop diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index d947afbf2..899aaa456 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -90,7 +90,7 @@ class Transaction # Mark that our change happened, so it can be reversed # if we ever get to that point - unless events.nil? + unless events.nil? or (events.is_a?(Array) and events.empty?) change.changed = true @objectmetrics[:applied] += 1 end @@ -98,15 +98,6 @@ class Transaction events }.flatten.reject { |e| e.nil? } - # If our child responds to a 'flush' method, call it. - if childevents.length > 0 and child.respond_to? :flush - begin - child.flush - rescue => detail - raise Puppet::Error, "Could not flush: %s" % detail, detail.backtrace - end - end - unless changes.empty? # Record when we last synced child.cache(:synced, Time.now) @@ -205,9 +196,6 @@ class Transaction [self.object_id, @count] allevents - ensure - # Unset 'is' everywhere. This is relatively hackish, but, eh. - @objects.each do |o| o.clear end end # Determine whether a given object has failed. @@ -329,9 +317,6 @@ class Transaction # And return the events for collection events }.flatten.reject { |e| e.nil? } - ensure - # Unset 'is' everywhere. This is relatively hackish, but, eh. - @objects.each do |o| o.clear end end # Trigger any subscriptions to a child. This does an upwardly recursive diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 53712d365..d87cbcbd3 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -29,10 +29,13 @@ class Type < Puppet::Element attr_accessor :file, :line attr_reader :tags, :parent - attr_accessor :implicit - attr_writer :title + attr_writer :implicit, :title def implicit? - self.implicit + if defined? @implicit and @implicit + return true + else + return false + end end include Enumerable @@ -53,7 +56,10 @@ class Type < Puppet::Element # iterate across all of the subclasses of Type def self.eachtype @types.each do |name, type| - yield type + # Only consider types that have names + #if ! type.parameters.empty? or ! type.validstates.empty? + yield type + #end end end @@ -301,7 +307,6 @@ class Type < Puppet::Element def self.clear if defined? @objects @objects.each do |name, obj| - obj.clear obj.remove(true) end @objects.clear @@ -938,10 +943,8 @@ class Type < Puppet::Element end end - # Remove a state from the object; useful in testing or in cleanup - # when an error has been encountered. Removes all objects with a given name, - # not just states. Some classes have both an alias state and an alias - # metaparam. + # remove a state from the object; useful in testing or in cleanup + # when an error has been encountered def delete(attr) case attr when Puppet::Type @@ -949,20 +952,13 @@ class Type < Puppet::Element @children.delete(attr) end else - done = false if @states.has_key?(attr) @states.delete(attr) - done = true - end - if @parameters.has_key?(attr) + elsif @parameters.has_key?(attr) @parameters.delete(attr) - done = true - end - if @metaparams.has_key?(attr) + elsif @metaparams.has_key?(attr) @metaparams.delete(attr) - done = true - end - unless done + else raise Puppet::DevError.new("Undefined attribute '#{attr}' in #{self}") end end @@ -1269,7 +1265,7 @@ class Type < Puppet::Element # Remove the reference to the provider. if self.provider - @provider.remove + @provider.clear @provider = nil end end @@ -1711,19 +1707,6 @@ class Type < Puppet::Element #@cache[name] = value end - # Remove any vestages of the transaction we're in. This is currently the only - # way to know if you're within a transaction -- if '@is' is set, yes, else, - # no. - def clear - @states.each do |name, state| - state.is = nil - end - - if provider and provider.respond_to? :clear - provider.clear - end - end - # Look up the schedule and set it appropriately. This is done after # the instantiation phase, so that the schedule can be anywhere in the # file. @@ -1964,25 +1947,9 @@ class Type < Puppet::Element def retrieve # it's important to use the method here, as it follows the order # in which they're defined in the object - is = {} states().each { |state| - is[state.name] = state.retrieve + state.retrieve } - - is - end - - # Convert our object to a hash. This just includes states. - def to_hash - rethash = {} - - [@parameters, @metaparams, @states].each do |hash| - hash.each do |name, obj| - rethash[name] = obj.value - end - end - - rethash end # convert to a string @@ -2033,22 +2000,30 @@ class Type < Puppet::Element end # Retrieve the changes associated with all of the states. - def statechanges(is) + def statechanges # If we are changing the existence of the object, then none of # the other states matter. changes = [] - if @states.include?(:ensure) and ! @states[:ensure].insync? - changes = [Puppet::StateChange.new(@states[:ensure], is[:ensure])] + #self.info "ensuring %s from %s" % + # [@states[:ensure].should, @states[:ensure].is] + changes = [Puppet::StateChange.new(@states[:ensure])] # Else, if the 'ensure' state is correctly absent, then do # nothing - elsif @states.include?(:ensure) and is[:ensure] == :absent + elsif @states.include?(:ensure) and @states[:ensure].is == :absent + #self.info "Object is correctly absent" return [] else + #if @states.include?(:ensure) + # self.info "ensure: Is: %s, Should: %s" % + # [@states[:ensure].is, @states[:ensure].should] + #else + # self.info "no ensure state" + #end changes = states().find_all { |state| ! state.insync? }.collect { |state| - Puppet::StateChange.new(state, is[state.name]) + Puppet::StateChange.new(state) } end @@ -2084,35 +2059,11 @@ class Type < Puppet::Element # it's important that we call retrieve() on the type instance, # not directly on the state, because it allows the type to override # the method, like pfile does - is = self.retrieve - - unless is.is_a? Hash - warnstamp :nohashreturned, "'retrieve' did not return hash" - is = {} - end - - @@novalreturned ||= {} - states.each do |state| - unless is.include? state.name - warnstamp "no#{state.name}returned", - "did not get %s from retrieve" % state.name - is[state.name] = state.is - end - end - - # For now, set the 'is' values - is.each do |name, value| - # A special value to indicate we shouldn't do anything here; usually - # the result of an internal error. - next if value == :unmanaged - if @states.include? name - @states[name].is = value - end - end + self.retrieve # states() is a private method, returning an ordered list unless self.class.depthfirst? - changes += statechanges(is) + changes += statechanges() end changes << @children.collect { |child| @@ -2122,7 +2073,7 @@ class Type < Puppet::Element } if self.class.depthfirst? - changes += statechanges(is) + changes += statechanges() end changes.flatten! diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index a911dac04..03ac24d29 100755 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -1,3 +1,7 @@ +require 'etc' +require 'facter' +require 'puppet/type/state' +require 'puppet/filetype' require 'puppet/type/parsedtype' module Puppet @@ -5,7 +9,7 @@ module Puppet # as parameters, with the 'command' as the single state. Also requires a # completely symbolic 'name' paremeter, which gets written to the file # and is used to manage the job. - newtype(:cron, Puppet::Type::ParsedType) do + newtype(:cron) do # A base class for all of the Cron parameters, since they all have # similar argument checking going on. We're stealing the base class @@ -333,6 +337,9 @@ module Puppet } " + @instances = {} + @tabs = {} + class << self attr_accessor :filetype diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 8ccebd242..0dc3ec02a 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -111,29 +111,19 @@ module Puppet end end - def insync? - case self.is - when :failedchecks - return true - when :shouldrun - return false - else - raise ArgumentError, "Invalid 'is' value '%s'" % self.is.inspect - end - end - - # Figure out whether we should run. + # First verify that all of our checks pass. def retrieve - # First make sure all of our checks pass. + # Default to somethinng + if @parent.check - return :shouldrun + self.is = :notrun else - return :failedchecks + self.is = self.should end end # Actually execute the command. - def sync(value = nil) + def sync olddir = nil self.checkexe @@ -454,7 +444,6 @@ module Puppet val = [val] unless val.is_a? Array val.each do |value| unless @parameters[check].check(value) - info "Failed '%s' check" % check return false end end diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb index c6e3324a7..b03f57907 100755 --- a/lib/puppet/type/group.rb +++ b/lib/puppet/type/group.rb @@ -26,12 +26,16 @@ module Puppet newstate(:ensure) do desc "The basic state that the object should be in." - newvalue(:present, :event => :group_created) do + newvalue(:present) do provider.create + + :group_created end - newvalue(:absent, :event => :group_deleted) do + newvalue(:absent) do provider.delete + + :group_removed end # If they're talking about the thing at all, they generally want to @@ -46,7 +50,7 @@ module Puppet def change_to_s begin - if self.is == :absent + if @is == :absent return "created" elsif self.should == :absent return "removed" @@ -65,11 +69,30 @@ module Puppet def retrieve if provider.exists? - return :present + @is = :present else - return :absent + @is = :absent end end + + # The default 'sync' method only selects among a list of registered + # values. + def sync + if self.insync? + self.info "already in sync" + return nil + #else + #self.info "%s vs %s" % [self.is.inspect, self.should.inspect] + end + unless self.class.values + self.devfail "No values defined for %s" % + self.class.name + end + + # Set ourselves to whatever our should value is. + self.set + end + end newstate(:gid) do @@ -100,17 +123,16 @@ module Puppet end def retrieve - return provider.gid + @is = provider.gid end - def sync(value) + def sync if self.should == :absent raise Puppet::DevError, "GID cannot be deleted" else - provider.gid = value + provider.gid = self.should + :group_modified end - - return nil end munge do |gid| @@ -162,19 +184,18 @@ module Puppet def retrieve if @provider.exists? - return super + super else # the group does not exist #unless @states.include?(:gid) # self[:gid] = :auto #end - current = {} @states.each { |name, state| - current[state.name] = :absent + state.is = :absent } - return current + return end end end diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb deleted file mode 100755 index 641ff8592..000000000 --- a/lib/puppet/type/mount.rb +++ /dev/null @@ -1,117 +0,0 @@ -require 'puppet/type/parsedtype' - -module Puppet - newtype(:mount, Puppet::Type::ParsedType) do - newstate(:ensure) do - desc "Control what to do with this mount. If the value is - ``present``, the mount is entered into the mount table, - but not mounted, if it is ``absent``, the entry is removed - from the mount table and the filesystem is unmounted if - currently mounted, if it is ``mounted``, the filesystem - is entered into the mount table and mounted." - - newvalue(:present, :event => :mount_created) do - end - - newvalue(:absent, :event => :mount_deleted) do - if provider.mounted? - provider.unmount - end - end - - newvalue(:mounted, :event => :mount_mounted) do - # We have to flush any changes to disk. - @parent.flush - - provider.mount - end - - defaultto do - if @parent.managed? - :mounted - else - nil - end - end - - def retrieve - if provider.mounted? - return :mounted - else - val = super() - return val - end - end - - # The default method doesn't, um, do anything, since it's all - # done in the flush at the end. - #def sync(value) - # set(value) - #end - end - - newstate(:device) do - desc "The device providing the mount. This can be whatever - device is supporting by the mount, including network - devices or devices specified by UUID rather than device - path, depending on the operating system." - end - - # Solaris specifies two devices, not just one. - newstate(:blockdevice) do - desc "The the device to fsck. This is state is only valid - on Solaris, and in most cases will default to the correct - value." - - # Default to the device but with "dsk" replaced with "rdsk". - defaultto do - if Facter["operatingsystem"].value == "Solaris" - device = @parent.value(:device) - if device =~ %r{/dsk/} - device.sub(%r{/dsk/}, "/rdsk/") - else - nil - end - else - nil - end - end - end - - newstate(:fstype) do - desc "The mount type. Valid values depend on the - operating system." - end - - newstate(:options) do - desc "Mount options for the mounts, as they would - appear in the fstab." - end - - newstate(:pass) do - desc "The pass in which the mount is checked." - end - - newstate(:atboot) do - desc "Whether to mount the mount at boot. Not all platforms - support this." - end - - newstate(:dump) do - desc "Whether to dump the mount. Not all platforms - support this." - end - - newparam(:path) do - desc "The mount path for the mount." - - isnamevar - end - - @doc = "Manages mounted mounts, including putting mount - information into the mount table. The actual behavior depends - on the value of the 'ensure' parameter." - end -end - -# $Id$ diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index 3c4d20cc9..f7ae0f0d4 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -136,7 +136,8 @@ module Puppet @is = @parent.retrieve end - def sync(value) + def sync + value = self.should unless value.is_a?(Symbol) value = value.intern end diff --git a/lib/puppet/type/parsedtype.rb b/lib/puppet/type/parsedtype.rb index 9d37adf17..b0e5f618d 100755 --- a/lib/puppet/type/parsedtype.rb +++ b/lib/puppet/type/parsedtype.rb @@ -4,205 +4,352 @@ require 'puppet/filetype' require 'puppet/type/state' module Puppet - # The base parameter for all of these types. Its only job is to copy - # the 'should' value to the 'is' value and to do support the right logging - # and such. - class State::ParsedParam < Puppet::State - # This is the info retrieved from disk. - attr_accessor :found - - def self.isoptional - @isoptional = true - end + class State + # The base parameter for all of these types. Its only job is to copy + # the 'should' value to the 'is' value and to do support the right logging + # and such. + class ParsedParam < Puppet::State + def self.isoptional + @isoptional = true + end - def self.isoptional? - if defined? @isoptional - return @isoptional - else - return false + def self.isoptional? + if defined? @isoptional + return @isoptional + else + return false + end end - end - # By default, support ':absent' as a value for optional - # parameters. Any parameters that define their own validation - # need to do this manuallly. - validate do |value| - if self.class.isoptional? and ( - value == "absent" or value == :absent - ) - return :absent - else - return value + # By default, support ':absent' as a value for optional + # parameters. Any parameters that define their own validation + # need to do this manuallly. + validate do |value| + if self.class.isoptional? and ( + value == "absent" or value == :absent + ) + return :absent + else + return value + end end - end - def clear - super - @found = nil - end + # Fix things so that the fields have to match exactly, instead + # of only kinda + def insync? + self.is == self.should + end - # Fix things so that the fields have to match exactly, instead - # of only kinda - def insync? - self.is == self.should - end + # Normally this would retrieve the current value, but our state is not + # actually capable of doing so. + def retrieve + # If we've synced, then just copy the values over and return. + # This allows this state to behave like any other state. + if defined? @synced and @synced + # by default, we only copy over the first value. + @is = @synced + @synced = false + return + end - # Normally this would retrieve the current value, but our state is not - # actually capable of doing so. So, we retrieve the whole object and - # just collect our current state. Note that this method is not called - # during a transaction, since transactions call the parent object method. - def retrieve - @parent.retrieve[self.name] - end + unless defined? @is and ! @is.nil? + @is = :absent + end + end - # If the ensure state is out of sync, it will always be called - # first, so I don't need to worry about that. - def sync(value) - # We only pass through to the parent method if there are values - # defined. Otherwise, there's no work to do, since all of the - # work is done in the flush. - if self.class.values and ! self.class.values.empty? - super(value) - end - # The value gets flushed later. - return nil - end - end + # If the ensure state is out of sync, it will always be called + # first, so I don't need to worry about that. + def sync(nostore = false) + ebase = @parent.class.name.to_s - # The collection of classes that are just simple records aggregated - # into a file. See 'host.rb' for an example. - class Type::ParsedType < Puppet::Type - @name = :parsedtype - - # Convert the hash to an object. - def self.hash2obj(hash) - obj = nil - - namevar = self.namevar - unless hash.include?(namevar) and hash[namevar] - raise Puppet::DevError, "Hash was not passed with namevar" - end - - # if the obj already exists with that name... - if obj = self[hash[namevar]] - # We're assuming here that objects with the same name - # are the same object, which *should* be the case, assuming - # we've set up our naming stuff correctly everywhere. - - # Mark found objects as present - obj.is = [:ensure, :present] - obj.state(:ensure).found = :present - hash.each { |param, value| - if state = obj.state(param) - state.is = value - elsif val = obj[param] - obj[param] = val - else - # There is a value on disk, but it should go away - obj.is = [param, value] - obj[param] = :absent + tail = nil + if self.class.name == :ensure + # We're either creating or destroying the object + if @is == :absent + #@is = self.should + tail = "created" + + # If we're creating it, then sync all of the other states + # but tell them not to store (we'll store just once, + # at the end). + unless nostore + @parent.eachstate { |state| + next if state == self or state.name == :ensure + state.sync(true) + } + end + elsif self.should == :absent + @parent.remove(true) + tail = "deleted" end - } - else - # create a new obj, since no existing one seems to - # match - obj = self.create(namevar => hash[namevar]) - - # We can't just pass the hash in at object creation time, - # because it sets the should value, not the is value. - hash.delete(namevar) - hash.each { |param, value| - obj.is = [param, value] - } + else + # We don't do the work here, it gets done in 'store' + tail = "changed" + end + @synced = self.should + + # This should really only be done once per run, rather than + # every time. I guess we need some kind of 'flush' mechanism. + if nostore + self.retrieve + else + @parent.store + end + + return (ebase + "_" + tail).intern end end + end - # Override 'newstate' so that all states default to having the - # correct parent type - def self.newstate(name, options = {}, &block) - options[:parent] ||= Puppet::State::ParsedParam - super(name, options, &block) - end + class Type + # The collection of classes that are just simple records aggregated + # into a file. See 'host.rb' for an example. + class ParsedType < Puppet::Type + @name = :parsedtype + class << self + attr_accessor :filetype, :hostfile, :fields + attr_reader :path + attr_writer :fileobj + end - def self.list - retrieve.collect { |i| i.is_a? Hash }.collect { |i| hash2obj(i) } - end + # Override 'newstate' so that all states default to having the + # correct parent type + def self.newstate(name, options = {}, &block) + options[:parent] ||= Puppet::State::ParsedParam + super(name, options, &block) + end - def self.listbyname - retrieve.collect { |i| i.is_a? Hash }.collect { |i| i[:name] } - end + # Add another type var. + def self.initvars + @instances = [] + super + end - # Make sure they've got an explicit :ensure class. - def self.postinit - unless validstate? :ensure - newstate(:ensure) do - newvalue(:present) do - # The value will get flushed appropriately - return nil - end + # In addition to removing the instances in @objects, we have to remove + # per-user host tab information. + def self.clear + @instances = [] + @fileobj = nil + super + end - newvalue(:absent) do - # The value will get flushed appropriately - return nil - end + # Add a non-object comment or whatever to our list of instances + def self.comment(line) + @instances << line + end + + # Override the default Puppet::Type method, because instances + # also need to be deleted from the @instances hash + def self.delete(child) + if @instances.include?(child) + @instances.delete(child) + end + super + end + + # Initialize the object if necessary. + def self.fileobj + @fileobj ||= @filetype.new(@path) + + @fileobj + end + + # Return the header placed at the top of each generated file, warning + # users that modifying this file manually is probably a bad idea. + def self.header +%{# HEADER: This file was autogenerated at #{Time.now} +# HEADER: by puppet. While it can still be managed manually, it +# HEADER: is definitely not recommended.\n} + end + + # Convert the hash to an object. + def self.hash2obj(hash) + obj = nil - defaultto do - if @parent.managed? - :present + namevar = self.namevar + unless hash.include?(namevar) and hash[namevar] + raise Puppet::DevError, "Hash was not passed with namevar" + end + + # if the obj already exists with that name... + if obj = self[hash[namevar]] + # We're assuming here that objects with the same name + # are the same object, which *should* be the case, assuming + # we've set up our naming stuff correctly everywhere. + + # Mark found objects as present + obj.is = [:ensure, :present] + hash.each { |param, value| + if state = obj.state(param) + state.is = value + elsif val = obj[param] + obj[param] = val else - nil + # There is a value on disk, but it should go away + obj.is = [param, value] + obj[param] = :absent end - end + } + else + # create a new obj, since no existing one seems to + # match + obj = self.create(namevar => hash[namevar]) + + # We can't just pass the hash in at object creation time, + # because it sets the should value, not the is value. + hash.delete(namevar) + hash.each { |param, value| + obj.is = [param, value] + } end + + # And then add it to our list of instances. This maintains the order + # in the file. + @instances << obj end - end - def exists? - h = self.retrieve + def self.list + retrieve - if h[:ensure] == :absent - return false - else - return true + self.collect do |obj| + obj + end end - end - # Flush our content to disk. - def flush - provider.store(self.to_hash) - end + # Return the last time the file was loaded. Could + # be used for reducing writes, but currently is not. + def self.loaded?(user) + fileobj().loaded + end - # Retrieve our current state from our provider - def retrieve - h = nil - if h = provider.hash and ! h.empty? - h[:ensure] ||= :present - - # If they passed back info we don't have, then mark it to - # be deleted. - h.each do |name, value| - next unless self.class.validstate?(name) - unless @states.has_key? name - self.newstate(name, :should => :absent) - end + # Parse a file + # + # Subclasses must override this method. + def self.parse(text) + raise Puppet::DevError, "Parse was not overridden in %s" % + self.name + end + + # If they change the path, we need to get rid of our cache object + def self.path=(path) + @fileobj = nil + @path = path + end + + # Retrieve the text for the file. Returns nil in the unlikely + # event that it doesn't exist. + def self.retrieve + text = fileobj.read + if text.nil? or text == "" + # there is no file + return nil + else + # First we mark all of our objects absent; any objects + # subsequently found will be marked present + self.each { |obj| + obj.is = [:ensure, :absent] + } + + # We clear this, so that non-objects don't get duplicated + @instances.clear + self.parse(text) end + end - @states.each do |name, state| - unless h.has_key? name - h[name] = :absent - end + # Write out the file. + def self.store + # Make sure all of our instances are in the to-be-written array + self.each do |inst| + @instances << inst unless @instances.include? inst + end + + if @instances.empty? + Puppet.notice "No %s instances for %s" % [self.name, @path] + else + fileobj.write(self.to_file()) end - return h - else - h = {} - @states.each do |name, state| - h[name] = :absent + end + + # Collect all Host instances convert them into literal text. + def self.to_file + str = self.header() + unless @instances.empty? + str += @instances.reject { |obj| + # Don't write out objects that should be absent + if obj.is_a?(self) + if obj.should(:ensure) == :absent + true + end + end + }.collect { |obj| + if obj.is_a?(self) + obj.to_record + else + obj.to_s + end + }.join("\n") + "\n" + + return str + else + Puppet.notice "No %s instances" % self.name + return "" end end - return h + # The 'store' method knows how to handle absence vs. presence + def create + self.store + end + + # The 'store' method knows how to handle absence vs. presence + def destroy + self.store + end + + # hash2obj marks the 'ensure' state as present + def exists? + @states.include?(:ensure) and @states[:ensure].is == :present + end + + # Override the default Puppet::Type method because we need to call + # the +@filetype+ retrieve method. + def retrieve + self.class.retrieve() + + self.eachstate { |st| + st.retrieve + } + end + + # Write the entire file out. + def store + self.class.store() + end + + def value(name) + unless name.is_a? Symbol + name = name.intern + end + if @states.include? name + val = @states[name].value + if val == :absent + return nil + else + return val + end + elsif @parameters.include? name + return @parameters[name].value + else + return nil + end + end end end end +require 'puppet/type/parsedtype/host' +require 'puppet/type/parsedtype/port' +require 'puppet/type/parsedtype/mount' +require 'puppet/type/parsedtype/sshkey' + # $Id$ diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/parsedtype/host.rb index d68fe25a2..00cfe7857 100755 --- a/lib/puppet/type/host.rb +++ b/lib/puppet/type/parsedtype/host.rb @@ -1,7 +1,11 @@ +require 'etc' +require 'facter' require 'puppet/type/parsedtype' +require 'puppet/type/state' module Puppet newtype(:host, Puppet::Type::ParsedType) do + newstate(:ip) do desc "The host's IP address." end @@ -13,19 +17,6 @@ module Puppet make those aliases available in your Puppet scripts and also on disk." - # Make sure our "is" value is always an array. - def is - current = super - unless current.is_a? Array - current = [current] - end - current - end - - def is_to_s - self.is.join(" ") - end - # We have to override the feeding mechanism; it might be nil or # white-space separated def is=(value) @@ -84,6 +75,69 @@ module Puppet @doc = "Installs and manages host entries. For most systems, these entries will just be in /etc/hosts, but some systems (notably OS X) will have different solutions." + + @instances = [] + + @path = "/etc/hosts" + @fields = [:ip, :name, :alias] + + @filetype = Puppet::FileType.filetype(:flat) + + # Parse a host file + # + # This method also stores existing comments, and it stores all host + # jobs in order, mostly so that comments are retained in the order + # they were written and in proximity to the same jobs. + def self.parse(text) + count = 0 + hash = {} + text.chomp.split("\n").each { |line| + case line + when /^#/, /^\s*$/: + # add comments and blank lines to the list as they are + @instances << line + else + if line.sub!(/^(\S+)\s+(\S+)\s*/, '') + hash[:ip] = $1 + hash[:name] = $2 + + unless line == "" + line.sub!(/\s*/, '') + line.sub!(/^([^#]+)\s*/) do |value| + aliases = $1 + unless aliases =~ /^\s*$/ + hash[:alias] = aliases + end + + "" + end + end + else + raise Puppet::Error, "Could not match '%s'" % line + end + + if hash[:alias] == "" + hash.delete(:alias) + end + + hash2obj(hash) + + hash.clear + count += 1 + end + } + end + + # Convert the current object into a host-style string. + def to_record + str = "%s\t%s" % [self.state(:ip).value, self[:name]] + + if value = self.value(:alias) + str += "\t%s" % value.join("\t") + end + + str + end end end diff --git a/lib/puppet/type/parsedtype/mount.rb b/lib/puppet/type/parsedtype/mount.rb new file mode 100755 index 000000000..3f186ddca --- /dev/null +++ b/lib/puppet/type/parsedtype/mount.rb @@ -0,0 +1,271 @@ +require 'etc' +require 'facter' +require 'puppet/type/parsedtype' +require 'puppet/type/state' + +module Puppet + newtype(:mount, Puppet::Type::ParsedType) do + ensurable do + desc "Control what to do with this mount. If the value is + ``present``, the mount is entered into the mount table, + but not mounted, if it is ``absent``, the entry is removed + from the mount table and the filesystem is unmounted if + currently mounted, if it is ``mounted``, the filesystem + is entered into the mount table and mounted." + + newvalue(:present) do + @parent.create() + end + + newvalue(:absent) do + @parent.destroy() + + if @parent.mounted? + @parent.unmount + end + + :mount_removed + end + + newvalue(:mounted) do + if @is == :absent + set_present + end + + @parent.mount + + :mount_mounted + end + + def retrieve + if @parent.mounted? + @is = :mounted + else + val = super() + @is = val + end + end + end + + newstate(:device) do + desc "The device providing the mount. This can be whatever + device is supporting by the mount, including network + devices or devices specified by UUID rather than device + path, depending on the operating system." + end + + # Solaris specifies two devices, not just one. + newstate(:blockdevice) do + desc "The the device to fsck. This is state is only valid + on Solaris, and in most cases will default to the correct + value." + + # Default to the device but with "dsk" replaced with "rdsk". + defaultto do + if Facter["operatingsystem"].value == "Solaris" + device = @parent.value(:device) + if device =~ %r{/dsk/} + device.sub(%r{/dsk/}, "/rdsk/") + else + nil + end + else + nil + end + end + end + + newstate(:fstype) do + desc "The mount type. Valid values depend on the + operating system." + end + + newstate(:options) do + desc "Mount options for the mounts, as they would + appear in the fstab." + end + + newstate(:pass) do + desc "The pass in which the mount is checked." + end + + newstate(:atboot) do + desc "Whether to mount the mount at boot. Not all platforms + support this." + end + + newstate(:dump) do + desc "Whether to dump the mount. Not all platforms + support this." + end + + newparam(:path) do + desc "The mount path for the mount." + + isnamevar + end + + @doc = "Manages mounted mounts, including putting mount + information into the mount table. The actual behavior depends + on the value of the 'ensure' parameter." + + def self.init + @platform = Facter["operatingsystem"].value + case @platform + when "Solaris": + @path = "/etc/vfstab" + @fields = [:device, :blockdevice, :path, :fstype, :pass, :atboot, + :options] + @defaults = [ nil ] * @fields.size + when "Darwin": + @filetype = Puppet::FileType.filetype(:netinfo) + @filetype.format = "fstab" + @path = "mounts" + @fields = [:device, :path, :fstype, :options, :dump, :pass] + @defaults = [ nil ] * @fields.size + + # How to map the dumped table to what we want + @fieldnames = { + "name" => :device, + "dir" => :path, + "dump_freq" => :dump, + "passno" => :pass, + "vfstype" => :fstype, + "opts" => :options + } + else + @path = "/etc/fstab" + @fields = [:device, :path, :fstype, :options, :dump, :pass] + @defaults = [ nil ] * 4 + [ "0", "2" ] + end + + # Allow Darwin to override the default filetype + unless defined? @filetype + @filetype = Puppet::FileType.filetype(:flat) + end + end + + init + + def self.clear + init + super + end + + # Parse a mount tab. + # + # This method also stores existing comments, and it stores all + # mounts in order, mostly so that comments are retained in the + # order they were written and in proximity to the same fses. + def self.parse(text) + # provide a single exception for darwin & netinfo + if @filetype == Puppet::FileType.filetype(:netinfo) + parseninfo(text) + return + end + count = 0 + hash = {} + text.chomp.split("\n").each { |line| + case line + when /^#/, /^\s*$/: + # add comments and blank lines to the list as they are + comment(line) + else + values = line.split(/\s+/) + if @fields.length < values.length + raise Puppet::Error, "Could not parse line %s" % line + end + + values = @defaults.zip(values).collect { |d, v| v || d } + unless @fields.length == values.length + raise Puppet::Error, "Could not parse line %s" % line + end + + @fields.zip(values).each do |field, value| + hash[field] = value + end + + hash2obj(hash) + + hash.clear + count += 1 + end + } + end + + # Parse a netinfo table. + def self.parseninfo(text) + array = @fileobj.to_array(text) + + hash = {} + array.each do |record| + @fieldnames.each do |name, field| + if value = record[name] + if field == :options + hash[field] = value.join(",") + else + hash[field] = value[0] + end + else + raise ArgumentError, "Field %s was not provided" % [name] + end + end + + + hash2obj(hash) + hash.clear + end + end + + # This only works when the mount point is synced to the fstab. + def mount + output = %x{mount #{self[:path]} 2>&1} + + unless $? == 0 + raise Puppet::Error, "Could not mount %s: %s" % [self[:path], output] + end + end + + # This only works when the mount point is synced to the fstab. + def unmount + output = %x{umount #{self[:path]}} + + unless $? == 0 + raise Puppet::Error, "Could not mount %s" % self[:path] + end + end + + # Is the mount currently mounted? + def mounted? + platform = Facter["operatingsystem"].value + df = "df" + case Facter["operatingsystem"].value + # Solaris's df prints in a very weird format + when "Solaris": df = "df -k" + end + %x{#{df}}.split("\n").find do |line| + fs = line.split(/\s+/)[-1] + if platform == "Darwin" + fs == "/private/var/automount" + self[:path] or + fs == self[:path] + else + fs == self[:path] + end + end + end + + # Convert the current object into an fstab-style string. + def to_record + self.class.fields.collect do |field| + if value = self.value(field) + value + else + raise Puppet::Error, + "Could not retrieve value for %s" % field + end + end.join("\t") + end + end +end + +# $Id$ diff --git a/lib/puppet/type/parsedtype/port.rb b/lib/puppet/type/parsedtype/port.rb new file mode 100755 index 000000000..f8a08913f --- /dev/null +++ b/lib/puppet/type/parsedtype/port.rb @@ -0,0 +1,261 @@ +require 'etc' +require 'facter' +require 'puppet/type/parsedtype' +require 'puppet/type/state' + +module Puppet + newtype(:port, Puppet::Type::ParsedType) do + newstate(:protocols) do + desc "The protocols the port uses. Valid values are *udp* and *tcp*. + Most services have both protocols, but not all. If you want + both protocols, you must specify that; Puppet replaces the + current values, it does not merge with them. If you specify + multiple protocols they must be as an array." + + def is=(value) + case value + when String + @is = value.split(/\s+/) + else + @is = value + end + end + + def is + @is + end + + # We actually want to return the whole array here, not just the first + # value. + def should + if defined? @should + if @should[0] == :absent + return :absent + else + return @should + end + else + return nil + end + end + + validate do |value| + valids = ["udp", "tcp", "ddp", :absent] + unless valids.include? value + raise Puppet::Error, + "Protocols can be either 'udp' or 'tcp', not %s" % value + end + end + end + + newstate(:number) do + desc "The port number." + end + + newstate(:description) do + desc "The port description." + isoptional + end + + newstate(:alias) do + desc "Any aliases the port might have. Multiple values must be + specified as an array. Note that this state has the same name as + one of the metaparams; using this state to set aliases will make + those aliases available in your Puppet scripts and also on disk." + + isoptional + + # We have to override the feeding mechanism; it might be nil or + # white-space separated + def is=(value) + # If it's just whitespace, ignore it + case value + when /^\s+$/ + @is = nil + when String + @is = value.split(/\s+/) + when Symbol + @is = value + else + raise Puppet::DevError, "Invalid value %s" % value.inspect + end + end + + # We actually want to return the whole array here, not just the first + # value. + def should + if defined? @should + if @should[0] == :absent + return :absent + else + return @should + end + else + return nil + end + end + + validate do |value| + if value.is_a? String and value =~ /\s/ + raise Puppet::Error, + "Aliases cannot have whitespace in them: %s" % + value.inspect + end + end + + munge do |value| + unless value == "absent" or value == :absent + # Add the :alias metaparam in addition to the state + @parent.newmetaparam( + @parent.class.metaparamclass(:alias), value + ) + end + value + end + end + + newparam(:name) do + desc "The port name." + + isnamevar + end + + @doc = "Installs and manages port entries. For most systems, these + entries will just be in /etc/services, but some systems (notably OS X) + will have different solutions." + + @path = "/etc/services" + @fields = [:ip, :name, :alias] + + @filetype = Puppet::FileType.filetype(:flat) + + # Parse a services file + # + # This method also stores existing comments, and it stores all port + # info in order, mostly so that comments are retained in the order + # they were written and in proximity to the same ports. + def self.parse(text) + count = 0 + hash = {} + text.chomp.split("\n").each { |line| + hash.clear + case line + when /^#/, /^\s*$/: + # add comments and blank lines to the list as they are + @instances << line + else + if line.sub!(/^(\S+)\s+(\d+)\/(\w+)\s*/, '') + hash[:name] = $1 + hash[:number] = $2 + hash[:protocols] = $3 + + unless line == "" + line.sub!(/^([^#]+)\s*/) do |value| + aliases = $1 + + # Remove any trailing whitespace + aliases.strip! + unless aliases =~ /^\s*$/ + hash[:alias] = aliases + end + + "" + end + + line.sub!(/^\s*#\s*(.+)$/) do |value| + desc = $1 + unless desc =~ /^\s*$/ + hash[:description] = desc.sub(/\s*$/, '') + end + + "" + end + end + else + if line =~ /^\s+\d+/ and + Facter["operatingsystem"].value == "Darwin" + #Puppet.notice "Skipping wonky OS X port entry %s" % + # line.inspect + next + end + raise Puppet::Error, "Could not match '%s'" % line + end + + # If there's already a service with this name, then check + # to see if the only difference is the proto; if so, just + # add our proto and walk away + if obj = self[hash[:name]] + if obj.portmerge(hash) + next + end + end + + hash2obj(hash) + + count += 1 + end + } + end + + def portmerge(hash) + unless @states.include?(:protocols) + return false + end + + # This method is only called from parsing, so we only worry + # about 'is' values. + proto = self.state(:protocols).is + + if proto.nil? or proto == :absent + # We are an unitialized object; we've got 'should' + # values but no 'is' values + return false + end + + # If this is happening, our object exists + self.is = [:ensure, :present] + + if hash[:protocols] + # The protocol can be a symbol, so... + if proto.is_a?(Symbol) + proto = [] + end + # Check to see if it already includes our proto + unless proto.include?(hash[:protocols]) + # We are missing their proto + proto << hash[:protocols] + @states[:protocols].is = proto + end + end + + if hash.include?(:description) and ! @states.include?(:description) + self.is = [:description, hash[:description]] + end + + return true + end + + # Convert the current object into one or more services entry. + def to_record + self.state(:protocols).value.collect { |proto| + str = "%s\t%s/%s" % [self[:name], self.value(:number), + proto] + + if value = self.value(:alias) and value != :absent + str += "\t%s" % value.join(" ") + else + str += "\t" + end + + if value = self.value(:description) and value != :absent + str += "\t# %s" % value + else + str += "\t" + end + str + }.join("\n") + end + end +end + +# $Id$ diff --git a/lib/puppet/type/parsedtype/sshkey.rb b/lib/puppet/type/parsedtype/sshkey.rb new file mode 100755 index 000000000..d12d4c697 --- /dev/null +++ b/lib/puppet/type/parsedtype/sshkey.rb @@ -0,0 +1,123 @@ +require 'etc' +require 'facter' +require 'puppet/type/parsedtype' +require 'puppet/type/state' + +module Puppet + newtype(:sshkey, Puppet::Type::ParsedType) do + newstate(:type) do + desc "The encryption type used. Probably ssh-dss or ssh-rsa." + end + + newstate(:key) do + desc "The key itself; generally a long string of hex digits." + end + + # FIXME This should automagically check for aliases to the hosts, just + # to see if we can automatically glean any aliases. + newstate(:alias) do + desc "Any alias the host might have. Multiple values must be + specified as an array. Note that this state has the same name + as one of the metaparams; using this state to set aliases will + make those aliases available in your Puppet scripts." + + # We actually want to return the whole array here, not just the first + # value. + def should + if defined? @should + return @should + else + return nil + end + end + + validate do |value| + if value =~ /\s/ + raise Puppet::Error, "Aliases cannot include whitespace" + end + if value =~ /,/ + raise Puppet::Error, "Aliases cannot include whitespace" + end + end + + # Make a puppet alias in addition. + munge do |value| + # Add the :alias metaparam in addition to the state + @parent.newmetaparam(@parent.class.metaparamclass(:alias), value) + value + end + end + + newparam(:name) do + desc "The host name." + + isnamevar + end + + @doc = "Installs and manages ssh host keys. At this point, this type + only knows how to install keys into /etc/ssh/ssh_known_hosts, and + it cannot manage user authorized keys yet." + + @instances = [] + + # FIXME This should be configurable. + @path = "/etc/ssh/ssh_known_hosts" + @fields = [:name, :type, :key] + + @filetype = Puppet::FileType.filetype(:flat) +# case Facter["operatingsystem"].value +# when "Solaris": +# @filetype = Puppet::FileType::SunOS +# else +# @filetype = Puppet::CronType::Default +# end + + # Parse a host file + # + # This method also stores existing comments, and it stores all host + # jobs in order, mostly so that comments are retained in the order + # they were written and in proximity to the same jobs. + def self.parse(text) + count = 0 + hash = {} + text.chomp.split("\n").each { |line| + case line + when /^#/, /^\s*$/: + # add comments and blank lines to the list as they are + @instances << line + else + hash = {} + fields().zip(line.split(" ")).each { |param, value| + hash[param] = value + } + + if hash[:name] =~ /,/ + names = hash[:name].split(",") + hash[:name] = names.shift + hash[:alias] = names + end + + if hash[:alias] == "" + hash.delete(:alias) + end + + hash2obj(hash) + + hash.clear + count += 1 + end + } + end + + # Convert the current object into a host-style string. + def to_record + name = self[:name] + if @states.include?(:alias) + name += "," + @states[:alias].value.join(",") + end + [name, @states[:type].value, @states[:key].value].join(" ") + end + end +end + +# $Id$ diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb index ac892c069..ec666497d 100644 --- a/lib/puppet/type/pfile.rb +++ b/lib/puppet/type/pfile.rb @@ -691,7 +691,7 @@ module Puppet if child = self.newchild(file, true, options) # Mark any unmanaged files for removal if purge is set. - if self[:purge] == :true and ! child.managed? + if self[:purge] == :true and child.implicit? child[:ensure] = :absent end @@ -761,8 +761,6 @@ module Puppet # a wrapper method to make sure the file exists before doing anything def retrieve - is = {} - if @states.include?(:source) # This probably isn't the best place for it, but we need # to make sure that we have a corresponding checksum state. @@ -772,35 +770,35 @@ module Puppet # We have to retrieve the source info before the recursion happens, # although I'm not exactly clear on why. - is[:source] = @states[:source].retrieve + @states[:source].retrieve end if @parameters.include?(:recurse) self.recurse end - if stat = self.stat(true) - states().each { |state| - # We don't want to call 'describe()' twice, so only do a local - # retrieve on the source. - if state.name == :source - is[state.name] = state.retrieve(false) - else - is[state.name] = state.retrieve - end - } - else + unless stat = self.stat(true) self.debug "File does not exist" @states.each { |name,state| # We've already retreived the source, and we don't # want to overwrite whatever it did. This is a bit # of a hack, but oh well, source is definitely special. next if name == :source - is[name] = :absent + state.is = :absent } + + return end - return is + states().each { |state| + # We don't want to call 'describe()' twice, so only do a local + # retrieve on the source. + if state.name == :source + state.retrieve(false) + else + state.retrieve + end + } end # Set the checksum, from another state. There are multiple states that diff --git a/lib/puppet/type/pfile/checksum.rb b/lib/puppet/type/pfile/checksum.rb index 514c47690..cd82a4439 100755 --- a/lib/puppet/type/pfile/checksum.rb +++ b/lib/puppet/type/pfile/checksum.rb @@ -191,7 +191,7 @@ module Puppet if self.insync? self.debug "Checksum is already in sync" - return :nochange + return nil end #@parent.debug "%s(%s): after refresh, is '%s'" % # [self.class.name,@parent.name,@is] @@ -207,7 +207,7 @@ module Puppet @parent[:path] ) end - return :nochange + return nil end end @@ -215,7 +215,7 @@ module Puppet if self.updatesum return :file_changed else - return :nochange + return nil end end @@ -270,8 +270,6 @@ module Puppet end #@parent.debug "checksum state is %s" % self.is - - return @is end # Store the new sum to the state db. diff --git a/lib/puppet/type/pfile/content.rb b/lib/puppet/type/pfile/content.rb index 3eb875dc2..a8422431f 100755 --- a/lib/puppet/type/pfile/content.rb +++ b/lib/puppet/type/pfile/content.rb @@ -29,36 +29,36 @@ module Puppet def retrieve stat = nil unless stat = @parent.stat - return :absent + @is = :absent + return end if stat.ftype == "link" and @parent[:links] == :ignore - return self.should + self.is = self.should + return end # Don't even try to manage the content on directories if stat.ftype == "directory" and @parent[:links] == :ignore @parent.delete(:content) - return :notmanaged + return end begin - retval = File.read(@parent[:path]) + @is = File.read(@parent[:path]) rescue => detail - retval = :unknown + @is = nil raise Puppet::Error, "Could not read %s: %s" % [@parent.title, detail] end - - return retval end # Just write our content out to disk. - def sync(value) - @parent.write { |f| f.print value } + def sync + @parent.write { |f| f.print self.should } - if self.is == :absent + if @is == :absent return :file_created else return :file_changed diff --git a/lib/puppet/type/pfile/ensure.rb b/lib/puppet/type/pfile/ensure.rb index 0debc245a..ac045dfd6 100755 --- a/lib/puppet/type/pfile/ensure.rb +++ b/lib/puppet/type/pfile/ensure.rb @@ -35,25 +35,19 @@ module Puppet # Most 'ensure' states have a default, but with files we, um, don't. nodefault - newvalue(:absent, :event => :file_deleted) do + newvalue(:absent) do File.unlink(@parent[:path]) end aliasvalue(:false, :absent) - newvalue(:file, :event => :file_created) do + newvalue(:file) do # Make sure we're not managing the content some other way - if state = (@parent.state(:content) || @parent.state(:source)) - # Manually sync the state, and reset its is value to it knows it's - # in sync. - should = state.should - state.commit - - # The 'sync' method here syncs any states that might still be - # out of sync like 'mode', so we need to mark this in sync. - state.is = should + if state = @parent.state(:content) or state = @parent.state(:source) + state.sync else @parent.write(false) { |f| f.flush } + mode = @parent.should(:mode) end return :file_created end @@ -65,7 +59,7 @@ module Puppet set_file end - newvalue(:directory, :event => :directory_created) do + newvalue(:directory) do mode = @parent.should(:mode) parent = File.dirname(@parent[:path]) unless FileTest.exists? parent @@ -87,7 +81,7 @@ module Puppet end - newvalue(:link, :event => :link_created) do + newvalue(:link) do if state = @parent.state(:target) state.retrieve @@ -103,7 +97,7 @@ module Puppet end # Symlinks. - newvalue(/./, :event => :link_created) do + newvalue(/./) do # This code never gets executed. We need the regex to support # specifying it, but the work is done in the 'symlink' code block. end @@ -136,9 +130,8 @@ module Puppet # We have to treat :present specially, because it works with any # type of file. def insync? - is = self.is if self.should == :present - if is.nil? or is == :absent + if @is.nil? or @is == :absent return false else return true @@ -149,22 +142,19 @@ module Puppet end def retrieve - retval = nil if stat = @parent.stat(false) - retval = stat.ftype.intern + @is = stat.ftype.intern else if self.should == :false - retval = :false + @is = :false else - retval = :absent + @is = :absent end end - - return retval end - def sync(value) - event = super(value) + def sync + event = super # There are some cases where all of the work does not get done on # file creation, so we have to do some extra checking. @@ -174,7 +164,7 @@ module Puppet thing.retrieve unless thing.insync? - thing.commit + thing.sync end end diff --git a/lib/puppet/type/pfile/group.rb b/lib/puppet/type/pfile/group.rb index ce6d43422..9431b0e65 100755 --- a/lib/puppet/type/pfile/group.rb +++ b/lib/puppet/type/pfile/group.rb @@ -41,7 +41,8 @@ module Puppet stat = @parent.stat(false) unless stat - return :absent + self.is = :absent + return end # Set our method appropriately, depending on links. @@ -50,7 +51,7 @@ module Puppet else @method = :chown end - return stat.gid + self.is = stat.gid end # Determine if the group is valid, and if so, return the UID @@ -81,7 +82,7 @@ module Puppet # Normal users will only be able to manage certain groups. Right now, # we'll just let it fail, but we should probably set things up so # that users get warned if they try to change to an unacceptable group. - def sync(value) + def sync if @is == :absent @parent.stat(true) self.retrieve @@ -89,17 +90,17 @@ module Puppet if @is == :absent self.debug "File '%s' does not exist; cannot chgrp" % @parent[:path] - return :nochange + return nil end if self.insync? - return :nochange + return nil end end gid = nil - unless gid = Puppet::Util.gid(value) - raise Puppet::Error, "Could not find group %s" % value + unless gid = Puppet::Util.gid(self.should) + raise Puppet::Error, "Could not find group %s" % self.should end begin @@ -107,7 +108,7 @@ module Puppet File.send(@method,nil,gid,@parent[:path]) rescue => detail error = Puppet::Error.new( "failed to chgrp %s to %s: %s" % - [@parent[:path], value, detail.message]) + [@parent[:path], self.should, detail.message]) raise error end return :file_changed diff --git a/lib/puppet/type/pfile/mode.rb b/lib/puppet/type/pfile/mode.rb index 9dd993682..ee6fb37d8 100755 --- a/lib/puppet/type/pfile/mode.rb +++ b/lib/puppet/type/pfile/mode.rb @@ -89,35 +89,35 @@ module Puppet # off mode management entirely. if stat = @parent.stat(false) + self.is = stat.mode & 007777 unless defined? @fixed if defined? @should and @should @should = @should.collect { |s| self.dirmask(s) } end end - return stat.mode & 007777 else - return :absent + self.is = :absent end #self.debug "chmod state is %o" % self.is end - def sync(value) + def sync if @is == :absent @parent.stat(true) self.retrieve if @is == :absent self.debug "File does not exist; cannot set mode" - return :nochange + return nil end if self.insync? # we're already in sync - return :nochange + return nil end end - mode = value + mode = self.should if mode == :absent # This is really only valid for create states... diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/pfile/source.rb index 057673457..744d66f34 100755 --- a/lib/puppet/type/pfile/source.rb +++ b/lib/puppet/type/pfile/source.rb @@ -116,7 +116,6 @@ module Puppet return nil end - retval = nil # If we're a normal file, then set things up to copy the file down. case @stats[:type] when "file": @@ -125,20 +124,20 @@ module Puppet if sum.is == :absent sum.retrieve(true) end - retval = sum.is + @is = sum.is else - retval = :absent + @is = :absent end else self.info "File does not have checksum" - retval = :absent + @is = :absent end # if replace => false then fake the checksum so that the file # is not overwritten. - unless retval == :absent + unless @is == :absent if @parent[:replace] == :false info "Not replacing existing file" - retval = @stats[:checksum] + @is = @stats[:checksum] end end @should = [@stats[:checksum]] @@ -155,11 +154,11 @@ module Puppet end # we'll let the :ensure state do our work @should.clear - retval = true + @is = true when "link": case @parent[:links] when :ignore - retval = :nocopy + @is = :nocopy @should = [:nocopy] self.info "Ignoring link %s" % @source return @@ -176,7 +175,7 @@ module Puppet self.err "Cannot use files of type %s as sources" % @stats[:type] @should = [:nocopy] - retval = :nocopy + @is = :nocopy end # Take each of the stats and set them as states on the local file @@ -197,8 +196,6 @@ module Puppet # @parent.info "Already specified %s" % stat end } - - return retval end # The special thing here is that we need to make sure that 'should' @@ -229,19 +226,22 @@ module Puppet end end - def sync(value) + def sync if @is == :notdescribed self.retrieve # try again if @is == :notdescribed @parent.log "Could not retreive information on %s" % @parent.title - return :nochange + return nil end if @is == @should - return :nochange + return nil end end + case @stats[:type] + when "link": + end unless @stats[:type] == "file" #if @stats[:type] == "directory" #[@parent.name, @is.inspect, @should.inspect] diff --git a/lib/puppet/type/pfile/target.rb b/lib/puppet/type/pfile/target.rb index 6bdc408f2..4a725d652 100644 --- a/lib/puppet/type/pfile/target.rb +++ b/lib/puppet/type/pfile/target.rb @@ -56,8 +56,8 @@ module Puppet def retrieve if @parent.state(:ensure).should == :directory + @is = self.should @linkmaker = true - return self.should else if stat = @parent.stat # If we're just checking the value @@ -73,18 +73,18 @@ module Puppet warning "Changing ensure to directory; recurse is %s but %s" % [@parent[:recurse].inspect, @parent.recurse?] @parent[:ensure] = :directory + @is = should @linkmaker = true - return should else if stat.ftype == "link" + @is = File.readlink(@parent[:path]) @linkmaker = false - return File.readlink(@parent[:path]) else - return :notlink + @is = :notlink end end else - return :absent + @is = :absent end end end diff --git a/lib/puppet/type/pfile/type.rb b/lib/puppet/type/pfile/type.rb index 4a7b9641d..e5db7d694 100755 --- a/lib/puppet/type/pfile/type.rb +++ b/lib/puppet/type/pfile/type.rb @@ -8,21 +8,18 @@ module Puppet #end def retrieve - retval = nil if stat = @parent.stat(false) - retval = stat.ftype + @is = stat.ftype else - retval = :absent + @is = :absent end # so this state is never marked out of sync - @should = [retval] - - return retval + @should = [@is] end - def sync(value) + def sync raise Puppet::Error, ":type is read-only" end end diff --git a/lib/puppet/type/pfile/uid.rb b/lib/puppet/type/pfile/uid.rb index e05ba4f4c..a492b31f4 100755 --- a/lib/puppet/type/pfile/uid.rb +++ b/lib/puppet/type/pfile/uid.rb @@ -81,7 +81,8 @@ module Puppet def retrieve unless stat = @parent.stat(false) - return :absent + @is = :absent + return end # Set our method appropriately, depending on links. @@ -91,17 +92,15 @@ module Puppet @method = :chown end - retval = stat.uid + self.is = stat.uid # On OS X, files that are owned by -2 get returned as really # large UIDs instead of negative ones. This isn't a Ruby bug, # it's an OS X bug, since it shows up in perl, too. - if retval > 120000 - self.warning "current state is silly: %s" % retval - retval = :absent + if @is > 120000 + self.warning "current state is silly: %s" % @is + @is = :absent end - - return retval end # If we're not root, we can check the values but we cannot change @@ -117,19 +116,19 @@ module Puppet end end - def sync(value) + def sync unless Process.uid == 0 unless defined? @@notifieduid self.notice "Cannot manage ownership unless running as root" #@parent.delete(self.name) @@notifieduid = true end - return :nochange + return nil end user = nil - unless user = self.validuser?(value) - tmp = value + unless user = self.validuser?(self.should) + tmp = self.should unless defined? @@usermissing @@usermissing = {} end @@ -140,7 +139,7 @@ module Puppet self.notice "user %s does not exist" % tmp @@usermissing[tmp] = 1 end - return :nochange + return nil end if @is == :absent @@ -148,10 +147,10 @@ module Puppet self.retrieve if @is == :absent self.debug "File does not exist; cannot set owner" - return :nochange + return nil end if self.insync? - return :nochange + return nil end #self.debug "%s: after refresh, is '%s'" % [self.class.name,@is] end diff --git a/lib/puppet/type/port.rb b/lib/puppet/type/port.rb deleted file mode 100755 index 3e73e625f..000000000 --- a/lib/puppet/type/port.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'puppet/type/parsedtype' - -module Puppet - newtype(:port, Puppet::Type::ParsedType) do - @doc = "Installs and manages port entries. For most systems, these - entries will just be in /etc/services, but some systems (notably OS X) - will have different solutions." - - newstate(:protocols) do - desc "The protocols the port uses. Valid values are *udp* and *tcp*. - Most services have both protocols, but not all. If you want - both protocols, you must specify that; Puppet replaces the - current values, it does not merge with them. If you specify - multiple protocols they must be as an array." - - def is=(value) - case value - when String - @is = value.split(/\s+/) - else - @is = value - end - end - - def is - @is - end - - # We actually want to return the whole array here, not just the first - # value. - def should - if defined? @should - if @should[0] == :absent - return :absent - else - return @should - end - else - return nil - end - end - - validate do |value| - valids = ["udp", "tcp", "ddp", :absent] - unless valids.include? value - raise Puppet::Error, - "Protocols can be either 'udp' or 'tcp', not %s" % value - end - end - end - - newstate(:number) do - desc "The port number." - end - - newstate(:description) do - desc "The port description." - isoptional - end - - newstate(:alias) do - desc "Any aliases the port might have. Multiple values must be - specified as an array. Note that this state has the same name as - one of the metaparams; using this state to set aliases will make - those aliases available in your Puppet scripts and also on disk." - - isoptional - - # We actually want to return the whole array here, not just the first - # value. - def should - if defined? @should - if @should[0] == :absent - return :absent - else - return @should - end - else - return nil - end - end - - validate do |value| - if value.is_a? String and value =~ /\s/ - raise Puppet::Error, - "Aliases cannot have whitespace in them: %s" % - value.inspect - end - end - - munge do |value| - unless value == "absent" or value == :absent - # Add the :alias metaparam in addition to the state - @parent.newmetaparam( - @parent.class.metaparamclass(:alias), value - ) - end - value - end - end - - newparam(:name) do - desc "The port name." - - isnamevar - end - end -end - -# $Id$ diff --git a/lib/puppet/type/sshkey.rb b/lib/puppet/type/sshkey.rb deleted file mode 100755 index e769199bf..000000000 --- a/lib/puppet/type/sshkey.rb +++ /dev/null @@ -1,58 +0,0 @@ -module Puppet - newtype(:sshkey, Puppet::Type::ParsedType) do - @doc = "Installs and manages ssh host keys. At this point, this type - only knows how to install keys into /etc/ssh/ssh_known_hosts, and - it cannot manage user authorized keys yet." - - newstate(:type) do - desc "The encryption type used. Probably ssh-dss or ssh-rsa." - end - - newstate(:key) do - desc "The key itself; generally a long string of hex digits." - end - - # FIXME This should automagically check for aliases to the hosts, just - # to see if we can automatically glean any aliases. - newstate(:alias) do - desc "Any alias the host might have. Multiple values must be - specified as an array. Note that this state has the same name - as one of the metaparams; using this state to set aliases will - make those aliases available in your Puppet scripts." - - # We actually want to return the whole array here, not just the first - # value. - def should - if defined? @should - return @should - else - return nil - end - end - - validate do |value| - if value =~ /\s/ - raise Puppet::Error, "Aliases cannot include whitespace" - end - if value =~ /,/ - raise Puppet::Error, "Aliases cannot include whitespace" - end - end - - # Make a puppet alias in addition. - munge do |value| - # Add the :alias metaparam in addition to the state - @parent.newmetaparam(@parent.class.metaparamclass(:alias), value) - value - end - end - - newparam(:name) do - desc "The host name." - - isnamevar - end - end -end - -# $Id$ diff --git a/lib/puppet/type/state.rb b/lib/puppet/type/state.rb index e186c401d..de0cfeb00 100644 --- a/lib/puppet/type/state.rb +++ b/lib/puppet/type/state.rb @@ -8,11 +8,12 @@ require 'puppet/parameter' module Puppet class State < Puppet::Parameter + attr_accessor :is + # Because 'should' uses an array, we have a special method for handling # it. We also want to keep copies of the original values, so that # they can be retrieved and compared later when merging. attr_reader :shouldorig - attr_writer :is class << self attr_accessor :unmanaged @@ -92,19 +93,11 @@ class State < Puppet::Parameter def change_to_s begin if @is == :absent - if self.name == :ensure - return "created as '%s'" % [self.should_to_s] - else - return "defined '%s' as '%s'" % - [self.name, self.should_to_s] - end + return "defined '%s' as '%s'" % + [self.name, self.should_to_s] elsif self.should == :absent or self.should == [:absent] - if self.name == :ensure - return "deleted from '%s'" % self.is_to_s - else - return "undefined %s from '%s'" % - [self.name, self.is_to_s] - end + return "undefined %s from '%s'" % + [self.name, self.is_to_s] else return "%s changed '%s' to '%s'" % [self.name, self.is_to_s, self.should_to_s] @@ -116,11 +109,6 @@ class State < Puppet::Parameter [self.name, detail] end end - - # Just a simple wrapper method to sync our current 'should' value. - def commit - self.sync(self.should) - end # initialize our state def initialize(hash) @@ -179,9 +167,8 @@ class State < Puppet::Parameter end # Look for a matching value - is = self.is @should.each { |val| - if is == val + if @is == val return true end } @@ -190,17 +177,12 @@ class State < Puppet::Parameter return false end - # If the '@is' value is set, then return it, else retrieve it. - def is - @is || self.retrieve - end - # because the @should and @is vars might be in weird formats, # we need to set up a mechanism for pretty printing of the values # default to just the values, but this way individual states can # override these methods def is_to_s - self.is + @is end # Send a log message. @@ -254,11 +236,17 @@ class State < Puppet::Parameter # provider. In other words, if the state name is 'gid', we'll call # 'provider.gid' to retrieve the current value. def retrieve - provider.send(self.class.name) + @is = provider.send(self.class.name) end # Call the method associated with a given value. - def set(value) + def set + if self.insync? + self.log "already in sync" + return nil + end + + value = self.should method = "set_" + value.to_s event = nil if self.respond_to?(method) @@ -288,18 +276,28 @@ class State < Puppet::Parameter end end - if event == :nochange - return :nochange - end - if setevent = self.class.event(value) return setevent else if event and event.is_a?(Symbol) - return event + if event == :nochange + return nil + else + return event + end else - # StateChange will autogenerate an event. - return nil + # Return the appropriate event. + event = case self.should + when :present: (@parent.class.name.to_s + "_created").intern + when :absent: (@parent.class.name.to_s + "_removed").intern + else + (@parent.class.name.to_s + "_changed").intern + end + + #self.log "made event %s because 'should' is %s, 'is' is %s" % + # [event, self.should.inspect, self.is.inspect] + + return event end end end @@ -349,20 +347,20 @@ class State < Puppet::Parameter # The default 'sync' method only selects among a list of registered # values. - def sync(value = nil) - - unless value - warnstamp :novalsynced, "No value passed to sync" - value = self.should + def sync + if self.insync? + self.info "already in sync" + return nil + #else + #self.info "%s vs %s" % [self.is.inspect, self.should.inspect] end - unless self.class.values self.devfail "No values defined for %s" % self.class.name end # Set ourselves to whatever our should value is. - self.set(value || self.should) + self.set end def to_s @@ -387,9 +385,15 @@ class State < Puppet::Parameter @doc ||= "The basic state that the object should be in." end + def self.inherited(sub) + # Add in the two states that everyone will have. + sub.class_eval do + end + end + def change_to_s begin - if self.is == :absent + if @is == :absent return "created" elsif self.should == :absent return "removed" @@ -412,9 +416,9 @@ class State < Puppet::Parameter # @is values set. This seems to be the source of quite a few bugs, # although they're mostly logging bugs, not functional ones. if @parent.exists? - return :present + @is = :present else - return :absent + @is = :absent end end diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index b0c3081e4..3a78d9669 100755 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -50,7 +50,7 @@ module Puppet def change_to_s begin - if self.is == :absent + if @is == :absent return "created" elsif self.should == :absent return "removed" @@ -69,11 +69,30 @@ module Puppet def retrieve if provider.exists? - return :present + @is = :present else - return :absent + @is = :absent end end + + # The default 'sync' method only selects among a list of registered + # values. + def sync + if self.insync? + self.info "already in sync" + return nil + #else + #self.info "%s vs %s" % [self.is.inspect, self.should.inspect] + end + unless self.class.values + self.devfail "No values defined for %s" % + self.class.name + end + + # Set ourselves to whatever our should value is. + self.set + end + end newstate(:uid) do @@ -174,21 +193,24 @@ module Puppet end def is_to_s - self.is.join(",") + @is.join(",") end # We need to override this because the groups need to # be joined with commas def should + unless defined? @is + retrieve + end + @should ||= [] if @parent[:membership] == :inclusive @should.sort else - current = self.is members = @should - if current.is_a?(Array) - members += current + if @is.is_a?(Array) + members += @is end members.uniq.sort end @@ -196,13 +218,9 @@ module Puppet def retrieve if tmp = provider.groups - if tmp == :absent - return tmp - else - return tmp.split(",") - end + @is = tmp.split(",") else - return :absent + @is = :absent end end @@ -210,13 +228,13 @@ module Puppet unless defined? @should and @should return false end - unless defined? self.is and self.is + unless defined? @is and @is return false end - unless self.is.class == @should.class + unless @is.class == @should.class return false end - return self.is.sort == @should.sort + return @is.sort == @should.sort end validate do |value| @@ -225,8 +243,8 @@ module Puppet end end - def sync(value) - provider.groups = value.join(",") + def sync + provider.groups = self.should.join(",") :user_changed end end @@ -356,16 +374,27 @@ module Puppet def retrieve absent = false - current = {} states().each { |state| - if current[:ensure] == :absent - current[state.name] = :absent + if absent + state.is = :absent else - current[state.name] = state.retrieve + state.retrieve end - } - return current + if state.name == :ensure and state.is == :absent + absent = true + next + end + } + #if provider.exists? + # super + #else + # # the user does not exist + # @states.each { |name, state| + # state.is = :absent + # } + # return + #end end end end diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 515a91c36..a818a77bf 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -512,15 +512,6 @@ module Util return seconds end - def warnstamp(var, msg) - $stampwarnings ||= {} - $stampwarnings[self.class] ||= {} - unless $stampwarnings[self.class][var] - warning msg - $stampwarnings[self.class][var] = true - end - end - module_function :memory end end diff --git a/test/data/types/mount/linux.fstab b/test/data/types/mount/linux.fstab index 978103b69..06afe1242 100644 --- a/test/data/types/mount/linux.fstab +++ b/test/data/types/mount/linux.fstab @@ -4,7 +4,7 @@ LABEL=/boot /boot ext3 defaults 1 2 devpts /dev/pts devpts gid=5,mode=620 0 0 tmpfs /dev/shm tmpfs defaults 0 0 LABEL=/home /home ext3 defaults 1 2 -/home /homes auto bind 0 2 +/home /homes auto bind proc /proc proc defaults 0 0 /dev/vg00/lv01 /spare ext3 defaults 1 2 sysfs /sys sysfs defaults 0 0 diff --git a/test/providers/parsedcron.rb b/test/providers/parsedcron.rb deleted file mode 100755 index fafb0ee92..000000000 --- a/test/providers/parsedcron.rb +++ /dev/null @@ -1,744 +0,0 @@ -# Test cron job creation, modification, and destruction - -if __FILE__ == $0 - $:.unshift '..' - $:.unshift '../lib' - $puppetbase = "../.." -end - -require 'puppettest' -require 'puppet' -require 'test/unit' -require 'facter' - -class TestParsedCron < Test::Unit::TestCase - include TestPuppet - def setup - super - @provider = Puppet.type(:cron).provider(:parsed) - - @oldfiletype = @provider.filetype - end - - def teardown - Puppet::FileType.filetype(:ram).clear - @provider.filetype = @oldfiletype - super - end - - def test_provider_existence - assert(@provider, "Could not retrieve provider") - end - - # Here we just create a fake cron type that answers to all of the methods - # but does not modify our actual system. - def mkfaketype - @provider.filetype = Puppet::FileType.filetype(:ram) - end - - def mkcronhash - if defined? @hcount - @hcount += 1 - else - @hcount = 1 - end - - return { - :name => "fakecron%s" % @hcount, - :ip => "192.168.27.%s" % @hcount, - :alias => ["alias%s" % @hcount] - } - end - - def mkcron - hash = mkcronhash() - - fakemodel = fakemodel(:cron, hash[:name]) - - cron = @provider.new(fakemodel) - - hash.each do |name, val| - fakemodel[name] = val - end - assert(cron, "Could not create provider cron") - - return cron - end - - # Back up the user's existing cron tab if they have one. - def cronback - tab = nil - assert_nothing_raised { - tab = Puppet.type(:cron).filetype.read(@me) - } - - if $? == 0 - @currenttab = tab - else - @currenttab = nil - end - end - - # Restore the cron tab to its original form. - def cronrestore - assert_nothing_raised { - if @currenttab - @crontype.filetype.new(@me).write(@currenttab) - else - @crontype.filetype.new(@me).remove - end - } - end - - # Create a cron job with all fields filled in. - def mkcron(name) - cron = nil - assert_nothing_raised { - cron = @crontype.create( - :command => "date > %s/crontest%s" % [tmpdir(), name], - :name => name, - :user => @me, - :minute => rand(59), - :month => "1", - :monthday => "1", - :hour => "1" - ) - } - - return cron - end - - # Run the cron through its paces -- install it then remove it. - def cyclecron(cron) - obj = Puppet::Type::Cron.cronobj(@me) - - text = obj.read - name = cron.name - comp = newcomp(name, cron) - - assert_events([:cron_created], comp) - cron.retrieve - - assert(cron.insync?, "Cron is not in sync") - - assert_events([], comp) - - curtext = obj.read - text.split("\n").each do |line| - assert(curtext.include?(line), "Missing '%s'" % line) - end - obj = Puppet::Type::Cron.cronobj(@me) - - cron[:ensure] = :absent - - assert_events([:cron_deleted], comp) - - cron.retrieve - - assert(cron.insync?, "Cron is not in sync") - assert_events([], comp) - end - - # A simple test to see if we can load the cron from disk. - def test_load - assert_nothing_raised { - @crontype.retrieve(@me) - } - end - - # Test that a cron job turns out as expected, by creating one and generating - # it directly - def test_simple_to_cron - cron = nil - # make the cron - name = "yaytest" - assert_nothing_raised { - cron = @crontype.create( - :name => name, - :command => "date > /dev/null", - :user => @me - ) - } - str = nil - # generate the text - assert_nothing_raised { - str = cron.to_record - } - - assert_equal(str, "# Puppet Name: #{name}\n* * * * * date > /dev/null", - "Cron did not generate correctly") - end - - def test_simpleparsing - @fakefiletype = Puppet::FileType.filetype(:ram) - @crontype.filetype = @fakefiletype - - @crontype.retrieve(@me) - obj = Puppet::Type::Cron.cronobj(@me) - - text = "5 1,2 * 1 0 /bin/echo funtest" - - assert_nothing_raised { - @crontype.parse(@me, text) - } - - @crontype.each do |obj| - assert_equal(["5"], obj.is(:minute), "Minute was not parsed correctly") - assert_equal(["1", "2"], obj.is(:hour), "Hour was not parsed correctly") - assert_equal([:absent], obj.is(:monthday), "Monthday was not parsed correctly") - assert_equal(["1"], obj.is(:month), "Month was not parsed correctly") - assert_equal(["0"], obj.is(:weekday), "Weekday was not parsed correctly") - assert_equal(["/bin/echo funtest"], obj.is(:command), "Command was not parsed correctly") - end - end - - # Test that changing any field results in the cron tab being rewritten. - # it directly - def test_any_field_changes - cron = nil - # make the cron - name = "yaytest" - assert_nothing_raised { - cron = @crontype.create( - :name => name, - :command => "date > /dev/null", - :month => "May", - :user => @me - ) - } - assert(cron, "Cron did not get created") - comp = newcomp(cron) - assert_events([:cron_created], comp) - - assert_nothing_raised { - cron[:month] = "June" - } - - cron.retrieve - - assert_events([:cron_changed], comp) - end - - # Test that a cron job with spaces at the end doesn't get rewritten - def test_trailingspaces - cron = nil - # make the cron - name = "yaytest" - assert_nothing_raised { - cron = @crontype.create( - :name => name, - :command => "date > /dev/null ", - :month => "May", - :user => @me - ) - } - comp = newcomp(cron) - - assert_events([:cron_created], comp, "did not create cron job") - cron.retrieve - assert_events([], comp, "cron job got rewritten") - end - - # Test that comments are correctly retained - def test_retain_comments - str = "# this is a comment\n#and another comment\n" - user = "fakeuser" - @crontype.retrieve(@me) - assert_nothing_raised { - @crontype.parse(@me, str) - } - - assert_nothing_raised { - newstr = @crontype.tab(@me) - assert(newstr.include?(str), "Comments were lost") - } - end - - # Test that a specified cron job will be matched against an existing job - # with no name, as long as all fields match - def test_matchcron - str = "0,30 * * * * date\n" - - assert_nothing_raised { - cron = @crontype.create( - :name => "yaycron", - :minute => [0, 30], - :command => "date", - :user => @me - ) - } - - assert_nothing_raised { - @crontype.parse(@me, str) - } - - count = @crontype.inject(0) do |c, obj| - c + 1 - end - - assert_equal(1, count, "Did not match cron job") - - modstr = "# Puppet Name: yaycron\n%s" % str - - assert_nothing_raised { - newstr = @crontype.tab(@me) - assert(newstr.include?(modstr), - "Cron was not correctly matched") - } - end - - # Test adding a cron when there is currently no file. - def test_mkcronwithnotab - tab = @fakefiletype.new(@me) - tab.remove - - @crontype.retrieve(@me) - cron = mkcron("testwithnotab") - cyclecron(cron) - end - - def test_mkcronwithtab - @crontype.retrieve(@me) - obj = Puppet::Type::Cron.cronobj(@me) - obj.write( -"1 1 1 1 * date > %s/crontesting\n" % tstdir() - ) - - cron = mkcron("testwithtab") - cyclecron(cron) - end - - def test_makeandretrievecron - tab = @fakefiletype.new(@me) - tab.remove - - %w{storeandretrieve a-name another-name more_naming SomeName}.each do |name| - cron = mkcron(name) - comp = newcomp(name, cron) - trans = assert_events([:cron_created], comp, name) - - cron = nil - - Puppet.type(:cron).retrieve(@me) - - assert(cron = Puppet.type(:cron)[name], "Could not retrieve named cron") - assert_instance_of(Puppet.type(:cron), cron) - end - end - - # Do input validation testing on all of the parameters. - def test_arguments - values = { - :monthday => { - :valid => [ 1, 13, "1" ], - :invalid => [ -1, 0, 32 ] - }, - :weekday => { - :valid => [ 0, 3, 6, "1", "tue", "wed", - "Wed", "MOnday", "SaTurday" ], - :invalid => [ -1, 7, "13", "tues", "teusday", "thurs" ] - }, - :hour => { - :valid => [ 0, 21, 23 ], - :invalid => [ -1, 24 ] - }, - :minute => { - :valid => [ 0, 34, 59 ], - :invalid => [ -1, 60 ] - }, - :month => { - :valid => [ 1, 11, 12, "mar", "March", "apr", "October", "DeCeMbEr" ], - :invalid => [ -1, 0, 13, "marc", "sept" ] - } - } - - cron = mkcron("valtesting") - values.each { |param, hash| - # We have to test the valid ones first, because otherwise the - # state will fail to create at all. - [:valid, :invalid].each { |type| - hash[type].each { |value| - case type - when :valid: - assert_nothing_raised { - cron[param] = value - } - - if value.is_a?(Integer) - assert_equal(value.to_s, cron.should(param), - "Cron value was not set correctly") - end - when :invalid: - assert_raise(Puppet::Error, "%s is incorrectly a valid %s" % - [value, param]) { - cron[param] = value - } - end - - if value.is_a?(Integer) - value = value.to_s - redo - end - } - } - } - end - - # Test that we can read and write cron tabs - def test_crontab - Puppet.type(:cron).filetype = Puppet.type(:cron).defaulttype - type = nil - unless type = Puppet.type(:cron).filetype - $stderr.puts "No crontab type; skipping test" - end - - obj = nil - assert_nothing_raised { - obj = type.new(Process.uid) - } - - txt = nil - assert_nothing_raised { - txt = obj.read - } - - assert_nothing_raised { - obj.write(txt) - } - end - - # Verify that comma-separated numbers are not resulting in rewrites - def test_norewrite - cron = nil - assert_nothing_raised { - cron = Puppet.type(:cron).create( - :command => "/bin/date > /dev/null", - :minute => [0, 30], - :name => "crontest" - ) - } - - assert_events([:cron_created], cron) - cron.retrieve - assert_events([], cron) - end - - def test_fieldremoval - cron = nil - assert_nothing_raised { - cron = Puppet.type(:cron).create( - :command => "/bin/date > /dev/null", - :minute => [0, 30], - :name => "crontest" - ) - } - - assert_events([:cron_created], cron) - - cron[:minute] = :absent - assert_events([:cron_changed], cron) - assert_nothing_raised { - cron.retrieve - } - assert_equal(:absent, cron.is(:minute)) - end - - def test_listing - @crontype.filetype = @oldfiletype - - crons = [] - assert_nothing_raised { - Puppet::Type.type(:cron).list.each do |cron| - crons << cron - end - } - - crons.each do |cron| - assert(cron, "Did not receive a real cron object") - assert_instance_of(String, cron[:user], - "Cron user is not a string") - end - end - - def verify_failonnouser - assert_raise(Puppet::Error) do - @crontype.retrieve("nosuchuser") - end - end - - def test_names - cron = mkcron("nametest") - - ["bad name", "bad.name"].each do |name| - assert_raise(ArgumentError) do - cron[:name] = name - end - end - - ["good-name", "good-name", "AGoodName"].each do |name| - assert_nothing_raised do - cron[:name] = name - end - end - end - - # Make sure we don't puke on env settings - def test_envsettings - cron = mkcron("envtst") - - assert_apply(cron) - - obj = Puppet::Type::Cron.cronobj(@me) - - assert(obj) - - text = obj.read - - text = "SHELL = /path/to/some/thing\n" + text - - obj.write(text) - - assert_nothing_raised { - cron.retrieve - } - - cron[:command] = "/some/other/command" - - assert_apply(cron) - - assert(obj.read =~ /SHELL/, "lost env setting") - - env1 = "TEST = /bin/true" - env2 = "YAY = fooness" - assert_nothing_raised { - cron[:environment] = [env1, env2] - } - - assert_apply(cron) - cron.retrieve - - vals = cron.is(:environment) - assert(vals, "Did not get environment settings") - assert(vals != :absent, "Env is incorrectly absent") - assert_instance_of(Array, vals) - - assert(vals.include?(env1), "Missing first env setting") - assert(vals.include?(env2), "Missing second env setting") - - end - - def test_divisionnumbers - cron = mkcron("divtest") - cron[:minute] = "*/5" - - assert_apply(cron) - - cron.retrieve - - assert_equal(["*/5"], cron.is(:minute)) - end - - def test_ranges - cron = mkcron("rangetest") - cron[:minute] = "2-4" - - assert_apply(cron) - - cron.retrieve - - assert_equal(["2-4"], cron.is(:minute)) - end - - def test_data - @fakefiletype = Puppet::FileType.filetype(:ram) - @crontype.filetype = @fakefiletype - - @crontype.retrieve(@me) - obj = Puppet::Type::Cron.cronobj(@me) - - fakedata("data/types/cron").each do |file| - names = [] - text = File.read(file) - obj.write(File.read(file)) - - @crontype.retrieve(@me) - - @crontype.each do |cron| - names << cron.name - end - - name = File.basename(file) - cron = mkcron("filetest-#{name}") - - assert_apply(cron) - - @crontype.retrieve(@me) - - names.each do |name| - assert(@crontype[name], "Could not retrieve %s" % name) - end - - tablines = @crontype.tab(@me).split("\n") - - text.split("\n").each do |line| - assert(tablines.include?(line), - "Did not get %s back out" % line.inspect) - end - end - end - - def test_value - cron = mkcron("valuetesting") - - # First, test the normal states - [:minute, :hour, :month].each do |param| - state = cron.state(param) - - assert(state, "Did not get %s state" % param) - - assert_nothing_raised { - state.should = :absent - state.is = :absent - } - - # Make sure our minute default is 0, not * - val = if param == :minute - "*" # the "0" thing is disabled for now - else - "*" - end - assert_equal(val, cron.value(param)) - - # Make sure we correctly get the "is" value if that's all there is - cron.is = [param, "1"] - assert_equal("1", cron.value(param)) - - # Make sure arrays work, too - cron.is = [param, ["1"]] - assert_equal("1", cron.value(param)) - - # Make sure values get comma-joined - cron.is = [param, ["2", "3"]] - assert_equal("2,3", cron.value(param)) - - # Make sure "should" values work, too - cron[param] = "4" - assert_equal("4", cron.value(param)) - - cron[param] = ["4"] - assert_equal("4", cron.value(param)) - - cron[param] = ["4", "5"] - assert_equal("4,5", cron.value(param)) - - cron.is = [param, :absent] - assert_equal("4,5", cron.value(param)) - end - - # Now make sure that :command works correctly - state = cron.state(:command) - - assert_nothing_raised { - state.should = :absent - state.is = :absent - } - - assert(state, "Did not get command state") - assert_raise(Puppet::DevError) do - cron.value(:command) - end - - param = :command - # Make sure we correctly get the "is" value if that's all there is - cron.is = [param, "1"] - assert_equal("1", cron.value(param)) - - # Make sure arrays work, too - cron.is = [param, ["1"]] - assert_equal("1", cron.value(param)) - - # Make sure values are not comma-joined - cron.is = [param, ["2", "3"]] - assert_equal("2", cron.value(param)) - - # Make sure "should" values work, too - cron[param] = "4" - assert_equal("4", cron.value(param)) - - cron[param] = ["4"] - assert_equal("4", cron.value(param)) - - cron[param] = ["4", "5"] - assert_equal("4", cron.value(param)) - - cron.is = [param, :absent] - assert_equal("4", cron.value(param)) - end - - # Make sure we can successfully list all cron jobs on all users - def test_cron_listing - crons = [] - %w{fake1 fake2 fake3 fake4 fake5}.each do |user| - crons << @crontype.create( - :name => "#{user}-1", - :command => "/usr/bin/#{user}", - :minute => "0", - :user => user, - :hour => user.sub("fake",'') - ) - - crons << @crontype.create( - :name => "#{user}-2", - :command => "/usr/sbin/#{user}", - :minute => "0", - :user => user, - :weekday => user.sub("fake",'') - ) - - assert_apply(*crons) - end - - list = @crontype.list.collect { |c| c.name } - - crons.each do |cron| - assert(list.include?(cron.name), "Did not match cron %s" % name) - end - end - - # Make sure we can create a cron in an empty tab - def test_mkcron_if_empty - @crontype.filetype = @oldfiletype - - @crontype.retrieve(@me) - - # Backup our tab - text = @crontype.tabobj(@me).read - - cleanup do - if text == "" - @crontype.tabobj(@me).remove - else - @crontype.tabobj(@me).write(text) - end - end - - # Now get rid of it - @crontype.tabobj(@me).remove - @crontype.clear - - cron = mkcron("emptycron") - - assert_apply(cron) - - # Clear the type, but don't clear the filetype - @crontype.clear - - # Get the stuff again - @crontype.retrieve(@me) - - assert(@crontype["emptycron"], - "Did not retrieve cron") - end -end - -# $Id$ diff --git a/test/providers/parsedhost.rb b/test/providers/parsedhost.rb deleted file mode 100644 index bb122129a..000000000 --- a/test/providers/parsedhost.rb +++ /dev/null @@ -1,242 +0,0 @@ -if __FILE__ == $0 - $:.unshift '..' - $:.unshift '../../lib' - $puppetbase = "../.." -end - -require 'etc' -require 'puppet/type' -require 'puppettest' -require 'test/unit' - -class TestParsedHostProvider < Test::Unit::TestCase - include TestPuppet - - def setup - super - @provider = Puppet.type(:host).provider(:parsed) - - @oldfiletype = @provider.filetype - end - - def teardown - Puppet::FileType.filetype(:ram).clear - @provider.filetype = @oldfiletype - super - end - - def test_provider_existence - assert(@provider, "Could not retrieve provider") - 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.filetype = Puppet::FileType.filetype(:ram) - end - - def mkhosthash - if defined? @hcount - @hcount += 1 - else - @hcount = 1 - end - - return { - :name => "fakehost%s" % @hcount, - :ip => "192.168.27.%s" % @hcount, - :alias => ["alias%s" % @hcount] - } - end - - def mkhost - hash = mkhosthash() - - fakemodel = fakemodel(:host, hash[:name]) - - host = @provider.new(fakemodel) - - hash.each do |name, val| - fakemodel[name] = val - end - assert(host, "Could not create provider host") - - return host - end - - # Make sure we convert both directlys correctly using a simple host. - def test_basic_isomorphism - hash = {:name => "myhost", :ip => "192.168.43.56", :alias => %w{another host}} - - str = nil - assert_nothing_raised do - str = @provider.to_record(hash) - end - - assert_equal("192.168.43.56\tmyhost\tanother\thost", str) - - newhash = nil - assert_nothing_raised do - newhash = @provider.parse(str).shift - end - - assert_equal(hash, newhash) - end - - # Make sure parsing gets comments, blanks, and hosts - def test_blanks_and_comments - mkfaketype() - text = %{# comment one - -192.168.43.56\tmyhost\tanother\thost - -# another comment -192.168.43.57\tanotherhost -} - - instances = nil - assert_nothing_raised do - instances = @provider.parse(text) - end - - assert_equal([ - "# comment one", - "", - {:name => "myhost", :ip => "192.168.43.56", :alias => %w{another host}}, - " ", - "# another comment", - {:name => "anotherhost", :ip => "192.168.43.57"} - ], instances) - - assert_nothing_raised do - @provider.store(instances) - end - newtext = nil - assert_nothing_raised do - newtext = @provider.fileobj.read - end - - assert_equal(text, newtext) - end - - def test_empty_and_absent_hashes_are_not_written - mkfaketype() - - instances = [ - {:name => "myhost", :ip => "192.168.43.56", :alias => %w{another host}}, - {}, - {:ensure => :absent, :name => "anotherhost", :ip => "192.168.43.57"} - ] - - assert_nothing_raised do - newtext = @provider.store(instances) - end - newtext = nil - assert_nothing_raised do - newtext = @provider.fileobj.read - end - text = "192.168.43.56\tmyhost\tanother\thost\n" - - assert_equal(text, newtext) - end - - def test_simplehost - mkfaketype - # Start out with no content. - assert_nothing_raised { - assert_equal([], @provider.retrieve) - } - - # Now create a provider - host = nil - assert_nothing_raised { - host = mkhost - } - - # Make sure we're still empty - assert_nothing_raised { - assert_equal([], @provider.retrieve) - } - - hash = host.model.to_hash - - # Try storing it - assert_nothing_raised do - host.store(hash) - end - - # Make sure we get the host back - assert_nothing_raised { - assert_equal([hash], @provider.retrieve) - } - - # Remove a single field and make sure it gets tossed - hash.delete(:alias) - - assert_nothing_raised { - host.store(hash) - assert_equal([hash], @provider.retrieve) - } - - # Make sure it throws up if we remove a required field - hash.delete(:ip) - - assert_raise(ArgumentError) { - host.store(hash) - } - - # Now remove the whole object - assert_nothing_raised { - host.store({}) - assert_equal([], @provider.retrieve) - } - end - - # Parse our sample data and make sure we regenerate it correctly. - def test_hostsparse - fakedata("data/types/hosts").each do |file| fakedataparse(file) end - end - - # Make sure we can modify the file elsewhere and those modifications will - # get taken into account. - def test_modifyingfile - hostfile = tempfile() - @provider.path = hostfile - - hosts = [] - 3.times { - h = mkhost() - hosts << h - } - - hosts.each do |host| - host.store - end - - newhost = mkhost() - hosts << newhost - - # Now store our new host - newhost.store() - - # Verify we can retrieve that info - assert_nothing_raised("Could not retrieve after second write") { - newhost.hash - } - - text = @provider.fileobj.read - - instances = @provider.retrieve - - # And verify that we have data for everything - hosts.each { |host| - name = host.model[:name] - assert(text.include?(name), "Host %s is not in file" % name) - hash = host.hash - assert(! hash.empty?, "Could not find host %s" % name) - assert(hash[:ip], "Could not find ip for host %s" % name) - } - end -end - -# $Id$ diff --git a/test/providers/parsedmount.rb b/test/providers/parsedmount.rb deleted file mode 100755 index a1630f7be..000000000 --- a/test/providers/parsedmount.rb +++ /dev/null @@ -1,220 +0,0 @@ -if __FILE__ == $0 - $:.unshift '..' - $:.unshift '../../lib' - $puppetbase = "../.." -end - -require 'puppettest' -require 'puppet' -require 'test/unit' -require 'facter' - -class TestParsedMounts < Test::Unit::TestCase - include TestPuppet - - def setup - super - @provider = Puppet.type(:mount).provider(:parsed) - - @oldfiletype = @provider.filetype - end - - def teardown - Puppet::FileType.filetype(:ram).clear - @provider.filetype = @oldfiletype - super - end - - def mkmountargs - mount = nil - - if defined? @pcount - @pcount += 1 - else - @pcount = 1 - end - args = { - :path => "/fspuppet%s" % @pcount, - :device => "/dev/dsk%s" % @pcount, - } - - @provider.fields.each do |field| - unless args.include? field - args[field] = "fake%s" % @pcount - end - end - - return args - end - - def mkmount - hash = mkmountargs() - #hash[:provider] = @provider.name - - fakemodel = fakemodel(:mount, hash[:name]) - - mount = @provider.new(fakemodel) - #mount = Puppet.type(:mount).create(hash) - - hash.each do |name, val| - fakemodel[name] = val - end - assert(mount, "Could not create provider mount") - - 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.filetype = Puppet::FileType.filetype(:ram) - end - - def test_simplemount - mkfaketype - assert_nothing_raised { - assert_equal([], @provider.retrieve) - } - - # Now create a provider - mount = nil - assert_nothing_raised { - mount = mkmount - } - - # Make sure we're still empty - assert_nothing_raised { - assert_equal([], @provider.retrieve) - } - - hash = mount.model.to_hash - - if hash.has_key? :name - hash.delete :name - end - - # Try storing it - assert_nothing_raised do - mount.store(hash) - end - - # Make sure we get the mount back - assert_nothing_raised { - assert_equal([hash], @provider.retrieve) - } - - # Now remove the whole object - assert_nothing_raised { - mount.store({}) - assert_equal([], @provider.retrieve) - } - end - - unless Facter["operatingsystem"].value == "Darwin" - def test_mountsparse - fakedataparse(fake_fstab) do - # Now just make we've got some mounts we know will be there - hashes = @provider.retrieve.find_all { |i| i.is_a? Hash } - assert(hashes.length > 0, "Did not create any hashes") - root = hashes.find { |i| i[:path] == "/" } - assert(root, "Could not retrieve root mount") - end - end - - def test_rootfs - fs = nil - @provider.path = fake_fstab() - fakemodel = fakemodel(:mount, "/") - mount = @provider.new(fakemodel) - mount.model[:path] = "/" - assert(mount.hash, "Could not retrieve root fs") - - assert_nothing_raised { - assert(mount.mounted?, "Root is considered not mounted") - } - end - end - - if Process.uid == 0 - def test_mountfs - fs = nil - case Facter["hostname"].value - when "culain": fs = "/ubuntu" - when "atalanta": fs = "/mnt" - when "figurehead": fs = "/cg4/net/depts" - else - $stderr.puts "No mount for mount testing; skipping" - return - end - - oldtext = @provider.fileobj.read - - ftype = @provider.filetype - - # Make sure the original gets reinstalled. - if ftype == Puppet::FileType.filetype(:netinfo) - cleanup do - IO.popen("niload -r /mounts .", "w") do |file| - file.puts oldtext - end - end - else - cleanup do - @provider.fileobj.write(oldtext) - end - end - - fakemodel = fakemodel(:mount, "/") - obj = @provider.new(fakemodel) - obj.model[:path] = fs - - current = nil - - assert_nothing_raised { - current = obj.mounted? - } - - if current - # Make sure the original gets reinstalled. - cleanup do - unless obj.mounted? - obj.mount - end - end - end - - unless current - assert_nothing_raised { - obj.mount - } - end - - assert_nothing_raised { - obj.unmount - } - assert(! obj.mounted?, "FS still mounted") - assert_nothing_raised { - obj.mount - } - assert(obj.mounted?, "FS not mounted") - - end - end - - 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.path - cleanup do @provider.path = oldpath end - return fakefile(File::join("data/types/mount", name)) - end -end - -# $Id$ diff --git a/test/providers/parsedport.rb b/test/providers/parsedport.rb deleted file mode 100755 index 51901f7f4..000000000 --- a/test/providers/parsedport.rb +++ /dev/null @@ -1,61 +0,0 @@ -# Test host job creation, modification, and destruction - -if __FILE__ == $0 - $:.unshift '..' - $:.unshift '../../lib' - $puppetbase = "../.." -end - -require 'puppettest' -require 'puppet' -require 'puppet/type/parsedtype/port' -require 'test/unit' -require 'facter' - -class TestParsedPort < Test::Unit::TestCase - include TestPuppet - - def setup - super - @provider = Puppet.type(:port).provider(:parsed) - - @oldfiletype = @provider.filetype - end - - def teardown - Puppet::FileType.filetype(:ram).clear - @provider.filetype = @oldfiletype - super - end - - # Parse our sample data and make sure we regenerate it correctly. - def test_portsparse - fakedata("data/types/ports").each do |file| - @provider.path = file - instances = nil - assert_nothing_raised { - instances = @provider.retrieve - } - - text = @provider.fileobj.read.gsub(/\s+/, ' ') - text.gsub!(/ #.+$/, '') - - dest = tempfile() - @provider.path = dest - - # Now write it back out - assert_nothing_raised { - @provider.store(instances) - } - - newtext = @provider.fileobj.read.gsub(/\s+/, ' ') - - newtext.gsub!(/ #.+$/, '') - - # Don't worry about difference in whitespace - assert_equal(text.gsub(/\s+/, ' '), newtext.gsub(/\s+/, ' ')) - end - end -end - -# $Id$ diff --git a/test/providers/parsedsshkey.rb b/test/providers/parsedsshkey.rb deleted file mode 100755 index 7f21a58b4..000000000 --- a/test/providers/parsedsshkey.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Test key job creation, modification, and destruction - -if __FILE__ == $0 - $:.unshift '..' - $:.unshift '../../lib' - $puppetbase = "../.." -end - -require 'puppettest' -require 'puppet' -require 'puppet/type/parsedtype/sshkey' -require 'test/unit' -require 'facter' - -class TestParsedSSHKey < Test::Unit::TestCase - include TestPuppet - - def setup - super - @provider = Puppet.type(:sshkey).provider(:parsed) - - @oldfiletype = @provider.filetype - end - - def teardown - Puppet::FileType.filetype(:ram).clear - @provider.filetype = @oldfiletype - super - end - - def test_keysparse - fakedata("data/types/sshkey").each { |file| - fakedataparse(file) - } - end -end - -# $Id$ diff --git a/test/puppettest.rb b/test/puppettest.rb index 2506f9fc3..f094045cd 100644 --- a/test/puppettest.rb +++ b/test/puppettest.rb @@ -35,10 +35,6 @@ module TestPuppet def name self[:name] end - - def to_hash - self - end end class FakeProvider @@ -80,33 +76,6 @@ module TestPuppet def initialize(model) @model = model end - - # Called in Type#remove - def remove(var = true) - end - end - - class FakeParsedProvider < FakeProvider - def hash - ret = {} - instance_variables.each do |v| - v = v.sub("@", '') - if val = self.send(v) - ret[v.intern] = val - end - end - - return ret - end - - def store(hash) - hash.each do |n, v| - method = n.to_s + "=" - if respond_to? method - send(method, v) - end - end - end end @@fakemodels = {} @@ -467,32 +436,6 @@ module TestPuppet return file end - # Run an isomorphism test on our parsing process. - def fakedataparse(file) - @provider.path = file - instances = nil - assert_nothing_raised { - instances = @provider.retrieve - } - - text = @provider.fileobj.read - - yield if block_given? - - dest = tempfile() - @provider.path = dest - - # Now write it back out - assert_nothing_raised { - @provider.store(instances) - } - - newtext = @provider.fileobj.read - - # Don't worry about difference in whitespace - assert_equal(text.gsub(/\s+/, ' '), newtext.gsub(/\s+/, ' ')) - end - # wrap how to retrieve the masked mode def filemode(file) File.stat(file).mode & 007777 diff --git a/test/types/cron.rb b/test/types/cron.rb index 94e490e40..caada9f91 100755 --- a/test/types/cron.rb +++ b/test/types/cron.rb @@ -97,7 +97,7 @@ class TestCron < Test::Unit::TestCase cron[:ensure] = :absent - assert_events([:cron_deleted], comp) + assert_events([:cron_removed], comp) cron.retrieve diff --git a/test/types/file.rb b/test/types/file.rb index 194d00958..3dc296cc0 100644 --- a/test/types/file.rb +++ b/test/types/file.rb @@ -704,6 +704,8 @@ class TestFile < Test::Unit::TestCase assert_events([:file_created], obj) + obj.retrieve + assert(obj.insync?, "Object is not in sync") text = File.read(file) @@ -793,17 +795,18 @@ class TestFile < Test::Unit::TestCase dest = tempfile() file = nil - str = "some content, ok?" assert_nothing_raised { file = Puppet.type(:file).create( :name => dest, :ensure => "file", - :content => str + :content => "this is some content, yo" ) } + file.retrieve + assert_events([:file_created], file) - assert_equal(str, File.read(dest)) + file.retrieve assert_events([], file) assert_events([], file) end @@ -1197,7 +1200,7 @@ class TestFile < Test::Unit::TestCase } # First run through without :force - assert_events([], file, "Link replaced directory without force enabled") + assert_events([], file) assert(FileTest.directory?(link), "Link replaced dir without force") @@ -1373,12 +1376,11 @@ class TestFile < Test::Unit::TestCase lfobj = Puppet::Type.newfile(:path => localfile, :content => "rahtest") - assert(! lfobj.implicit?, "object incorrectly implicit") - destobj = Puppet::Type.newfile(:path => destdir, :source => sourcedir, :recurse => true) + assert_apply(lfobj, destobj) assert(FileTest.exists?(dsourcefile), "File did not get copied") diff --git a/test/types/group.rb b/test/types/group.rb index 2b47f14d4..508b8436c 100755 --- a/test/types/group.rb +++ b/test/types/group.rb @@ -18,9 +18,6 @@ class TestGroup < Test::Unit::TestCase def create @ensure = :present - - # Just set a fake gid - self.gid = 10 end def delete @@ -79,7 +76,7 @@ class TestGroup < Test::Unit::TestCase assert_events([:group_created], comp) assert_equal(:present, group.provider.ensure, "Group is absent") group[:ensure] = :absent - trans = assert_events([:group_deleted], comp) + trans = assert_events([:group_removed], comp) assert_equal(:absent, group.provider.ensure, "Group is present") assert_rollback_events(trans, [:group_created], "group") @@ -89,6 +86,7 @@ class TestGroup < Test::Unit::TestCase # This is a bit odd, since we're not actually doing anything on the machine. # Just make sure we can set the gid and that it will work correctly. def attrtest_gid(group) + # Check the validation. assert_nothing_raised { group[:gid] = "15" @@ -98,8 +96,7 @@ class TestGroup < Test::Unit::TestCase "Did not convert gid to number") comp = newcomp(group) - - trans = assert_events([:group_changed], comp, "group") + trans = assert_events([:group_modified], comp, "group") assert_equal(15, group.provider.gid, "GID was not changed") assert_nothing_raised { @@ -110,12 +107,12 @@ class TestGroup < Test::Unit::TestCase "Did not keep gid as number") # Now switch to 16 - trans = assert_events([:group_changed], comp, "group") + trans = assert_events([:group_modified], comp, "group") assert_equal(16, group.provider.gid, "GID was not changed") # And then rollback - assert_rollback_events(trans, [:group_changed], "group") - assert_equal(15, group.provider.gid, "GID was not reverted") + assert_rollback_events(trans, [:group_modified], "group") + assert_equal(15, group.provider.gid, "GID was not changed") end def test_owngroups @@ -170,7 +167,7 @@ class TestGroup < Test::Unit::TestCase end } - assert_rollback_events(trans, [:group_deleted], "group") + assert_rollback_events(trans, [:group_removed], "group") assert(! gobj.provider.exists?, "Did not delete group") diff --git a/test/types/host.rb b/test/types/host.rb index 54596aa7f..8af9e3e4a 100755 --- a/test/types/host.rb +++ b/test/types/host.rb @@ -13,25 +13,24 @@ require 'facter' class TestHost < Test::Unit::TestCase include TestPuppet - def setup super + # god i'm lazy @hosttype = Puppet.type(:host) + @oldhosttype = @hosttype.filetype + end - @provider = @hosttype.defaultprovider - - # Make sure they aren't using something funky like netinfo - unless @provider.name == :parsed - @hosttype.defaultprovider = @hosttype.provider(:parsed) - end - - cleanup do @hosttype.defaultprovider = nil end + def teardown + Puppet::FileType.filetype(:ram).clear + @hosttype.filetype = @oldhosttype + Puppet.type(:file).clear + super + end - oldpath = @provider.path - cleanup do - @provider.path = oldpath - end - @provider.path = tempfile() + # Here we just create a fake host type that answers to all of the methods + # but does not modify our actual system. + def mkfaketype + @hosttype.filetype = Puppet::FileType.filetype(:ram) end def mkhost @@ -53,16 +52,10 @@ class TestHost < Test::Unit::TestCase end def test_simplehost + mkfaketype host = nil assert_nothing_raised { - Puppet.type(:host).defaultprovider.retrieve - - count = 0 - @hosttype.each do |h| - count += 1 - end - - assert_equal(0, count, "Found hosts in empty file somehow") + assert_nil(Puppet.type(:host).retrieve) } assert_nothing_raised { @@ -72,14 +65,49 @@ class TestHost < Test::Unit::TestCase ) } - assert_apply(host) + assert_nothing_raised { + Puppet.type(:host).store + } + + assert_nothing_raised { + assert( + Puppet.type(:host).to_file.include?( + Puppet.type(:host).fileobj.read + ), + "File does not include all of our objects" + ) + } + end + + def test_hostsparse + fakedata("data/types/hosts").each { |file| + @hosttype.path = file + Puppet.info "Parsing %s" % file + assert_nothing_raised { + Puppet.type(:host).retrieve + } + + text = Puppet.type(:host).fileobj.read - assert_nothing_raised { host.retrieve } + dest = tempfile() + @hosttype.path = dest - assert_equal(:present, host.is(:ensure)) + # Now write it back out + assert_nothing_raised { + Puppet.type(:host).store + } + + newtext = Puppet.type(:host).fileobj.read + + # Don't worry about difference in whitespace + assert_equal(text.gsub(/\s+/, ' '), newtext.gsub(/\s+/, ' ')) + + @hosttype.clear + } end def test_moddinghost + mkfaketype host = mkhost() assert_events([:host_created], host) @@ -117,33 +145,34 @@ class TestHost < Test::Unit::TestCase end def test_removal + mkfaketype host = mkhost() assert_nothing_raised { host[:ensure] = :present } assert_events([:host_created], host) - assert(host.exists?, "Host is not considered in sync") - - assert_equal(:present, host.is(:ensure)) - + host.retrieve + assert(host.insync?) assert_nothing_raised { host[:ensure] = :absent } - assert_events([:host_deleted], host) - - text = host.provider.class.fileobj.read - assert(! text.include?(host[:name]), "Host is still in text") + assert_events([:host_removed], host) host.retrieve assert_events([], host) end def test_modifyingfile + hostfile = tempfile() + Puppet.type(:host).path = hostfile + hosts = [] names = [] 3.times { h = mkhost() + #h[:ensure] = :present + #h.retrieve hosts << h names << h.name } @@ -151,18 +180,20 @@ class TestHost < Test::Unit::TestCase hosts.clear Puppet.type(:host).clear newhost = mkhost() + #newhost[:ensure] = :present names << newhost.name assert_apply(newhost) + Puppet.type(:host).clear # Verify we can retrieve that info assert_nothing_raised("Could not retrieve after second write") { newhost.retrieve } - text = newhost.provider.class.fileobj.read - # And verify that we have data for everything names.each { |name| - assert(text.include?(name), "Host is not in file") + host = Puppet.type(:host)[name] + assert(host) + assert(host[:ip]) } end end diff --git a/test/types/mount.rb b/test/types/mount.rb index c7bd378a9..70170289e 100755 --- a/test/types/mount.rb +++ b/test/types/mount.rb @@ -8,63 +8,40 @@ end require 'puppettest' require 'puppet' +require 'puppet/type/parsedtype/mount' require 'test/unit' +require 'facter' class TestMounts < Test::Unit::TestCase include TestPuppet - - p = Puppet::Type.type(:mount).provide :fake, :parent => TestPuppet::FakeParsedProvider do - @name = :fake - apimethods :ensure - - attr_accessor :mounted - - def create - @ensure = :present - end - - def delete - @ensure = :absent - @mounted = false - end - - def exists? - if defined? @ensure and @ensure == :present - true - else - false - end - end - - def mounted? - self.mounted - end - - def mount - self.mounted = true - end - - def unmount - self.mounted = false - end - end - - FakeMountProvider = p - - @@fakeproviders[:mount] = p - def setup super - @realprovider = Puppet::Type.type(:mount).defaultprovider - Puppet::Type.type(:mount).defaultprovider = FakeMountProvider + @mounttype = Puppet.type(:mount) + @oldfiletype = @mounttype.filetype end def teardown - Puppet.type(:mount).clear - Puppet::Type.type(:mount).defaultprovider = nil + @mounttype.filetype = @oldfiletype + Puppet.type(:file).clear super 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 + pfile = tempfile() + old = @mounttype.filetype + @mounttype.filetype = Puppet::FileType.filetype(:ram) + + cleanup do + @mounttype.filetype = old + @mounttype.fileobj = nil + end + + # Reset this, just in case + @mounttype.fileobj = nil + end + def mkmount mount = nil @@ -78,7 +55,7 @@ class TestMounts < Test::Unit::TestCase :device => "/dev/dsk%s" % @pcount, } - @realprovider.fields.each do |field| + Puppet.type(:mount).fields.each do |field| unless args.include? field args[field] = "fake%s" % @pcount end @@ -92,47 +69,178 @@ class TestMounts < Test::Unit::TestCase end def test_simplemount - mount = nil - oldprv = Puppet.type(:mount).defaultprovider - Puppet.type(:mount).defaultprovider = nil + mkfaketype + host = nil assert_nothing_raised { - Puppet.type(:mount).defaultprovider.retrieve + assert_nil(Puppet.type(:mount).retrieve) + } - count = 0 - Puppet.type(:mount).each do |h| - count += 1 - end + mount = mkmount - assert_equal(0, count, "Found mounts in empty file somehow") + assert_nothing_raised { + Puppet.type(:mount).store } - Puppet.type(:mount).defaultprovider = oldprv - mount = mkmount + assert_nothing_raised { + assert( + Puppet.type(:mount).to_file.include?( + Puppet.type(:mount).fileobj.read + ), + "File does not include all of our objects" + ) + } + end + + unless Facter["operatingsystem"].value == "Darwin" + def test_mountsparse + use_fake_fstab + assert_nothing_raised { + @mounttype.retrieve + } + + # Now just make we've got some mounts we know will be there + root = @mounttype["/"] + assert(root, "Could not retrieve root mount") + end + + def test_rootfs + fs = nil + use_fake_fstab + assert_nothing_raised { + Puppet.type(:mount).retrieve + } + + assert_nothing_raised { + fs = Puppet.type(:mount)["/"] + } + assert(fs, "Could not retrieve root fs") + + assert_nothing_raised { + assert(fs.mounted?, "Root is considered not mounted") + } + end + end + + # Make sure it reads and writes correctly. + def test_readwrite + use_fake_fstab + assert_nothing_raised { + Puppet::Type.type(:mount).retrieve + } + + oldtype = Puppet::Type.type(:mount).filetype + + # Now switch to storing in ram + mkfaketype + + fs = mkmount - assert_apply(mount) + assert(Puppet::Type.type(:mount).filetype != oldtype) - assert_nothing_raised { mount.retrieve } + assert_events([:mount_created], fs) - assert_equal(:mounted, mount.is(:ensure)) + text = Puppet::Type.type(:mount).fileobj.read + + assert(text =~ /#{fs[:path]}/, "Text did not include new fs") + + fs[:ensure] = :absent + + assert_events([:mount_removed], fs) + text = Puppet::Type.type(:mount).fileobj.read + + assert(text !~ /#{fs[:path]}/, "Text still includes new fs") + + fs[:ensure] = :present + + assert_events([:mount_created], fs) + + text = Puppet::Type.type(:mount).fileobj.read + + assert(text =~ /#{fs[:path]}/, "Text did not include new fs") + + fs[:options] = "rw,noauto" + + assert_events([:mount_changed], fs) end - # Make sure fs mounting behaves appropriately. This is more a test of - # whether things get mounted and unmounted based on the value of 'ensure'. + if Process.uid == 0 def test_mountfs - obj = mkmount + fs = nil + case Facter["hostname"].value + when "culain": fs = "/ubuntu" + when "atalanta": fs = "/mnt" + when "figurehead": fs = "/cg4/net/depts" + else + $stderr.puts "No mount for mount testing; skipping" + return + end + + assert_nothing_raised { + Puppet.type(:mount).retrieve + } + + oldtext = Puppet::Type.type(:mount).fileobj.read + + ftype = Puppet::Type.type(:mount).filetype + + # Make sure the original gets reinstalled. + if ftype == Puppet::FileType.filetype(:netinfo) + cleanup do + IO.popen("niload -r /mounts .", "w") do |file| + file.puts oldtext + end + end + else + cleanup do + Puppet::Type.type(:mount).fileobj.write(oldtext) + end + end + + obj = Puppet.type(:mount)[fs] - assert_apply(obj) + assert(obj, "Could not retrieve %s object" % fs) + + current = nil + + assert_nothing_raised { + current = obj.mounted? + } + + if current + # Make sure the original gets reinstalled. + cleanup do + unless obj.mounted? + obj.mount + end + end + end + + unless current + assert_nothing_raised { + obj.mount + } + end + + # Now copy all of the states' "is" values to the "should" values + obj.each do |state| + state.should = state.is + end # Verify we can remove the mount assert_nothing_raised { obj[:ensure] = :absent } - assert_events([:mount_deleted], obj) + assert_events([:mount_removed], obj) assert_events([], obj) # And verify it's gone - assert(!obj.provider.mounted?, "Object is mounted after being removed") + assert(!obj.mounted?, "Object is mounted after being removed") + + text = Puppet.type(:mount).fileobj.read + + assert(text !~ /#{fs}/, + "Fstab still contains %s" % fs) assert_nothing_raised { obj[:ensure] = :present @@ -141,7 +249,10 @@ class TestMounts < Test::Unit::TestCase assert_events([:mount_created], obj) assert_events([], obj) - assert(! obj.provider.mounted?, "Object is mounted incorrectly") + text = Puppet::Type.type(:mount).fileobj.read + assert(text =~ /#{fs}/, "Fstab does not contain %s" % fs) + + assert(! obj.mounted?, "Object is mounted incorrectly") assert_nothing_raised { obj[:ensure] = :mounted @@ -150,8 +261,33 @@ class TestMounts < Test::Unit::TestCase assert_events([:mount_mounted], obj) assert_events([], obj) + text = Puppet::Type.type(:mount).fileobj.read + assert(text =~ /#{fs}/, + "Fstab does not contain %s" % fs) + obj.retrieve - assert(obj.provider.mounted?, "Object is not mounted") + assert(obj.mounted?, "Object is not mounted") + + unless current + assert_nothing_raised { + obj.unmount + } + end + end + end + + def use_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 + fstab = fakefile(File::join("data/types/mount", name)) + Puppet::Type.type(:mount).path = fstab end end diff --git a/test/types/package.rb b/test/types/package.rb index 9ffd846ef..779c693fe 100644 --- a/test/types/package.rb +++ b/test/types/package.rb @@ -167,7 +167,7 @@ class TestPackages < Test::Unit::TestCase obj[:source] = file assert_raise(Puppet::PackageError, "Successfully installed nonexistent package") { - state.sync(state.should) + state.sync } end diff --git a/test/types/port.rb b/test/types/port.rb index f02825431..debd7ed89 100755 --- a/test/types/port.rb +++ b/test/types/port.rb @@ -8,30 +8,29 @@ end require 'puppettest' require 'puppet' +require 'puppet/type/parsedtype/port' require 'test/unit' require 'facter' class TestPort < Test::Unit::TestCase include TestPuppet - def setup super @porttype = Puppet.type(:port) + @oldfiletype = @porttype.filetype + end - @provider = @porttype.defaultprovider - - # Make sure they aren't using something funky like netinfo - unless @provider.name == :parsed - @porttype.defaultprovider = @porttype.provider(:parsed) - end - - cleanup do @porttype.defaultprovider = nil end + def teardown + @porttype.filetype = @oldfiletype + Puppet.type(:file).clear + super + end - oldpath = @provider.path - cleanup do - @provider.path = oldpath - end - @provider.path = tempfile() + # Here we just create a fake host type that answers to all of the methods + # but does not modify our actual system. + def mkfaketype + pfile = tempfile() + @porttype.path = pfile end def mkport @@ -56,29 +55,51 @@ class TestPort < Test::Unit::TestCase end def test_simpleport + mkfaketype host = nil assert_nothing_raised { - Puppet.type(:port).defaultprovider.retrieve - - count = 0 - @porttype.each do |h| - count += 1 - end - - assert_equal(0, count, "Found hosts in empty file somehow") + assert_nil(Puppet.type(:port).retrieve) } port = mkport - assert_apply(port) assert_nothing_raised { - port.retrieve + Puppet.type(:port).store + } + + assert_nothing_raised { + assert( + Puppet.type(:port).to_file.include?( + Puppet.type(:port).fileobj.read + ), + "File does not include all of our objects" + ) } + end - assert(port.exists?, "Port did not get created") + def test_portsparse + fakedata("data/types/ports").each { |file| + @porttype.path = file + Puppet.info "Parsing %s" % file + assert_nothing_raised { + @porttype.retrieve + } + + # Now just make we've got some ports we know will be there + dns = @porttype["domain"] + assert(dns, "Could not retrieve DNS port") + + assert_equal("53", dns.is(:number), "DNS number was wrong") + %w{udp tcp}.each { |v| + assert(dns.is(:protocols).include?(v), "DNS did not include proto %s" % v) + } + + @porttype.clear + } end def test_moddingport + mkfaketype port = nil port = mkport @@ -102,24 +123,59 @@ class TestPort < Test::Unit::TestCase end def test_removal + mkfaketype port = mkport() assert_nothing_raised { port[:ensure] = :present } assert_events([:port_created], port) - assert_events([], port) - assert(port.exists?, "port was not created") + port.retrieve + assert(port.insync?) assert_nothing_raised { port[:ensure] = :absent } - assert_events([:port_deleted], port) - assert(! port.exists?, "port was not removed") + assert_events([:port_removed], port) + port.retrieve assert_events([], port) end + def test_modifyingfile + mkfaketype() + + ports = [] + names = [] + 3.times { + k = mkport() + ports << k + names << k.name + } + assert_apply(*ports) + ports.clear + Puppet.type(:port).clear + newport = mkport() + #newport[:ensure] = :present + names << newport.name + assert_apply(newport) + Puppet.type(:port).clear + # Verify we can retrieve that info + assert_nothing_raised("Could not retrieve after second write") { + newport.retrieve + } + + # And verify that we have data for everything + names.each { |name| + port = Puppet.type(:port)[name] + assert(port) + port.retrieve + assert(port[:number], "port %s has no number" % name) + } + end + def test_addingstates + mkfaketype + port = mkport() assert_events([:port_created], port) @@ -133,7 +189,7 @@ class TestPort < Test::Unit::TestCase assert_equal(:present, port.is(:ensure)) - assert_equal(:absent, port.retrieve[:alias]) + assert(port.state(:alias).is == :absent) port[:alias] = "yaytest" assert_events([:port_changed], port) diff --git a/test/types/sshkey.rb b/test/types/sshkey.rb index cf8313654..ff9adc336 100755 --- a/test/types/sshkey.rb +++ b/test/types/sshkey.rb @@ -17,22 +17,21 @@ class TestSSHKey < Test::Unit::TestCase def setup super # god i'm lazy - @sshkeytype = Puppet.type(:sshkey) - - @provider = @sshkeytype.defaultprovider - - # Make sure they aren't using something funky like netinfo - unless @provider.name == :parsed - @sshkeytype.defaultprovider = @sshkeytype.provider(:parsed) - end + @sshtype = Puppet.type(:sshkey) + @oldfiletype = @sshtype.filetype + end - cleanup do @sshkeytype.defaultprovider = nil end + def teardown + Puppet::FileType.filetype(:ram).clear + @sshtype.filetype = @oldfiletype + Puppet.type(:file).clear + super + end - oldpath = @provider.path - cleanup do - @provider.path = oldpath - end - @provider.path = tempfile() + # Here we just create a fake key type that answers to all of the methods + # but does not modify our actual system. + def mkfaketype + @sshtype.filetype = Puppet::FileType.filetype(:ram) end def mkkey @@ -45,7 +44,7 @@ class TestSSHKey < Test::Unit::TestCase end assert_nothing_raised { - key = @sshkeytype.create( + key = @sshtype.create( :name => "host%s.madstop.com" % @kcount, :key => "%sAAAAB3NzaC1kc3MAAACBAMnhSiku76y3EGkNCDsUlvpO8tRgS9wL4Eh54WZfQ2lkxqfd2uT/RTT9igJYDtm/+UHuBRdNGpJYW1Nw2i2JUQgQEEuitx4QKALJrBotejGOAWxxVk6xsh9xA0OW8Q3ZfuX2DDitfeC8ZTCl4xodUMD8feLtP+zEf8hxaNamLlt/AAAAFQDYJyf3vMCWRLjTWnlxLtOyj/bFpwAAAIEAmRxxXb4jjbbui9GYlZAHK00689DZuX0EabHNTl2yGO5KKxGC6Esm7AtjBd+onfu4Rduxut3jdI8GyQCIW8WypwpJofCIyDbTUY4ql0AQUr3JpyVytpnMijlEyr41FfIb4tnDqnRWEsh2H7N7peW+8DWZHDFnYopYZJ9Yu4/jHRYAAACAERG50e6aRRb43biDr7Ab9NUCgM9bC0SQscI/xdlFjac0B/kSWJYTGVARWBDWug705hTnlitY9cLC5Ey/t/OYOjylTavTEfd/bh/8FkAYO+pWdW3hx6p97TBffK0b6nrc6OORT2uKySbbKOn0681nNQh4a6ueR3JRppNkRPnTk5c=" % @kcount, :type => "ssh-dss", @@ -57,25 +56,41 @@ class TestSSHKey < Test::Unit::TestCase end def test_simplekey + mkfaketype assert_nothing_raised { - Puppet.type(:sshkey).defaultprovider.retrieve - - count = 0 - @sshkeytype.each do |h| - count += 1 - end - - assert_equal(0, count, "Found sshkeys in empty file somehow") + assert_nil(Puppet.type(:sshkey).retrieve) } key = mkkey assert_apply(key) - assert(key.exists?, "Key did not get created") + assert_nothing_raised { + Puppet.type(:sshkey).store + } + + assert_nothing_raised { + assert( + Puppet.type(:sshkey).to_file.include?( + Puppet.type(:sshkey).fileobj.read + ), + "File does not include all of our objects" + ) + } + end + + def test_keysparse + fakedata("data/types/sshkey").each { |file| + @sshtype.path = file + assert_nothing_raised { + @sshtype.retrieve + } + @sshtype.clear + } end def test_moddingkey + mkfaketype key = mkkey() assert_events([:sshkey_created], key) @@ -84,11 +99,12 @@ class TestSSHKey < Test::Unit::TestCase key[:alias] = %w{madstop kirby yayness} + Puppet.err :mark assert_events([:sshkey_changed], key) end def test_aliasisstate - assert_equal(:state, @sshkeytype.attrtype(:alias)) + assert_equal(:state, @sshtype.attrtype(:alias)) end def test_multivalues @@ -110,23 +126,28 @@ class TestSSHKey < Test::Unit::TestCase end def test_removal + mkfaketype sshkey = mkkey() assert_nothing_raised { sshkey[:ensure] = :present } assert_events([:sshkey_created], sshkey) - assert(sshkey.exists?, "key was not created") + sshkey.retrieve + assert(sshkey.insync?) assert_nothing_raised { sshkey[:ensure] = :absent } - assert_events([:sshkey_deleted], sshkey) - assert(! sshkey.exists?, "Key was not deleted") + assert_events([:sshkey_removed], sshkey) + sshkey.retrieve assert_events([], sshkey) end def test_modifyingfile + keyfile = tempfile() + Puppet.type(:sshkey).path = keyfile + keys = [] names = [] 3.times { @@ -143,7 +164,7 @@ class TestSSHKey < Test::Unit::TestCase #newkey[:ensure] = :present names << newkey.name assert_apply(newkey) - + Puppet.type(:sshkey).clear # Verify we can retrieve that info assert_nothing_raised("Could not retrieve after second write") { newkey.retrieve @@ -151,9 +172,9 @@ class TestSSHKey < Test::Unit::TestCase # And verify that we have data for everything names.each { |name| - key = Puppet.type(:sshkey)[name] || Puppet.type(:sshkey).create(:name => name) + key = Puppet.type(:sshkey)[name] assert(key) - assert(key.exists?, "key %s is missing" % name) + assert(key[:type]) } end end diff --git a/test/types/user.rb b/test/types/user.rb index 7522a041b..008f39272 100755 --- a/test/types/user.rb +++ b/test/types/user.rb @@ -19,7 +19,7 @@ class TestUser < Test::Unit::TestCase @ensure = :present @model.eachstate do |state| next if state.name == :ensure - state.commit + state.sync end end |