summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2007-03-17 02:50:48 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2007-03-17 02:50:48 +0000
commit86c63ce2d9e93786cb27f9056b90f6887cbc8826 (patch)
tree2457fcfa3098bc1f6da35a6dff7ce1fcc2f52c90
parentba23a5ac276e59fdda8186750c6d0fd2cfecdeac (diff)
downloadpuppet-86c63ce2d9e93786cb27f9056b90f6887cbc8826.tar.gz
puppet-86c63ce2d9e93786cb27f9056b90f6887cbc8826.tar.xz
puppet-86c63ce2d9e93786cb27f9056b90f6887cbc8826.zip
Fixing cron support (I hope). It now uses providers, and seems to work, at least on my os x box.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2284 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r--CHANGELOG9
-rw-r--r--lib/puppet/metatype/attributes.rb54
-rwxr-xr-xlib/puppet/provider/cron/crontab.rb136
-rwxr-xr-xlib/puppet/provider/parsedfile.rb19
-rwxr-xr-xlib/puppet/provider/service/smf.rb1
-rw-r--r--lib/puppet/type.rb2
-rwxr-xr-xlib/puppet/type/cron.rb669
-rw-r--r--lib/puppet/type/property.rb12
-rw-r--r--lib/puppet/util.rb4
-rw-r--r--lib/puppet/util/fileparsing.rb67
-rw-r--r--test/data/providers/cron/crontab.allthree17
-rw-r--r--test/data/providers/cron/crontab.envNcomment12
-rw-r--r--test/data/providers/cron/crontab.envNname11
-rw-r--r--test/data/providers/cron/crontab.multirecords12
-rw-r--r--test/data/providers/cron/examples/freebsd (renamed from test/data/types/cron/freebsd)0
-rw-r--r--test/data/providers/cron/examples/one (renamed from test/data/types/cron/one)0
-rwxr-xr-xtest/ral/manager/attributes.rb45
-rwxr-xr-xtest/ral/manager/provider.rb52
-rwxr-xr-xtest/ral/providers/cron/crontab.rb364
-rwxr-xr-xtest/ral/types/cron.rb474
-rwxr-xr-xtest/util/fileparsing.rb5
21 files changed, 1055 insertions, 910 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 037cd7499..8a7ac586b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,13 @@
0.22.2 (grover)
+ Refactored cron support entirely. Cron now uses providers, and there
+ is a single 'crontab' provider that handles user crontabs. While this
+ refactor does not include providers for /etc/crontab or cron.d, it should
+ now be straightforward to write those providers.
+
+ Changed the parameter sorting so that the provider parameter comes
+ right after name, so the provider is available when the other parameters
+ and properties are being created.
+
Redid some of the internals of the ParsedFile provider base class.
It now passes a FileRecord around instead of a hash.
diff --git a/lib/puppet/metatype/attributes.rb b/lib/puppet/metatype/attributes.rb
index ad964b7ed..a96533964 100644
--- a/lib/puppet/metatype/attributes.rb
+++ b/lib/puppet/metatype/attributes.rb
@@ -22,8 +22,11 @@ class Puppet::Type
namevar = self.namevar
order = [namevar]
+ if self.parameters.include?(:provider)
+ order << :provider
+ end
order << [self.properties.collect { |property| property.name },
- self.parameters,
+ self.parameters - [:provider],
self.metaparams].flatten.reject { |param|
# we don't want our namevar in there multiple times
param == namevar
@@ -34,6 +37,19 @@ class Puppet::Type
return order
end
+ # Retrieve an attribute alias, if there is one.
+ def self.attr_alias(param)
+ @attr_aliases[symbolize(param)]
+ end
+
+ # Create an alias to an existing attribute. This will cause the aliased
+ # attribute to be valid when setting and retrieving values on the instance.
+ def self.set_attr_alias(hash)
+ hash.each do |new, old|
+ @attr_aliases[symbolize(new)] = symbolize(old)
+ end
+ end
+
# Find the class associated with any given attribute.
def self.attrclass(name)
@attrclasses ||= {}
@@ -409,6 +425,16 @@ class Puppet::Type
return hash
end
+
+ # Return either the attribute alias or the attribute.
+ def attr_alias(name)
+ name = symbolize(name)
+ if synonym = self.class.attr_alias(name)
+ return synonym
+ else
+ return name
+ end
+ end
# Are we deleting this resource?
def deleting?
@@ -420,9 +446,7 @@ class Puppet::Type
# Most classes won't use this.
def is=(ary)
param, value = ary
- if param.is_a?(String)
- param = param.intern
- end
+ param = attr_alias(param)
if self.class.validproperty?(param)
unless prop = @parameters[param]
prop = self.newattr(param)
@@ -439,18 +463,16 @@ class Puppet::Type
# value, but you can also specifically return 'is' and 'should'
# values using 'object.is(:property)' or 'object.should(:property)'.
def [](name)
- if name.is_a?(String)
- name = name.intern
+ name = attr_alias(name)
+
+ unless self.class.validattr?(name)
+ raise TypeError.new("Invalid parameter %s(%s)" % [name, name.inspect])
end
if name == :name
name = self.class.namevar
end
- unless self.class.validattr?(name)
- raise TypeError.new("Invalid parameter %s(%s)" % [name, name.inspect])
- end
-
if obj = @parameters[name]
if obj.is_a?(Puppet::Type::Property)
return obj.is
@@ -466,10 +488,11 @@ class Puppet::Type
# access to always be symbols, not strings. This sets the 'should'
# value on properties, and otherwise just sets the appropriate parameter.
def []=(name,value)
+ name = attr_alias(name)
+
unless self.class.validattr?(name)
raise TypeError.new("Invalid parameter %s" % [name])
end
- name = symbolize(name)
if name == :name
name = self.class.namevar
@@ -516,7 +539,8 @@ class Puppet::Type
# retrieve the 'is' value for a specified property
def is(name)
- if prop = @parameters[symbolize(name)] and prop.is_a?(Puppet::Type::Property)
+ name = attr_alias(name)
+ if prop = @parameters[name] and prop.is_a?(Puppet::Type::Property)
return prop.is
else
return nil
@@ -525,7 +549,8 @@ class Puppet::Type
# retrieve the 'should' value for a specified property
def should(name)
- if prop = @parameters[symbolize(name)] and prop.is_a?(Puppet::Type::Property)
+ name = attr_alias(name)
+ if prop = @parameters[name] and prop.is_a?(Puppet::Type::Property)
return prop.should
else
return nil
@@ -634,7 +659,8 @@ class Puppet::Type
# Return a specific value for an attribute.
def value(name)
- name = symbolize(name)
+ name = attr_alias(name)
+
if obj = @parameters[name] and obj.respond_to?(:value)
return obj.value
else
diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb
index c50bc2680..b7caa4a21 100755
--- a/lib/puppet/provider/cron/crontab.rb
+++ b/lib/puppet/provider/cron/crontab.rb
@@ -6,49 +6,130 @@ tab = case Facter.value(:operatingsystem)
:crontab
end
+
Puppet::Type.type(:cron).provide(:crontab,
:parent => Puppet::Provider::ParsedFile,
+ :default_target => ENV["USER"],
:filetype => tab
) do
commands :crontab => "crontab"
- attr_accessor :target
-
text_line :comment, :match => %r{^#}, :post_parse => proc { |hash|
if hash[:line] =~ /Puppet Name: (.+)\s*$/
hash[:name] = $1
end
}
- text_line :blank, :match => /^\s+/
+ text_line :blank, :match => %r{^\s+}
+
+ text_line :environment, :match => %r{^\w+=}
- text_line :environment, :match => %r{/^\w+=/}
+ record_line :freebsd_special, :fields => %w{special command},
+ :match => %r{^@(\w+)\s+(.+)$}, :pre_gen => proc { |hash|
+ hash[:special] = "@" + hash[:special]
+ }
- record_line :crontab, :fields => %w{minute hour weekday month monthday command},
+ crontab = record_line :crontab, :fields => %w{minute hour monthday month weekday command},
:optional => %w{minute hour weekday month monthday}, :absent => "*"
- #record_line :freebsd_special, :fields => %w{special command},
- # :match => %r{^@(\w+)\s+(.+)}
- # Apparently Freebsd will "helpfully" add a new TZ line to every
- # single cron line, but not in all cases (e.g., it doesn't do it
- # on my machine. This is my attempt to fix it so the TZ lines don't
- # multiply.
- #if tab =~ /^TZ=.+$/
- # return tab.sub(/\n/, "\n" + self.header)
- #else
- # return self.header() + tab
- #end
+ class << crontab
+ def numeric_fields
+ fields - [:command]
+ end
+ # Do some post-processing of the parsed record. Basically just
+ # split the numeric fields on ','.
+ def post_parse(details)
+ numeric_fields.each do |field|
+ if val = details[field] and val != :absent
+ details[field] = details[field].split(",")
+ end
+ end
+ end
+
+ # Join the fields back up based on ','.
+ def pre_gen(details)
+ numeric_fields.each do |field|
+ if vals = details[field] and vals.is_a?(Array)
+ details[field] = vals.join(",")
+ end
+ end
+ end
+
+
+ # Add name and environments as necessary.
+ def to_line(details)
+ str = ""
+ if details[:name]
+ str = "# Puppet Name: %s\n" % details[:name]
+ end
+ if details[:environment] and details[:environment] != :absent
+ details[:environment].each do |env|
+ str += env + "\n"
+ end
+ end
+
+ str += join(details)
+ str
+ end
+ 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} by puppet. While it
-# HEADER can still be managed manually, it is definitely not recommended.
+%{# HEADER This file was autogenerated at #{Time.now} by puppet.
+# HEADER While it can still be managed manually, it is definitely notrecommended.
# HEADER Note particularly that the comments starting with 'Puppet Name' should
# HEADER not be deleted, as doing so could cause duplicate cron jobs.\n}
end
+ # See if we can match the hash against an existing cron job.
+ def self.match(hash)
+ model.find_all { |obj|
+ obj.value(:user) == hash[:user] and obj.value(:command) == hash[:command]
+ }.each do |obj|
+ # we now have a cron job whose command exactly matches
+ # let's see if the other fields match
+
+ # First check the @special stuff
+ if hash[:special]
+ next unless obj.value(:special) == hash[:special]
+ end
+
+ # Then the normal fields.
+ matched = true
+ record_type(hash[:record_type]).fields().each do |field|
+ next if field == :command
+ if hash[field] and ! obj.value(field)
+ Puppet.info "Cron is missing %s: %s and %s" %
+ [field, hash[field].inspect, obj.value(field).inspect]
+ matched = false
+ break
+ end
+
+ if ! hash[field] and obj.value(field)
+ Puppet.info "Hash is missing %s: %s and %s" %
+ [field, obj.value(field).inspect, hash[field].inspect]
+ matched = false
+ break
+ end
+
+ # FIXME It'd be great if I could somehow reuse how the
+ # fields are turned into text, but....
+ next if (hash[field] == :absent and obj.value(field) == "*")
+ next if (hash[field].join(",") == obj.value(field))
+ Puppet.info "Did not match %s: %s vs %s" %
+ [field, obj.value(field).inspect, hash[field].inspect]
+ matched = false
+ break
+ end
+ next unless matched
+ return obj
+ end
+
+ return false
+ end
+
# Collapse name and env records.
def self.prefetch_hook(records)
name = nil
@@ -78,12 +159,27 @@ Puppet::Type.type(:cron).provide(:crontab,
}.reject { |record| record[:skip] }
end
+ def self.to_file(records)
+ text = super
+ # Apparently Freebsd will "helpfully" add a new TZ line to every
+ # single cron line, but not in all cases (e.g., it doesn't do it
+ # on my machine). This is my attempt to fix it so the TZ lines don't
+ # multiply.
+ if text =~ /(^TZ=.+\n)/
+ tz = $1
+ text.sub!(tz, '')
+ text = tz + text
+ end
+ return text
+ end
+
def user=(user)
- self.target = target
+ @property_hash[:user] = user
+ @property_hash[:target] = user
end
def user
- self.target
+ @property_hash[:user] || @property_hash[:target]
end
end
diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb
index 56474e273..bb6546095 100755
--- a/lib/puppet/provider/parsedfile.rb
+++ b/lib/puppet/provider/parsedfile.rb
@@ -80,9 +80,10 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
# Flush all of the records relating to a specific target.
def self.flush_target(target)
- target_object(target).write(to_file(target_records(target).reject { |r|
+ records = target_records(target).reject { |r|
r[:ensure] == :absent
- }))
+ }
+ target_object(target).write(to_file(records))
end
# Return the header placed at the top of each generated file, warning
@@ -101,7 +102,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
@target = nil
# Default to flat files
- @filetype = Puppet::Util::FileType.filetype(:flat)
+ @filetype ||= Puppet::Util::FileType.filetype(:flat)
super
end
@@ -191,7 +192,7 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
end
if respond_to?(:prefetch_hook)
- prefetch_hook(target_records)
+ target_records = prefetch_hook(target_records)
end
@records += target_records
@@ -202,8 +203,8 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
if instance = self.model[record[:name]]
next unless instance.provider.is_a?(self)
instance.provider.property_hash = record
- elsif self.respond_to?(:match)
- if instance = self.match(record)
+ elsif respond_to?(:match)
+ if instance = match(record)
record[:name] = instance[:name]
instance.provider.property_hash = record
end
@@ -269,6 +270,12 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
targets.uniq.reject { |t| t.nil? }
end
+ def self.to_file(records)
+ text = super
+ header + text
+ end
+
+
def create
@model.class.validproperties.each do |property|
if value = @model.should(property)
diff --git a/lib/puppet/provider/service/smf.rb b/lib/puppet/provider/service/smf.rb
index 08f2bd731..e2e5c989a 100755
--- a/lib/puppet/provider/service/smf.rb
+++ b/lib/puppet/provider/service/smf.rb
@@ -54,6 +54,7 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
value = $2
else
Puppet.err "Could not match %s" % line.inspect
+ next
end
case var
when "state":
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 023f85ab3..bf2060018 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -73,6 +73,8 @@ class Type < Puppet::Element
@parameters = []
@paramhash = {}
+ @attr_aliases = {}
+
@paramdoc = Hash.new { |hash,key|
if key.is_a?(String)
key = key.intern
diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb
index d71654586..dd9ed3ab5 100755
--- a/lib/puppet/type/cron.rb
+++ b/lib/puppet/type/cron.rb
@@ -4,453 +4,378 @@ require 'puppet/type/property'
require 'puppet/util/filetype'
require 'puppet/type/parsedtype'
-module Puppet
- # Model the actual cron jobs. Supports all of the normal cron job fields
- # as parameters, with the 'command' as the single property. Also requires a
- # completely symbolic 'name' paremeter, which gets written to the file
- # and is used to manage the job.
- 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
- # from parsedtype, and we should probably subclass Cron from there,
- # but it was just too annoying to do.
- class CronParam < Puppet::Property
- class << self
- attr_accessor :boundaries, :default
- end
-
- # We have to override the parent method, because we consume the entire
- # "should" array
- def insync?
- if defined? @should and @should
- self.is_to_s == self.should_to_s
- else
- true
- end
- end
+Puppet::Type.newtype(:cron) do
+ @doc = "Installs and manages cron jobs. All fields except the command
+ and the user are optional, although specifying no periodic
+ fields would result in the command being executed every
+ minute. While the name of the cron job is not part of the actual
+ job, it is used by Puppet to store and retrieve it.
+
+ If you specify a cron job that matches an existing job in every way
+ except name, then the jobs will be considered equivalent and the
+ new name will be permanently associated with that job. Once this
+ association is made and synced to disk, you can then manage the job
+ normally (e.g., change the schedule of the job).
+
+ Example:
+
+ cron { logrotate:
+ command => \"/usr/sbin/logrotate\",
+ user => root,
+ hour => 2,
+ minute => 0
+ }
+ "
+ ensurable
+
+ # A base class for all of the Cron parameters, since they all have
+ # similar argument checking going on. We're stealing the base class
+ # from parsedtype, and we should probably subclass Cron from there,
+ # but it was just too annoying to do.
+ class CronParam < Puppet::Property
+ class << self
+ attr_accessor :boundaries, :default
+ end
- # A method used to do parameter input handling. Converts integers
- # in string form to actual integers, and returns the value if it's
- # an integer or false if it's just a normal string.
- def numfix(num)
- if num =~ /^\d+$/
- return num.to_i
- elsif num.is_a?(Integer)
- return num
- else
- return false
- end
+ # We have to override the parent method, because we consume the entire
+ # "should" array
+ def insync?
+ if defined? @should and @should
+ self.is_to_s == self.should_to_s
+ else
+ true
end
+ end
- # Verify that a number is within the specified limits. Return the
- # number if it is, or false if it is not.
- def limitcheck(num, lower, upper)
- if num >= lower and num <= upper
- return num
- else
- return false
- end
+ # A method used to do parameter input handling. Converts integers
+ # in string form to actual integers, and returns the value if it's
+ # an integer or false if it's just a normal string.
+ def numfix(num)
+ if num =~ /^\d+$/
+ return num.to_i
+ elsif num.is_a?(Integer)
+ return num
+ else
+ return false
end
+ end
- # Verify that a value falls within the specified array. Does case
- # insensitive matching, and supports matching either the entire word
- # or the first three letters of the word.
- def alphacheck(value, ary)
- tmp = value.downcase
-
- # If they specified a shortened version of the name, then see
- # if we can lengthen it (e.g., mon => monday).
- if tmp.length == 3
- ary.each_with_index { |name, index|
- if name =~ /#{tmp}/i
- return index
- end
- }
- else
- if ary.include?(tmp)
- return ary.index(tmp)
- end
- end
-
+ # Verify that a number is within the specified limits. Return the
+ # number if it is, or false if it is not.
+ def limitcheck(num, lower, upper)
+ if num >= lower and num <= upper
+ return num
+ else
return false
end
+ end
- def should_to_s
- if @should
- if self.name == :command or @should[0].is_a? Symbol
- @should[0]
- else
- @should.join(",")
+ # Verify that a value falls within the specified array. Does case
+ # insensitive matching, and supports matching either the entire word
+ # or the first three letters of the word.
+ def alphacheck(value, ary)
+ tmp = value.downcase
+
+ # If they specified a shortened version of the name, then see
+ # if we can lengthen it (e.g., mon => monday).
+ if tmp.length == 3
+ ary.each_with_index { |name, index|
+ if name =~ /#{tmp}/i
+ return index
end
- else
- nil
+ }
+ else
+ if ary.include?(tmp)
+ return ary.index(tmp)
end
end
- def is_to_s
- if @is
- unless @is.is_a?(Array)
- return @is
- end
+ return false
+ end
- if self.name == :command or @is[0].is_a? Symbol
- @is[0]
- else
- @is.join(",")
- end
+ def should_to_s
+ if @should
+ if self.name == :command or @should[0].is_a? Symbol
+ @should[0]
else
- nil
+ @should.join(",")
end
+ else
+ nil
end
+ end
- # The method that does all of the actual parameter value
- # checking; called by all of the +param<name>=+ methods.
- # Requires the value, type, and bounds, and optionally supports
- # a boolean of whether to do alpha checking, and if so requires
- # the ary against which to do the checking.
- munge do |value|
- # Support 'absent' as a value, so that they can remove
- # a value
- if value == "absent" or value == :absent
- return :absent
- end
-
- # Allow the */2 syntax
- if value =~ /^\*\/[0-9]+$/
- return value
- end
-
- # Allow ranges
- if value =~ /^[0-9]+-[0-9]+$/
- return value
- end
-
- if value == "*"
- return value
- end
-
- return value unless self.class.boundaries
- lower, upper = self.class.boundaries
- retval = nil
- if num = numfix(value)
- retval = limitcheck(num, lower, upper)
- elsif respond_to?(:alpha)
- # If it has an alpha method defined, then we check
- # to see if our value is in that list and if so we turn
- # it into a number
- retval = alphacheck(value, alpha())
+ def is_to_s
+ if @is
+ unless @is.is_a?(Array)
+ return @is
end
- if retval
- return retval.to_s
+ if self.name == :command or @is[0].is_a? Symbol
+ @is[0]
else
- self.fail "%s is not a valid %s" %
- [value, self.class.name]
+ @is.join(",")
end
+ else
+ nil
end
end
- # Somewhat uniquely, this property does not actually change anything -- it
- # just calls +@parent.sync+, which writes out the whole cron tab for
- # the user in question. There is no real way to change individual cron
- # jobs without rewriting the entire cron file.
- #
- # Note that this means that managing many cron jobs for a given user
- # could currently result in multiple write sessions for that user.
- newproperty(:command, :parent => CronParam) do
- desc "The command to execute in the cron job. The environment
- provided to the command varies by local system rules, and it is
- best to always provide a fully qualified command. The user's
- profile is not sourced when the command is run, so if the
- user's environment is desired it should be sourced manually.
-
- All cron parameters support ``absent`` as a value; this will
- remove any existing values for that field."
-
- def should
- if @should
- if @should.is_a? Array
- @should[0]
- else
- devfail "command is not an array"
- end
- else
- nil
- end
- end
+ def should
+ @should
end
- newproperty(:special) do
- desc "Special schedules only supported on FreeBSD."
-
- def specials
- %w{reboot yearly annually monthly weekly daily midnight hourly}
+ # The method that does all of the actual parameter value
+ # checking; called by all of the +param<name>=+ methods.
+ # Requires the value, type, and bounds, and optionally supports
+ # a boolean of whether to do alpha checking, and if so requires
+ # the ary against which to do the checking.
+ munge do |value|
+ # Support 'absent' as a value, so that they can remove
+ # a value
+ if value == "absent" or value == :absent
+ return :absent
end
- validate do |value|
- unless specials().include?(value)
- raise ArgumentError, "Invalid special schedule %s" %
- value.inspect
- end
+ # Allow the */2 syntax
+ if value =~ /^\*\/[0-9]+$/
+ return value
end
- end
- newproperty(:minute, :parent => CronParam) do
- self.boundaries = [0, 59]
- desc "The minute at which to run the cron job.
- Optional; if specified, must be between 0 and 59, inclusive."
- end
-
- newproperty(:hour, :parent => CronParam) do
- self.boundaries = [0, 23]
- desc "The hour at which to run the cron job. Optional;
- if specified, must be between 0 and 23, inclusive."
- end
+ # Allow ranges
+ if value =~ /^[0-9]+-[0-9]+$/
+ return value
+ end
- newproperty(:weekday, :parent => CronParam) do
- def alpha
- %w{sunday monday tuesday wednesday thursday friday saturday}
+ if value == "*"
+ return value
end
- self.boundaries = [0, 6]
- desc "The weekday on which to run the command.
- Optional; if specified, must be between 0 and 6, inclusive, with
- 0 being Sunday, or must be the name of the day (e.g., Tuesday)."
- end
- newproperty(:month, :parent => CronParam) do
- def alpha
- %w{january february march april may june july
- august september october november december}
+ return value unless self.class.boundaries
+ lower, upper = self.class.boundaries
+ retval = nil
+ if num = numfix(value)
+ retval = limitcheck(num, lower, upper)
+ elsif respond_to?(:alpha)
+ # If it has an alpha method defined, then we check
+ # to see if our value is in that list and if so we turn
+ # it into a number
+ retval = alphacheck(value, alpha())
end
- self.boundaries = [1, 12]
- desc "The month of the year. Optional; if specified
- must be between 1 and 12 or the month name (e.g., December)."
- end
- newproperty(:monthday, :parent => CronParam) do
- self.boundaries = [1, 31]
- desc "The day of the month on which to run the
- command. Optional; if specified, must be between 1 and 31."
+ if retval
+ return retval.to_s
+ else
+ self.fail "%s is not a valid %s" %
+ [value, self.class.name]
+ end
end
+ end
- newproperty(:environment) do
- desc "Any environment settings associated with this cron job. They
- will be stored between the header and the job in the crontab. There
- can be no guarantees that other, earlier settings will not also
- affect a given cron job.
-
- Also, Puppet cannot automatically determine whether an existing,
- unmanaged environment setting is associated with a given cron
- job. If you already have cron jobs with environment settings,
- then Puppet will keep those settings in the same place in the file,
- but will not associate them with a specific job.
-
- Settings should be specified exactly as they should appear in
- the crontab, e.g., 'PATH=/bin:/usr/bin:/usr/sbin'. Multiple
- settings should be specified as an array."
-
- validate do |value|
- unless value =~ /^\s*(\w+)\s*=\s*(.+)\s*$/
- raise ArgumentError, "Invalid environment setting %s" %
- value.inspect
- end
- end
+ # Somewhat uniquely, this property does not actually change anything -- it
+ # just calls +@parent.sync+, which writes out the whole cron tab for
+ # the user in question. There is no real way to change individual cron
+ # jobs without rewriting the entire cron file.
+ #
+ # Note that this means that managing many cron jobs for a given user
+ # could currently result in multiple write sessions for that user.
+ newproperty(:command, :parent => CronParam) do
+ desc "The command to execute in the cron job. The environment
+ provided to the command varies by local system rules, and it is
+ best to always provide a fully qualified command. The user's
+ profile is not sourced when the command is run, so if the
+ user's environment is desired it should be sourced manually.
+
+ All cron parameters support ``absent`` as a value; this will
+ remove any existing values for that field."
- def insync?
- if @is.is_a? Array
- return @is.sort == @should.sort
+ def should
+ if @should
+ if @should.is_a? Array
+ @should[0]
else
- return @is == @should
+ devfail "command is not an array"
end
+ else
+ nil
end
+ end
+ end
- def should
- @should
- end
+ newproperty(:special) do
+ desc "Special schedules only supported on FreeBSD."
+
+ def specials
+ %w{reboot yearly annually monthly weekly daily midnight hourly}
end
- newparam(:name) do
- desc "The symbolic name of the cron job. This name
- is used for human reference only and is generated automatically
- for cron jobs found on the system. This generally won't
- matter, as Puppet will do its best to match existing cron jobs
- against specified jobs (and Puppet adds a comment to cron jobs it
- adds), but it is at least possible that converting from
- unmanaged jobs to managed jobs might require manual
- intervention.
-
- The names can only have alphanumeric characters plus the '-'
- character."
-
- isnamevar
-
- validate do |value|
- unless value =~ /^[-\w]+$/
- raise ArgumentError, "Invalid name format '%s'" % value
- end
+ validate do |value|
+ unless specials().include?(value)
+ raise ArgumentError, "Invalid special schedule %s" %
+ value.inspect
end
end
+ end
- newproperty(:user) do
- desc "The user to run the command as. This user must
- be allowed to run cron jobs, which is not currently checked by
- Puppet.
-
- The user defaults to whomever Puppet is running as."
+ newproperty(:minute, :parent => CronParam) do
+ self.boundaries = [0, 59]
+ desc "The minute at which to run the cron job.
+ Optional; if specified, must be between 0 and 59, inclusive."
+ end
- defaultto { ENV["USER"] }
+ newproperty(:hour, :parent => CronParam) do
+ self.boundaries = [0, 23]
+ desc "The hour at which to run the cron job. Optional;
+ if specified, must be between 0 and 23, inclusive."
+ end
+
+ newproperty(:weekday, :parent => CronParam) do
+ def alpha
+ %w{sunday monday tuesday wednesday thursday friday saturday}
end
+ self.boundaries = [0, 6]
+ desc "The weekday on which to run the command.
+ Optional; if specified, must be between 0 and 6, inclusive, with
+ 0 being Sunday, or must be the name of the day (e.g., Tuesday)."
+ end
- @doc = "Installs and manages cron jobs. All fields except the command
- and the user are optional, although specifying no periodic
- fields would result in the command being executed every
- minute. While the name of the cron job is not part of the actual
- job, it is used by Puppet to store and retrieve it.
-
- If you specify a cron job that matches an existing job in every way
- except name, then the jobs will be considered equivalent and the
- new name will be permanently associated with that job. Once this
- association is made and synced to disk, you can then manage the job
- normally (e.g., change the schedule of the job).
-
- Example:
-
- cron { logrotate:
- command => \"/usr/sbin/logrotate\",
- user => root,
- hour => 2,
- minute => 0
- }
- "
+ newproperty(:month, :parent => CronParam) do
+ def alpha
+ %w{january february march april may june july
+ august september october november december}
+ end
+ self.boundaries = [1, 12]
+ desc "The month of the year. Optional; if specified
+ must be between 1 and 12 or the month name (e.g., December)."
+ end
- attr_accessor :uid
+ newproperty(:monthday, :parent => CronParam) do
+ self.boundaries = [1, 31]
+ desc "The day of the month on which to run the
+ command. Optional; if specified, must be between 1 and 31."
+ end
- # Convert our hash to an object
- def self.hash2obj(hash)
- obj = nil
- namevar = self.namevar
- unless hash.include?(namevar) and hash[namevar]
- Puppet.info "Autogenerating name for %s" % hash[:command]
- hash[:name] = "autocron-%s" % hash.object_id
+ newproperty(:environment) do
+ desc "Any environment settings associated with this cron job. They
+ will be stored between the header and the job in the crontab. There
+ can be no guarantees that other, earlier settings will not also
+ affect a given cron job.
+
+ Also, Puppet cannot automatically determine whether an existing,
+ unmanaged environment setting is associated with a given cron
+ job. If you already have cron jobs with environment settings,
+ then Puppet will keep those settings in the same place in the file,
+ but will not associate them with a specific job.
+
+ Settings should be specified exactly as they should appear in
+ the crontab, e.g., 'PATH=/bin:/usr/bin:/usr/sbin'. Multiple
+ settings should be specified as an array."
+
+ validate do |value|
+ unless value =~ /^\s*(\w+)\s*=\s*(.+)\s*$/
+ raise ArgumentError, "Invalid environment setting %s" %
+ value.inspect
end
+ end
- unless hash.include?(:command)
- raise Puppet::DevError, "No command for %s" % name
- end
- # if the cron already exists with that name...
- if obj = (self[hash[:name]] || match(hash))
- # Mark the cron job as present
- obj.is = [:ensure, :present]
-
- # Mark all of the values appropriately
- hash.each { |param, value|
- if property = obj.property(param)
- property.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
- end
- }
+ def insync?
+ if @is.is_a? Array
+ return @is.sort == @should.sort
else
- # create a new cron job, since no existing one
- # seems to match
- obj = self.create(
- :name => hash[namevar]
- )
+ return @is == @should
+ end
+ end
- obj.is = [:ensure, :present]
+ def should
+ @should
+ end
+ end
- obj.notice "created"
+ newparam(:name) do
+ desc "The symbolic name of the cron job. This name
+ is used for human reference only and is generated automatically
+ for cron jobs found on the system. This generally won't
+ matter, as Puppet will do its best to match existing cron jobs
+ against specified jobs (and Puppet adds a comment to cron jobs it
+ adds), but it is at least possible that converting from
+ unmanaged jobs to managed jobs might require manual
+ intervention.
+
+ The names can only have alphanumeric characters plus the '-'
+ character."
- hash.delete(namevar)
- hash.each { |param, value|
- obj.is = [param, value]
- }
- end
+ isnamevar
- instance(obj)
+ validate do |value|
+ unless value =~ /^[-\w]+$/
+ raise ArgumentError, "Invalid name format '%s'" % value
+ end
end
+ end
- # See if we can match the hash against an existing cron job.
- def self.match(hash)
- self.find_all { |obj|
- obj[:user] == hash[:user] and obj.value(:command) == hash[:command][0]
- }.each do |obj|
- # we now have a cron job whose command exactly matches
- # let's see if the other fields match
-
- # First check the @special stuff
- if hash[:special]
- next unless obj.value(:special) == hash[:special]
- end
+ newproperty(:user) do
+ desc "The user to run the command as. This user must
+ be allowed to run cron jobs, which is not currently checked by
+ Puppet.
+
+ The user defaults to whomever Puppet is running as."
- # Then the normal fields.
- matched = true
- fields().each do |field|
- next if field == :command
- if hash[field] and ! obj.value(field)
- #Puppet.info "Cron is missing %s: %s and %s" %
- # [field, hash[field].inspect, obj.value(field).inspect]
- matched = false
- break
- end
+ defaultto { ENV["USER"] }
+ end
- if ! hash[field] and obj.value(field)
- #Puppet.info "Hash is missing %s: %s and %s" %
- # [field, obj.value(field).inspect, hash[field].inspect]
- matched = false
- break
- end
+ newproperty(:target) do
+ desc "Where the cron job should be stored. For crontab-style
+ entries this is the same as the user and defaults that way.
+ Other providers default accordingly."
- # FIXME It'd be great if I could somehow reuse how the
- # fields are turned into text, but....
- next if (hash[field] == [:absent] and obj.value(field) == "*")
- next if (hash[field].join(",") == obj.value(field))
- #Puppet.info "Did not match %s: %s vs %s" %
- # [field, obj.value(field).inspect, hash[field].inspect]
- matched = false
- break
+ defaultto {
+ if provider.is_a?(@parent.class.provider(:crontab))
+ if val = @parent.should(:user)
+ val
+ else
+ raise ArgumentError,
+ "You must provide a user with crontab entries"
end
- next unless matched
- return obj
+ elsif provider.class.ancestors.include?(Puppet::Provider::ParsedFile)
+ provider.class.default_target
+ else
+ nil
end
+ }
+ end
- return false
- end
+ # We have to reorder things so that :provide is before :target
- def value(name)
- name = symbolize(name)
- ret = nil
- if obj = @parameters[name]
- ret = obj.should_to_s
+ attr_accessor :uid
- if ret.nil?
- ret = obj.is_to_s
- end
+ def value(name)
+ name = symbolize(name)
+ ret = nil
+ if obj = @parameters[name]
+ ret = obj.should_to_s
- if ret == :absent
- ret = nil
- end
+ if ret.nil?
+ ret = obj.is_to_s
end
- unless ret
- case name
- when :command
- devfail "No command, somehow"
- when :special
- # nothing
- else
- #ret = (self.class.validproperty?(name).default || "*").to_s
- ret = "*"
- end
+ if ret == :absent
+ ret = nil
end
+ end
- ret
+ unless ret
+ case name
+ when :command
+ devfail "No command, somehow"
+ when :special
+ # nothing
+ else
+ #ret = (self.class.validproperty?(name).default || "*").to_s
+ ret = "*"
+ end
end
+
+ ret
end
end
diff --git a/lib/puppet/type/property.rb b/lib/puppet/type/property.rb
index b4dcdfae0..2cc6beb03 100644
--- a/lib/puppet/type/property.rb
+++ b/lib/puppet/type/property.rb
@@ -201,11 +201,15 @@ class Property < Puppet::Parameter
return event
end
else
- event = case self.should
- when :present: (@parent.class.name.to_s + "_created").intern
- when :absent: (@parent.class.name.to_s + "_removed").intern
+ if self.class.name == :ensure
+ 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
else
- (@parent.class.name.to_s + "_changed").intern
+ event = (@parent.class.name.to_s + "_changed").intern
end
end
end
diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb
index 3bd7d277c..fa4d0fae4 100644
--- a/lib/puppet/util.rb
+++ b/lib/puppet/util.rb
@@ -324,9 +324,9 @@ module Util
Process.uid = uid unless @@os == "Darwin"
end
if command.is_a?(Array)
- system(*command)
+ Kernel.exec(*command)
else
- system(command)
+ Kernel.exec(command)
end
exit!($?.exitstatus)
rescue => detail
diff --git a/lib/puppet/util/fileparsing.rb b/lib/puppet/util/fileparsing.rb
index 08f1c79d9..ed92c398d 100644
--- a/lib/puppet/util/fileparsing.rb
+++ b/lib/puppet/util/fileparsing.rb
@@ -75,6 +75,25 @@ module Puppet::Util::FileParsing
end
end
+ # Convert a record into a line by joining the fields together appropriately.
+ # This is pulled into a separate method so it can be called by the hooks.
+ def join(details)
+ joinchar = self.joiner
+
+ fields.collect { |field|
+ # If the field is marked absent, use the appropriate replacement
+ if details[field] == :absent or details[field].nil?
+ if self.optional.include?(field)
+ self.absent
+ else
+ raise ArgumentError, "Field %s is required" % field
+ end
+ else
+ details[field].to_s
+ end
+ }.reject { |c| c.nil?}.join(joinchar)
+ end
+
# Customize this so we can do a bit of validation.
def optional=(optional)
@optional = optional.collect do |field|
@@ -91,6 +110,10 @@ module Puppet::Util::FileParsing
def pre_gen=(block)
meta_def(:pre_gen, &block)
end
+
+ def to_line=(block)
+ meta_def(:to_line, &block)
+ end
end
# Clear all existing record definitions. Only used for testing.
@@ -118,28 +141,25 @@ module Puppet::Util::FileParsing
# Try to match a record.
def handle_record_line(line, record)
+ ret = nil
if record.respond_to?(:process)
if ret = record.send(:process, line.dup)
unless ret.is_a?(Hash)
raise Puppet::DevError,
"Process record type %s returned non-hash" % record.name
end
- ret[:record_type] = record.name
- return ret
else
return nil
end
elsif regex = record.match
- raise "Cannot use matches to handle records yet"
# In this case, we try to match the whole line and then use the
# match captures to get our fields.
if match = regex.match(line)
fields = []
- ignore = record.ignore || []
- match.captures.each_with_index do |value, i|
- fields << value unless ignore.include? i
+ ret = {}
+ record.fields.zip(match.captures).each do |f, v|
+ ret[f] = v
end
- nil
else
Puppet.info "Did not match %s" % line
nil
@@ -168,8 +188,13 @@ module Puppet::Util::FileParsing
val = ([ret[last_field]] + line_fields).join(record.joiner)
ret[last_field] = val
end
+ end
+
+ if ret
ret[:record_type] = record.name
return ret
+ else
+ return nil
end
end
@@ -200,13 +225,7 @@ module Puppet::Util::FileParsing
raise Puppet::DevError, "No record types defined; cannot parse lines"
end
- @record_order.each do |name|
- record = record_type(name)
- unless record
- raise Puppet::DevError, "Did not get record type for %s: %s" %
- [name, @record_types.inspect]
- end
-
+ @record_order.each do |record|
# These are basically either text or record lines.
method = "handle_%s_line" % record.type
if respond_to?(method)
@@ -290,20 +309,11 @@ module Puppet::Util::FileParsing
case record.type
when :text: return details[:line]
else
- joinchar = record.joiner
+ if record.respond_to?(:to_line)
+ return record.to_line(details)
+ end
- line = record.fields.collect { |field|
- # If the field is marked absent, use the appropriate replacement
- if details[field] == :absent or details[field].nil?
- if record.optional.include?(field)
- record.absent
- else
- raise ArgumentError, "Field %s is required" % field
- end
- else
- details[field].to_s
- end
- }.reject { |c| c.nil?}.join(joinchar)
+ line = record.join(details)
if regex = record.rts
# If they say true, then use whitespace; else, use their regex.
@@ -340,6 +350,7 @@ module Puppet::Util::FileParsing
end
private
+
# Define a new type of record.
def new_line_type(record)
@record_types ||= {}
@@ -350,7 +361,7 @@ module Puppet::Util::FileParsing
end
@record_types[record.name] = record
- @record_order << record.name
+ @record_order << record
return record
end
diff --git a/test/data/providers/cron/crontab.allthree b/test/data/providers/cron/crontab.allthree
new file mode 100644
index 000000000..dd2a40466
--- /dev/null
+++ b/test/data/providers/cron/crontab.allthree
@@ -0,0 +1,17 @@
+---
+- |
+ # comment 1
+ # Puppet Name: name2
+ env3=val
+ * * * * * command4
+ # comment 5
+
+- - :record_type: :comment
+ :line: "# comment 1"
+ - :command: command4
+ :environment:
+ - env3=val
+ :name: name2
+ :record_type: :crontab
+ - :record_type: :comment
+ :line: "# comment 5"
diff --git a/test/data/providers/cron/crontab.envNcomment b/test/data/providers/cron/crontab.envNcomment
new file mode 100644
index 000000000..63effe076
--- /dev/null
+++ b/test/data/providers/cron/crontab.envNcomment
@@ -0,0 +1,12 @@
+---
+- |
+ # comment 9
+ env10=val
+ * * * * * command11
+
+- - :record_type: :comment
+ :line: "# comment 9"
+ - :record_type: :environment
+ :line: env10=val
+ - :command: command11
+ :record_type: :crontab
diff --git a/test/data/providers/cron/crontab.envNname b/test/data/providers/cron/crontab.envNname
new file mode 100644
index 000000000..cc058248e
--- /dev/null
+++ b/test/data/providers/cron/crontab.envNname
@@ -0,0 +1,11 @@
+---
+- |
+ env6=val
+ # Puppet Name: name7
+ * * * * * command8
+
+- - :record_type: :environment
+ :line: env6=val
+ - :command: command8
+ :record_type: :crontab
+ :name: name7
diff --git a/test/data/providers/cron/crontab.multirecords b/test/data/providers/cron/crontab.multirecords
new file mode 100644
index 000000000..63effe076
--- /dev/null
+++ b/test/data/providers/cron/crontab.multirecords
@@ -0,0 +1,12 @@
+---
+- |
+ # comment 9
+ env10=val
+ * * * * * command11
+
+- - :record_type: :comment
+ :line: "# comment 9"
+ - :record_type: :environment
+ :line: env10=val
+ - :command: command11
+ :record_type: :crontab
diff --git a/test/data/types/cron/freebsd b/test/data/providers/cron/examples/freebsd
index fba1e310b..fba1e310b 100644
--- a/test/data/types/cron/freebsd
+++ b/test/data/providers/cron/examples/freebsd
diff --git a/test/data/types/cron/one b/test/data/providers/cron/examples/one
index 9cddf97e7..9cddf97e7 100644
--- a/test/data/types/cron/one
+++ b/test/data/providers/cron/examples/one
diff --git a/test/ral/manager/attributes.rb b/test/ral/manager/attributes.rb
index 6fd132aef..f27b0513a 100755
--- a/test/ral/manager/attributes.rb
+++ b/test/ral/manager/attributes.rb
@@ -182,6 +182,51 @@ class TestTypeAttributes < Test::Unit::TestCase
end
assert_equal(false, inst[:falsetest], "false default was not set")
end
+
+ def test_alias_parameter
+ type = mktype
+ type.newparam(:name) {}
+ type.newparam(:one) {}
+ type.newproperty(:two) {}
+
+ aliases = {
+ :three => :one,
+ :four => :two
+ }
+ aliases.each do |new, old|
+ assert_nothing_raised("Could not create alias parameter %s" % new) do
+ type.set_attr_alias new => old
+ end
+ end
+
+ aliases.each do |new, old|
+ assert_equal(old, type.attr_alias(new), "did not return alias info for %s" %
+ new)
+ end
+
+ assert_nil(type.attr_alias(:name), "got invalid alias info for name")
+
+ inst = type.create(:name => "my name")
+ assert(inst, "could not create instance")
+
+ aliases.each do |new, old|
+ val = "value %s" % new
+ assert_nothing_raised do
+ inst[new] = val
+ end
+
+ case old
+ when :one: # param
+ assert_equal(val, inst[new],
+ "Incorrect alias value for %s in []" % new)
+ else
+ assert_equal(val, inst.should(new),
+ "Incorrect alias value for %s in should" % new)
+ end
+ assert_equal(val, inst.value(new), "Incorrect alias value for %s" % new)
+ assert_equal(val, inst.value(old), "Incorrect orig value for %s" % old)
+ end
+ end
end
# $Id$
diff --git a/test/ral/manager/provider.rb b/test/ral/manager/provider.rb
new file mode 100755
index 000000000..6075697d6
--- /dev/null
+++ b/test/ral/manager/provider.rb
@@ -0,0 +1,52 @@
+#!/usr/bin/env ruby
+
+$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppettest'
+require 'mocha'
+
+class TestTypeProviders < Test::Unit::TestCase
+ include PuppetTest
+
+ # Make sure default providers behave correctly
+ def test_defaultproviders
+ # Make a fake type
+ type = Puppet::Type.newtype(:defaultprovidertest) do
+ newparam(:name) do end
+ end
+
+ basic = type.provide(:basic) do
+ defaultfor :operatingsystem => :somethingelse,
+ :operatingsystemrelease => :yayness
+ end
+
+ assert_equal(basic, type.defaultprovider)
+ type.defaultprovider = nil
+
+ greater = type.provide(:greater) do
+ defaultfor :operatingsystem => Facter.value("operatingsystem")
+ end
+
+ assert_equal(greater, type.defaultprovider)
+ end
+
+ # Make sure the provider is always the first parameter created.
+ def test_provider_sorting
+ type = Puppet::Type.newtype(:sorttest) do
+ newparam(:name) {}
+ ensurable
+ end
+ cleanup { Puppet::Type.rmtype(:sorttest) }
+
+ should = [:name, :ensure]
+ assert_equal(should, type.allattrs.reject { |p| ! should.include?(p) },
+ "Got wrong order of parameters")
+
+ type.provide(:yay) { }
+ should = [:name, :provider, :ensure]
+ assert_equal(should, type.allattrs.reject { |p| ! should.include?(p) },
+ "Providify did not reorder parameters")
+ end
+end
+
+# $Id$
diff --git a/test/ral/providers/cron/crontab.rb b/test/ral/providers/cron/crontab.rb
index c4f1031cd..6d0d1e8a9 100755
--- a/test/ral/providers/cron/crontab.rb
+++ b/test/ral/providers/cron/crontab.rb
@@ -14,7 +14,9 @@ class TestCronParsedProvider < Test::Unit::TestCase
def setup
super
- @provider = Puppet::Type.type(:cron).provider(:crontab)
+ @type = Puppet::Type.type(:cron)
+ @provider = @type.provider(:crontab)
+ @provider.initvars
@oldfiletype = @provider.filetype
end
@@ -30,7 +32,7 @@ class TestCronParsedProvider < Test::Unit::TestCase
fields = [:month, :weekday, :monthday, :hour, :command, :minute]
{
"* * * * * /bin/echo" => {:command => "/bin/echo"},
- "10 * * * * /bin/echo test" => {:minute => "10",
+ "10 * * * * /bin/echo test" => {:minute => ["10"],
:command => "/bin/echo test"}
}.each do |line, should|
result = nil
@@ -50,113 +52,44 @@ class TestCronParsedProvider < Test::Unit::TestCase
end
end
- def test_prefetch_hook
- count = 0
- env = Proc.new do
- count += 1
- {:record_type => :environment, :line => "env%s=val" % count}
- end
- comment = Proc.new do |name|
- count += 1
- hash = {:record_type => :comment, :line => "comment %s" % count}
- if name
- hash[:name] = name
- end
- hash
- end
- record = Proc.new do
- count += 1
- {:record_type => :crontab, :command => "command%s" % count}
- end
+ def test_parse_and_generate
+ @provider.filetype = :ram
+ crondir = datadir(File.join(%w{providers cron}))
+ files = Dir.glob("%s/crontab.*" % crondir)
- result = nil
- args = []
- # First try it with all three
- args << comm = comment.call(false)
- args << name = comment.call(true)
- args << var = env.call()
- args << line = record.call
- args << sec = comment.call(false)
- assert_nothing_raised do
- result = @provider.prefetch_hook(args)
- end
- assert_equal([comm, line, sec], result,
- "Did not remove name and var records")
-
- assert_equal(name[:name], line[:name],
- "did not set name in hook")
- assert_equal([var[:line]], line[:environment],
- "did not set env")
-
- # Now try it with an env, a name, and a record
- args.clear
- args << var = env.call()
- args << name = comment.call(true)
- args << line = record.call
- assert_nothing_raised do
- result = @provider.prefetch_hook(args)
- end
+ setme
+ target = @provider.target_object(@me)
+ files.each do |file|
+ str = args = nil
+ assert_nothing_raised("could not load %s" % file) do
+ str, args = YAML.load(File.read(file))
+ end
+ target.write(str)
+ assert_nothing_raised("could not parse %s" % file) do
+ @provider.prefetch_target(@me)
+ end
+ records = @provider.send(:instance_variable_get, "@records")
- assert_equal([var, line], result,
- "Removed var record")
- assert_equal(name[:name], line[:name],
- "did not set name in hook")
- assert_nil(line[:environment], "incorrectly set env")
-
- # try it with a comment, an env, and a record
- args.clear
- args << comm = comment.call(false)
- args << var = env.call()
- args << line = record.call
- assert_nothing_raised do
- result = @provider.prefetch_hook(args)
- end
- assert_equal([comm, var, line], result,
- "Removed var record")
- assert_nil(line[:name], "name got set somehow")
- assert_nil(line[:environment], "env got set somehow")
-
- # Try it with multiple records
- args = []
- should = []
- args << startcom = comment.call(false)
- should << startcom
- args << startenv = env.call
- should << startenv
-
- args << name1 = comment.call(true)
- args << env1s = env.call
- args << env1m = env.call
- args << line1 = record.call
- should << line1
-
- args << midcom = comment.call(false)
- args << midenv = env.call
- should << midcom << midenv
-
- args << name2 = comment.call(true)
- args << env2s = env.call
- args << env2m = env.call
- args << line2 = record.call
- should << line2
-
- args << endcom = comment.call(false)
- args << endenv = env.call
- should << endcom << endenv
-
- assert_nothing_raised do
- result = @provider.prefetch_hook(args)
- end
- assert_equal(should, result,
- "Did not handle records correctly")
+ args.zip(records) do |should, sis|
+ # Make the values a bit more equal.
+ should[:target] = @me
+ should[:ensure] = :present
+ should[:on_disk] = true
+ is = sis.dup
+ sis.dup.each do |p,v|
+ is.delete(p) if v == :absent
+ end
+ assert_equal(should, is,
+ "Did not parse %s correctly" % file)
+ end
- assert_equal(line1[:name], line1[:name], "incorrectly set first name")
- assert_equal(line1[:environment], line1[:environment],
- "incorrectly set first env")
+ assert_nothing_raised("could not generate %s" % file) do
+ @provider.flush_target(@me)
+ end
- assert_equal(line2[:name], line2[:name], "incorrectly set second name")
- assert_equal(line2[:environment], line2[:environment],
- "incorrectly set second env")
+ assert_equal(str, target.read, "%s changed" % file)
+ @provider.clear
+ end
end
# A simple test to see if we can load the cron from disk.
@@ -168,6 +101,227 @@ class TestCronParsedProvider < Test::Unit::TestCase
}
assert_instance_of(Array, records, "did not get correct response")
end
+
+ # Test that a cron job turns out as expected, by creating one and generating
+ # it directly
+ def test_simple_to_cron
+ # make the cron
+ setme()
+
+ name = "yaytest"
+ args = {:name => name,
+ :command => "date > /dev/null",
+ :minute => "30",
+ :user => @me,
+ :record_type => :crontab
+ }
+ # generate the text
+ str = nil
+ assert_nothing_raised {
+ str = @provider.to_line(args)
+ }
+
+ assert_equal("# Puppet Name: #{name}\n30 * * * * date > /dev/null", str,
+ "Cron did not generate correctly")
+ end
+
+ # Test that comments are correctly retained
+ def test_retain_comments
+ str = "# this is a comment\n#and another comment\n"
+ user = "fakeuser"
+ records = nil
+ target = @provider.filetype = :ram
+ target = @provider.target_object(user)
+ target.write(str)
+ assert_nothing_raised {
+ @provider.prefetch_target(user)
+ }
+
+ assert_nothing_raised {
+ newstr = @provider.flush_target(user)
+ assert(target.read.include?(str), "Comments were lost")
+ }
+ end
+
+ def test_simpleparsing
+ @provider.filetype = :ram
+ text = "5 1,2 * 1 0 /bin/echo funtest"
+
+ records = nil
+ assert_nothing_raised {
+ records = @provider.parse(text)
+ }
+
+ should = {
+ :minute => %w{5},
+ :hour => %w{1 2},
+ :monthday => :absent,
+ :month => %w{1},
+ :weekday => %w{0},
+ :command => "/bin/echo funtest"
+ }
+
+ is = records.shift
+ assert(is, "Did not get record")
+
+ should.each do |p, v|
+ assert_equal(v, is[p], "did not parse %s correctly" % p)
+ end
+ end
+
+ # Make sure we can create a cron in an empty tab
+ def test_mkcron_if_empty
+ setme
+ @provider.filetype = @oldfiletype
+
+ records = @provider.retrieve(@me)
+
+ target = @provider.target_object(@me)
+
+ cleanup do
+ if records.length == 0
+ target.remove
+ else
+ target.write(@provider.to_file(records))
+ end
+ end
+
+ # Now get rid of it
+ assert_nothing_raised("Could not remove cron tab") do
+ target.remove
+ end
+
+ @provider.flush :target => @me, :command => "/do/something",
+ :record_type => :crontab
+ created = @provider.retrieve(@me)
+ assert(created.detect { |r| r[:command] == "/do/something" },
+ "Did not create cron tab")
+ end
+
+ # Make sure we correctly bidirectionally parse things.
+ def test_records_and_strings
+ @provider.filetype = :ram
+ setme
+
+ target = @provider.target_object(@me)
+
+ [
+ "* * * * * /some/command",
+ "0,30 * * * * /some/command",
+ "0-30 * * * * /some/command",
+ "# Puppet Name: name\n0-30 * * * * /some/command",
+ "# Puppet Name: name\nVAR=VALUE\n0-30 * * * * /some/command",
+ "# Puppet Name: name\nVAR=VALUE\nC=D\n0-30 * * * * /some/command",
+ "0 * * * * /some/command"
+ ].each do |str|
+ @provider.initvars
+ str += "\n"
+ target.write(str)
+ assert_equal(str, target.read,
+ "Did not write correctly")
+ assert_nothing_raised("Could not prefetch with %s" % str.inspect) do
+ @provider.prefetch_target(@me)
+ end
+ assert_nothing_raised("Could not flush with %s" % str.inspect) do
+ @provider.flush_target(@me)
+ end
+
+ assert_equal(str, target.read,
+ "Changed in read/write")
+
+ @provider.clear
+ end
+ 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"
+ setme
+ @provider.filetype = :ram
+
+ cron = nil
+ assert_nothing_raised {
+ cron = @type.create(
+ :name => "yaycron",
+ :minute => [0, 30],
+ :command => "date",
+ :user => @me
+ )
+ }
+
+ hash = @provider.parse_line(str)
+ hash[:user] = @me
+
+ instance = @provider.match(hash)
+ assert(instance, "did not match cron")
+ assert_equal(cron, instance,
+ "Did not match cron job")
+ end
+
+ def test_data
+ setme
+ @provider.filetype = :ram
+ target = @provider.target_object(@me)
+ fakedata("data/providers/cron/examples").each do |file|
+ text = File.read(file)
+ target.write(text)
+
+ assert_nothing_raised("Could not parse %s" % file) do
+ @provider.prefetch_target(@me)
+ end
+ # mark the provider modified
+ @provider.modified(@me)
+
+ # and zero the text
+ target.write("")
+
+ result = nil
+ assert_nothing_raised("Could not generate %s" % file) do
+ @provider.flush_target(@me)
+ end
+
+ assert_equal(text, target.read,
+ "File was not rewritten the same")
+
+ @provider.clear
+ end
+ end
+
+ # Match freebsd's annoying @daily stuff.
+ def test_match_freebsd_special
+ @provider.filetype = :ram
+ setme
+
+ target = @provider.target_object(@me)
+
+ [
+ "@daily /some/command",
+ "@daily /some/command more"
+ ].each do |str|
+ @provider.initvars
+ str += "\n"
+ target.write(str)
+ assert_equal(str, target.read,
+ "Did not write correctly")
+ assert_nothing_raised("Could not prefetch with %s" % str.inspect) do
+ @provider.prefetch_target(@me)
+ end
+ records = @provider.send(:instance_variable_get, "@records")
+ records.each do |r|
+ assert_equal(:freebsd_special, r[:record_type],
+ "Did not create lines as freebsd lines")
+ end
+ assert_nothing_raised("Could not flush with %s" % str.inspect) do
+ @provider.flush_target(@me)
+ end
+
+ assert_equal(str, target.read,
+ "Changed in read/write")
+
+ @provider.clear
+ end
+ end
end
# $Id$
diff --git a/test/ral/types/cron.rb b/test/ral/types/cron.rb
index f4b9719ea..96d7cb3f6 100755
--- a/test/ral/types/cron.rb
+++ b/test/ral/types/cron.rb
@@ -3,18 +3,38 @@
$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
+require 'spec'
# Test cron job creation, modification, and destruction
class TestCron < Test::Unit::TestCase
include PuppetTest
+
def setup
super
setme()
- # god i'm lazy
- @crontype = Puppet.type(:cron)
+ @crontype = Puppet::Type.type(:cron)
+ @provider = @crontype.defaultprovider
+ if @provider.respond_to?(:filetype=)
+ @oldfiletype = @provider.filetype
+ @provider.filetype = :ram
+ end
+ @crontype = Puppet::Type.type(:cron)
+ end
+
+ def teardown
+ @crontype.defaultprovider = nil
+ if defined? @oldfiletype
+ @provider.filetype = @oldfiletype
+ end
+ end
+
+ def eachprovider
+ @crontype.suitableprovider.each do |provider|
+ yield provider
+ end
end
# Back up the user's existing cron tab if they have one.
@@ -76,6 +96,7 @@ class TestCron < Test::Unit::TestCase
comp = newcomp(name, cron)
assert_events([:cron_created], comp)
+ cron.provider.class.prefetch
cron.retrieve
assert(cron.insync?, "Cron is not in sync")
@@ -92,187 +113,47 @@ class TestCron < Test::Unit::TestCase
assert_events([:cron_removed], comp)
+ cron.provider.class.prefetch
cron.retrieve
assert(cron.insync?, "Cron is not in sync")
assert_events([], comp)
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::Util::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)
- }
+ eachprovider do |provider|
+ cron = nil
+ # make the cron
+ name = "yaytest"
+ command = "date > /dev/null "
+ assert_nothing_raised {
+ cron = @crontype.create(
+ :name => name,
+ :command => "date > /dev/null ",
+ :month => "May",
+ :user => @me
+ )
+ }
+ property = cron.send(:property, :command)
+ cron.provider.command = command
+ cron.provider.class.prefetch
+ cron.retrieve
- count = @crontype.inject(0) do |c, obj|
- c + 1
+ assert(property.insync?, "command parsing removes trailing whitespace")
+ @crontype.clear
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.provider.class.prefetch
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
@@ -336,78 +217,80 @@ class TestCron < Test::Unit::TestCase
}
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(Puppet::Util::SUIDManager.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"
- )
- }
+ def test_comma_separated_vals_work
+ eachprovider do |provider|
+ cron = nil
+ assert_nothing_raised {
+ cron = @crontype.create(
+ :command => "/bin/date > /dev/null",
+ :minute => [0, 30],
+ :name => "crontest",
+ :provider => provider.name
+ )
+ }
- assert_events([:cron_created], cron)
- cron.retrieve
- assert_events([], cron)
+ minute = cron.send(:property, :minute)
+ cron.provider.minute = %w{0 30}
+ cron.provider.class.prefetch
+ cron.retrieve
+
+ assert(minute.insync?, "minute is out of sync with %s" % provider.name)
+ @crontype.clear
+ end
end
def test_fieldremoval
cron = nil
assert_nothing_raised {
- cron = Puppet.type(:cron).create(
+ cron = @crontype.create(
:command => "/bin/date > /dev/null",
:minute => [0, 30],
- :name => "crontest"
+ :name => "crontest",
+ :provider => :crontab
)
}
assert_events([:cron_created], cron)
+ cron.provider.class.prefetch
cron[:minute] = :absent
assert_events([:cron_changed], cron)
assert_nothing_raised {
+ cron.provider.class.prefetch
cron.retrieve
}
assert_equal(:absent, cron.is(:minute))
end
def test_listing
- @crontype.filetype = @oldfiletype
+ # Make a crontab cron for testing
+ provider = @crontype.provider(:crontab)
+ return unless provider.suitable?
+
+ ft = provider.filetype
+ provider.filetype = :ram
+ cleanup { provider.filetype = ft }
+
+ setme
+ cron = @crontype.create(:name => "testing",
+ :minute => [0, 30],
+ :command => "/bin/testing",
+ :user => @me
+ )
+ # Write it to our file
+ assert_apply(cron)
crons = []
assert_nothing_raised {
- Puppet::Type.type(:cron).list.each do |cron|
+ @crontype.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],
+ assert_instance_of(@crontype, cron, "Did not receive a real cron object")
+ assert_instance_of(String, cron.value(:user),
"Cron user is not a string")
end
end
@@ -434,61 +317,13 @@ class TestCron < Test::Unit::TestCase
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")
-
- # Now do it again and make sure there are no changes
- assert_events([], cron)
-
- end
-
def test_divisionnumbers
cron = mkcron("divtest")
cron[:minute] = "*/5"
assert_apply(cron)
+ cron.provider.class.prefetch
cron.retrieve
assert_equal(["*/5"], cron.is(:minute))
@@ -500,49 +335,12 @@ class TestCron < Test::Unit::TestCase
assert_apply(cron)
+ cron.provider.class.prefetch
cron.retrieve
assert_equal(["2-4"], cron.is(:minute))
end
- def test_data
- @fakefiletype = Puppet::Util::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", false)
@@ -591,6 +389,8 @@ class TestCron < Test::Unit::TestCase
assert_equal("4,5", cron.value(param))
end
+ Puppet[:trace] = false
+
# Now make sure that :command works correctly
cron.delete(:command)
cron.newattr(:command)
@@ -632,71 +432,6 @@ class TestCron < Test::Unit::TestCase
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
-
def test_multiple_users
crons = []
users = ["root", nonrootuser.name]
@@ -708,18 +443,43 @@ class TestCron < Test::Unit::TestCase
:minute => [0,30]
)
end
+ provider = crons[0].provider.class
assert_apply(*crons)
users.each do |user|
users.each do |other|
next if user == other
- assert(Puppet::Type.type(:cron).tabobj(other).read !~ /testcron-#{user}/,
+ text = provider.target_object(other).read
+
+ assert(text !~ /testcron-#{user}/,
"%s's cron job is in %s's tab" %
[user, other])
end
end
end
+
+ # Make sure the user stuff defaults correctly.
+ def test_default_user
+ crontab = @crontype.provider(:crontab)
+ if crontab.suitable?
+ inst = @crontype.create(
+ :name => "something", :command => "/some/thing",
+ :provider => :crontab)
+ assert_equal(ENV["USER"], inst.should(:user),
+ "user did not default to current user with crontab")
+ assert_equal(ENV["USER"], inst.should(:target),
+ "target did not default to current user with crontab")
+
+ # Now make a new cron with a user, and make sure it gets copied
+ # over
+ inst = @crontype.create(:name => "yay", :command => "/some/thing",
+ :user => "bin", :provider => :crontab)
+ assert_equal("bin", inst.should(:target),
+ "target did not default to user with crontab")
+ end
+ end
end
+
# $Id$
diff --git a/test/util/fileparsing.rb b/test/util/fileparsing.rb
index 12343c38b..c81c9e006 100755
--- a/test/util/fileparsing.rb
+++ b/test/util/fileparsing.rb
@@ -564,7 +564,8 @@ billy three four\n"
assert_nothing_raised("Could not set hooks") do
record = @parser.record_line :yay, :fields => %w{one two},
:post_parse => proc { |hash| hash[:posted] = true },
- :pre_gen => proc { |hash| hash[:one] = hash[:one].upcase }
+ :pre_gen => proc { |hash| hash[:one] = hash[:one].upcase },
+ :to_line => proc { |hash| "# Line\n" + join(hash) }
end
assert(record.respond_to?(:post_parse), "did not create method for post-hook")
@@ -582,7 +583,7 @@ billy three four\n"
assert_nothing_raised("Could not generate line with hooks") do
result = @parser.to_line(result)
end
- assert_equal("ONE two", result, "did not call pre-gen hook")
+ assert_equal("# Line\nONE two", result, "did not call pre-gen hook")
assert_equal("one", old_result[:one], "passed original hash to pre hook")
end
end