summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG3
-rw-r--r--lib/puppet/provider.rb17
-rwxr-xr-xlib/puppet/provider/cron/crontab.rb90
-rwxr-xr-xlib/puppet/provider/parsedfile.rb20
-rwxr-xr-xlib/puppet/provider/sshkey/parsed.rb42
-rwxr-xr-xlib/puppet/type/cron.rb446
-rw-r--r--lib/puppet/util/fileparsing.rb215
-rw-r--r--lib/puppet/util/methodhelper.rb2
-rwxr-xr-xtest/ral/providers/cron/crontab.rb173
-rwxr-xr-xtest/ral/providers/parsedfile.rb41
-rwxr-xr-xtest/ral/providers/provider.rb28
-rwxr-xr-xtest/ral/providers/sshkey/parsed.rb (renamed from test/ral/providers/parsedsshkey.rb)20
-rwxr-xr-xtest/ral/types/cron.rb16
-rwxr-xr-xtest/util/fileparsing.rb319
14 files changed, 759 insertions, 673 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 68400d590..037cd7499 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,7 @@
0.22.2 (grover)
+ Redid some of the internals of the ParsedFile provider base class.
+ It now passes a FileRecord around instead of a hash.
+
Fixing a bug related to link recursion that caused link directories
to always be considered out of sync.
diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb
index 3a31a89a3..23c921ac0 100644
--- a/lib/puppet/provider.rb
+++ b/lib/puppet/provider.rb
@@ -123,6 +123,22 @@ class Puppet::Provider
end
end
+ # Create getter/setter methods for each property our model supports.
+ # They all get stored in @property_hash. This method is useful
+ # for those providers that use prefetch and flush.
+ def self.mkmodelmethods
+ [model.validproperties, model.parameters].flatten.each do |attr|
+ attr = symbolize(attr)
+ define_method(attr) do
+ @property_hash[attr] || :absent
+ end
+
+ define_method(attr.to_s + "=") do |val|
+ @property_hash[attr] = val
+ end
+ end
+ end
+
self.initvars
# Check whether this implementation is suitable for our platform.
@@ -208,6 +224,7 @@ class Puppet::Provider
def initialize(model)
@model = model
+ @property_hash = {}
end
def name
diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb
new file mode 100755
index 000000000..c50bc2680
--- /dev/null
+++ b/lib/puppet/provider/cron/crontab.rb
@@ -0,0 +1,90 @@
+require 'puppet/provider/parsedfile'
+
+tab = case Facter.value(:operatingsystem)
+ when "Solaris": :suntab
+ else
+ :crontab
+ end
+
+Puppet::Type.type(:cron).provide(:crontab,
+ :parent => Puppet::Provider::ParsedFile,
+ :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 :environment, :match => %r{/^\w+=/}
+
+ record_line :crontab, :fields => %w{minute hour weekday month monthday 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
+
+
+ # 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 Note particularly that the comments starting with 'Puppet Name' should
+# HEADER not be deleted, as doing so could cause duplicate cron jobs.\n}
+ end
+
+ # Collapse name and env records.
+ def self.prefetch_hook(records)
+ name = nil
+ envs = []
+ records.each { |record|
+ case record[:record_type]
+ when :comment:
+ if record[:name]
+ name = record[:name]
+ record[:skip] = true
+ end
+ when :environment:
+ if name
+ envs << record[:line]
+ record[:skip] = true
+ end
+ else
+ if name
+ record[:name] = name
+ name = nil
+ end
+ unless envs.empty?
+ record[:environment] = envs
+ envs = []
+ end
+ end
+ }.reject { |record| record[:skip] }
+ end
+
+ def user=(user)
+ self.target = target
+ end
+
+ def user
+ self.target
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb
index f1b77e56b..56474e273 100755
--- a/lib/puppet/provider/parsedfile.rb
+++ b/lib/puppet/provider/parsedfile.rb
@@ -117,8 +117,8 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
list.collect { |r| r[:name] }
end
- # Create attribute methods for each of the model's non-metaparam attributes.
- def self.model=(model)
+ # Override the default method with a lot more functionality.
+ def self.mkmodelmethods
[model.validproperties, model.parameters].flatten.each do |attr|
attr = symbolize(attr)
define_method(attr) do
@@ -152,7 +152,12 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
@property_hash[attr] = val
end
end
- @model = model
+ end
+
+ # Always make the model methods.
+ def self.model=(model)
+ super
+ mkmodelmethods()
end
# Mark a target as modified so we know to flush it. This only gets
@@ -179,12 +184,18 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
# Prefetch an individual target.
def self.prefetch_target(target)
- @records += retrieve(target).each do |r|
+ target_records = retrieve(target).each do |r|
r[:on_disk] = true
r[:target] = target
r[:ensure] = :present
end
+ if respond_to?(:prefetch_hook)
+ prefetch_hook(target_records)
+ end
+
+ @records += target_records
+
# Set current property on any existing resource instances.
target_records(target).find_all { |i| i.is_a?(Hash) }.each do |record|
# Find any model instances whose names match our instances.
@@ -222,7 +233,6 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
ensure
@target = old
end
-
end
end
diff --git a/lib/puppet/provider/sshkey/parsed.rb b/lib/puppet/provider/sshkey/parsed.rb
index e247502ff..d6e175cf8 100755
--- a/lib/puppet/provider/sshkey/parsed.rb
+++ b/lib/puppet/provider/sshkey/parsed.rb
@@ -1,6 +1,5 @@
require 'puppet/provider/parsedfile'
-
known = nil
case Facter.value(:operatingsystem)
when "Darwin": known = "/etc/ssh_known_hosts"
@@ -15,30 +14,23 @@ Puppet::Type.type(:sshkey).provide(:parsed,
) do
text_line :comment, :match => /^#/
text_line :blank, :match => /^\s+/
- record_line :parsed, :fields => %w{name type key}
-
- # Override the line parsing a bit, so we can split the aliases out.
- def self.parse_line(line)
- hash = super
- if hash[:name] =~ /,/
- names = hash[:name].split(",")
- hash[:name] = names.shift
- hash[:alias] = names
- end
- hash
- end
-
-
- def self.to_line(hash)
- if hash[:alias]
- hash = hash.dup
- names = [hash[:name], hash[:alias]].flatten
-
- hash[:name] = [hash[:name], hash[:alias]].flatten.join(",")
- hash.delete(:alias)
- end
- super(hash)
- end
+
+ record_line :parsed, :fields => %w{name type key},
+ :post_parse => proc { |hash|
+ if hash[:name] =~ /,/
+ names = hash[:name].split(",")
+ hash[:name] = names.shift
+ hash[:alias] = names
+ end
+ },
+ :pre_gen => proc { |hash|
+ if hash[:alias]
+ names = [hash[:name], hash[:alias]].flatten
+
+ hash[:name] = [hash[:name], hash[:alias]].flatten.join(",")
+ hash.delete(:alias)
+ end
+ }
end
# $Id$
diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb
index a91af13ed..d71654586 100755
--- a/lib/puppet/type/cron.rb
+++ b/lib/puppet/type/cron.rb
@@ -11,75 +11,11 @@ module Puppet
# and is used to manage the job.
newtype(:cron) do
- # A stupid hack because Cron is the only parsed type that I haven't
- # converted to using providers. This whole stupid type needs to
- # be rewritten.
- class CronHackParam < Puppet::Property::ParsedParam
- # Normally this would retrieve the current value, but our property is not
- # actually capable of doing so.
- def retrieve
- # If we've synced, then just copy the values over and return.
- # This allows this property to behave like any other property.
- if defined? @synced and @synced
- # by default, we only copy over the first value.
- @is = @synced
- @synced = false
- return
- end
-
- unless defined? @is and ! @is.nil?
- @is = :absent
- end
- end
-
- # If the ensure property 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
-
- 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 properties
- # but tell them not to store (we'll store just once,
- # at the end).
- unless nostore
- @parent.eachproperty { |property|
- next if property == self or property.name == :ensure
- property.sync(true)
- }
- end
- elsif self.should == :absent
- @parent.remove(true)
- tail = "deleted"
- end
- 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
-
# 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 < CronHackParam
+ class CronParam < Puppet::Property
class << self
attr_accessor :boundaries, :default
end
@@ -215,14 +151,6 @@ module Puppet
end
end
-
- # Override 'newproperty' so that all properties default to having the
- # correct parent type
- def self.newproperty(name, options = {}, &block)
- options[:parent] ||= Puppet::Property::CronParam
- super(name, options, &block)
- 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
@@ -253,7 +181,7 @@ module Puppet
end
end
- newproperty(:special, :parent => CronHackParam) do
+ newproperty(:special) do
desc "Special schedules only supported on FreeBSD."
def specials
@@ -268,19 +196,19 @@ module Puppet
end
end
- newproperty(:minute) do
+ 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) do
+ 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) do
+ newproperty(:weekday, :parent => CronParam) do
def alpha
%w{sunday monday tuesday wednesday thursday friday saturday}
end
@@ -290,7 +218,7 @@ module Puppet
0 being Sunday, or must be the name of the day (e.g., Tuesday)."
end
- newproperty(:month) do
+ newproperty(:month, :parent => CronParam) do
def alpha
%w{january february march april may june july
august september october november december}
@@ -300,13 +228,13 @@ module Puppet
must be between 1 and 12 or the month name (e.g., December)."
end
- newproperty(:monthday) do
+ 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
- newproperty(:environment, :parent => CronHackParam) do
+ 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
@@ -364,7 +292,7 @@ module Puppet
end
end
- newparam(:user) do
+ 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.
@@ -372,15 +300,6 @@ module Puppet
The user defaults to whomever Puppet is running as."
defaultto { ENV["USER"] }
-
- def value=(value)
- super
-
- # Make sure the user is not an array
- if @value.is_a? Array
- @value = @value[0]
- end
- end
end
@doc = "Installs and manages cron jobs. All fields except the command
@@ -405,58 +324,8 @@ module Puppet
}
"
- @instances = {}
- @tabs = {}
-
- class << self
- attr_accessor :filetype
-
- def cronobj(name)
- if defined? @tabs
- return @tabs[name]
- else
- return nil
- end
- end
- end
-
attr_accessor :uid
- # In addition to removing the instances in @objects, Cron has to remove
- # per-user cron tab information.
- def self.clear
- @instances = {}
- @tabs = {}
- super
- end
-
- def self.defaulttype
- case Facter["operatingsystem"].value
- when "Solaris":
- return Puppet::Util::FileType.filetype(:suntab)
- else
- return Puppet::Util::FileType.filetype(:crontab)
- end
- end
-
- self.filetype = self.defaulttype()
-
- # 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[:user])
- if @instances[child[:user]].include?(child)
- @instances[child[:user]].delete(child)
- end
- end
- super
- end
-
- # Return the fields found in the cron tab.
- def self.fields
- return [:minute, :hour, :monthday, :month, :weekday, :command]
- end
-
# Convert our hash to an object
def self.hash2obj(hash)
obj = nil
@@ -506,33 +375,6 @@ module Puppet
instance(obj)
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 Note particularly that the comments starting with 'Puppet Name' should
-# HEADER not be deleted, as doing so could cause duplicate cron jobs.\n}
- end
-
- def self.instance(obj)
- user = obj[:user]
- unless @instances.include?(user)
- @instances[user] = []
- end
-
- @instances[user] << obj
- end
-
- def self.list
- # Look for cron jobs for each user
- Puppet::Type.type(:user).list_by_name.each { |user|
- self.retrieve(user, false)
- }
-
- self.collect { |c| c }
- end
-
# See if we can match the hash against an existing cron job.
def self.match(hash)
self.find_all { |obj|
@@ -580,276 +422,6 @@ module Puppet
return false
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
- hash = {}
-
- envs = []
- text.chomp.split("\n").each { |line|
- case line
- when /^# Puppet Name: (.+)$/
- hash[:name] = $1
- next
- when /^#/:
- # add other comments to the list as they are
- @instances[user] << line
- next
- when /^\s*(\w+)\s*=\s*(.+)\s*$/:
- # Match env settings.
- if hash[:name]
- envs << line
- else
- @instances[user] << 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?
- # We have to dup here so that we don't remove the settings
- # in @is on the object.
- hash[:environment] = envs.dup
- end
-
- hash[:user] = user
-
- # Now convert our hash to an object.
- hash2obj(hash)
-
- hash = {}
- envs.clear
- count += 1
- }
- end
-
- # Retrieve a given user's cron job, using the @filetype's +retrieve+
- # method. Returns nil if there was no cron job; else, returns the
- # number of cron instances found.
- def self.retrieve(user, checkuser = true)
- # First make sure the user exists, unless told not to
- if checkuser
- begin
- Puppet::Util.uid(user)
- rescue ArgumentError
- raise Puppet::Error, "User %s not found" % user
- end
- end
-
- @tabs[user] ||= @filetype.new(user)
- text = @tabs[user].read
- if $? != 0
- # there is no cron file
- return nil
- else
- # Preemptively mark everything absent, so that retrieving it
- # can mark it present again.
- self.find_all { |obj|
- obj[:user] == user
- }.each { |obj|
- obj.is = [:ensure, :absent]
- }
-
- # Get rid of the old instances, so we don't get duplicates
- if @instances.include?(user)
- @instances[user].clear
- else
- @instances[user] = []
- end
-
- self.parse(user, text)
- end
- end
-
- # Remove a user's cron tab.
- def self.remove(user)
- @tabs[user] ||= @filetype.new(user)
- @tabs[user].remove
- end
-
- # Store the user's cron tab. Collects the text of the new tab and
- # sends it to the +@filetype+ module's +write+ function. Also adds
- # header, warning users not to modify the file directly.
- def self.store(user)
- unless @instances.include?(user) or @objects.find do |n,o|
- o[:user] == user
- end
- Puppet.notice "No cron instances for %s" % user
- return
- end
-
- @tabs[user] ||= @filetype.new(user)
-
- self.each do |inst|
- next unless inst[:user] == user
- unless (@instances[user] and @instances[user].include? inst)
- @instances[user] ||= []
- @instances[user] << inst
- end
- end
- @tabs[user].write(self.tab(user))
- end
-
- # Collect all Cron instances for a given user and convert them
- # into literal text.
- def self.tab(user)
- Puppet.info "Writing cron tab for %s" % user
- if @instances.include?(user)
- tab = @instances[user].reject { |obj|
- if obj.is_a?(self) and obj.should(:ensure) == :absent
- true
- else
- false
- end
- }.collect { |obj|
- if obj.is_a? self
- obj.to_record
- else
- obj.to_s
- end
- }.join("\n") + "\n"
-
- # 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
-
- else
- Puppet.notice "No cron instances for %s" % user
- end
- end
-
- # Return the tab object itself. Pretty much just used for testing.
- def self.tabobj(user)
- @tabs[user]
- end
-
- # Return the last time a given user's cron tab was loaded. Could
- # be used for reducing writes, but currently is not.
- def self.loaded?(user)
- if @tabs.include?(user)
- return @loaded[user].loaded
- else
- return nil
- end
- end
-
- def create
- # nothing
- self.store
- end
-
- def destroy
- # nothing, since the 'Cron.tab' method just doesn't write out
- # crons whose 'ensure' states are set to 'absent'.
- self.store
- end
-
- def exists?
- obj = @parameters[:ensure] and obj.is == :present
- end
-
- # Override the default Puppet::Type method because we need to call
- # the +@filetype+ retrieve method.
- def retrieve
- unless @parameters.include?(:user)
- self.fail "You must specify the cron user"
- end
-
- self.class.retrieve(self[:user])
- self.eachproperty { |st|
- st.retrieve
- }
- end
-
- # Write the entire user's cron tab out.
- def store
- self.class.store(self[:user])
- 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 to_record
- hash = {}
-
- # Collect all of the values that we have
- self.class.fields().each { |param|
- hash[param] = self.value(param)
-
- unless hash[param]
- devfail "Got no value for %s" % param
- end
- }
-
- str = ""
-
- str = "# Puppet Name: %s\n" % self.name
-
- if env = @parameters[:environment] and env.should != :absent
- envs = env.should
- unless envs.is_a? Array
- envs = [envs]
- end
-
- envs.each do |line| str += (line + "\n") end
- end
-
- line = nil
- if special = self.value(:special)
- line = str + "@%s %s" %
- [special, self.value(: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
-
def value(name)
name = symbolize(name)
ret = nil
diff --git a/lib/puppet/util/fileparsing.rb b/lib/puppet/util/fileparsing.rb
index 8d8d92df9..08f1c79d9 100644
--- a/lib/puppet/util/fileparsing.rb
+++ b/lib/puppet/util/fileparsing.rb
@@ -24,10 +24,75 @@
# You could then call 'parser.to_line(hash)' on any of those hashes to generate
# the text line again.
+require 'puppet/util/methodhelper'
+
module Puppet::Util::FileParsing
include Puppet::Util
attr_writer :line_separator, :trailing_separator
+ class FileRecord
+ include Puppet::Util
+ include Puppet::Util::MethodHelper
+ attr_accessor :absent, :joiner, :rts,
+ :separator, :rollup, :name, :match
+
+ attr_reader :fields, :optional, :type
+
+ INVALID_FIELDS = [:record_type, :target, :on_disk]
+
+ # Customize this so we can do a bit of validation.
+ def fields=(fields)
+ @fields = fields.collect do |field|
+ r = symbolize(field)
+ if INVALID_FIELDS.include?(r)
+ raise ArgumentError.new("Cannot have fields named %s" % r)
+ end
+ r
+ end
+ end
+
+ def initialize(type, options = {}, &block)
+ @type = symbolize(type)
+ unless [:record, :text].include?(@type)
+ raise ArgumentError, "Invalid record type %s" % @type
+ end
+
+ set_options(options)
+
+ if self.type == :record
+ # Now set defaults.
+ self.absent ||= ""
+ self.separator ||= /\s+/
+ self.joiner ||= " "
+ self.optional ||= []
+ unless defined? @rollup
+ @rollup = true
+ end
+ end
+
+ if block_given?
+ meta_def(:process, &block)
+ end
+ end
+
+ # Customize this so we can do a bit of validation.
+ def optional=(optional)
+ @optional = optional.collect do |field|
+ symbolize(field)
+ end
+ end
+
+ # Create a hook that modifies the hash resulting from parsing.
+ def post_parse=(block)
+ meta_def(:post_parse, &block)
+ end
+
+ # Create a hook that modifies the hash just prior to generation.
+ def pre_gen=(block)
+ meta_def(:pre_gen, &block)
+ end
+ end
+
# Clear all existing record definitions. Only used for testing.
def clear_records
@record_types.clear
@@ -35,44 +100,45 @@ module Puppet::Util::FileParsing
end
def fields(type)
- type = symbolize(type)
- if @record_types.include?(type)
- @record_types[type][:fields].dup
+ if record = record_type(type)
+ record.fields.dup
else
nil
end
end
# Try to match a specific text line.
- def handle_text_line(line, hash)
- if line =~ hash[:match]
- return {:record_type => hash[:name], :line => line}
+ def handle_text_line(line, record)
+ if line =~ record.match
+ return {:record_type => record.name, :line => line}
else
return nil
end
end
# Try to match a record.
- def handle_record_line(line, hash)
- if method = hash[:method]
- if ret = send(method, line.dup)
- ret[:record_type] = hash[:name]
+ def handle_record_line(line, record)
+ 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 = hash[:match]
+ 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 = hash[:ignore] || []
- p match.captures
+ ignore = record.ignore || []
match.captures.each_with_index do |value, i|
fields << value unless ignore.include? i
end
- p fields
nil
else
Puppet.info "Did not match %s" % line
@@ -80,21 +146,29 @@ module Puppet::Util::FileParsing
end
else
ret = {}
- sep = hash[:separator]
+ sep = record.separator
# String "helpfully" replaces ' ' with /\s+/ in splitting, so we
# have to work around it.
if sep == " "
sep = / /
end
- hash[:fields].zip(line.split(sep)) do |param, value|
- if value and value != ""
+ line_fields = line.split(sep)
+ record.fields.each do |param|
+ value = line_fields.shift
+ if value and value != record.absent
ret[param] = value
else
ret[param] = :absent
end
end
- ret[:record_type] = hash[:name]
+
+ unless line_fields.empty? or ! record.rollup
+ last_field = record.fields[-1]
+ val = ([ret[last_field]] + line_fields).join(record.joiner)
+ ret[last_field] = val
+ end
+ ret[:record_type] = record.name
return ret
end
end
@@ -127,18 +201,24 @@ module Puppet::Util::FileParsing
end
@record_order.each do |name|
- hash = @record_types[name]
- unless hash
- raise Puppet::DevError, "Did not get hash for %s: %s" %
+ record = record_type(name)
+ unless record
+ raise Puppet::DevError, "Did not get record type for %s: %s" %
[name, @record_types.inspect]
end
- method = "handle_%s_line" % hash[:type]
+
+ # These are basically either text or record lines.
+ method = "handle_%s_line" % record.type
if respond_to?(method)
- if result = send(method, line, hash)
+ if result = send(method, line, record)
+ if record.respond_to?(:post_parse)
+ record.send(:post_parse, result)
+ end
return result
end
else
- raise Puppet::DevError, "Somehow got invalid line type %s" % hash[:type]
+ raise Puppet::DevError,
+ "Somehow got invalid line type %s" % record.type
end
end
@@ -162,42 +242,10 @@ module Puppet::Util::FileParsing
raise ArgumentError, "Must include a list of fields"
end
- invalidfields = [:record_type, :target, :on_disk]
- options[:fields] = options[:fields].collect do |field|
- r = symbolize(field)
- if invalidfields.include?(r)
- raise ArgumentError.new("Cannot have fields named %s" % r)
- end
- r
- end
-
- options[:absent] ||= ""
-
- if options[:optional]
- options[:optional] = options[:optional].collect { |f| symbolize(f) }
- else
- options[:optional] = []
- end
-
- options[:separator] ||= /\s+/
+ record = FileRecord.new(:record, options, &block)
+ record.name = symbolize(name)
- # Unless they specified a string-based joiner, just use a single
- # space as the join value.
- unless options[:separator].is_a?(String) or options[:joiner]
- options[:joiner] = " "
- end
-
-
- if block_given?
- method = "handle_record_line_%s" % name
- if respond_to?(method)
- raise "Already have a method defined for this record"
- end
- meta_def(method, &block)
- options[:method] = method
- end
-
- new_line_type(name, :record, options)
+ new_line_type(record)
end
# Are there any record types defined?
@@ -206,12 +254,15 @@ module Puppet::Util::FileParsing
end
# Define a new type of text record.
- def text_line(name, options)
+ def text_line(name, options, &block)
unless options.include?(:match)
raise ArgumentError, "You must provide a :match regex for text lines"
end
- new_line_type(name, :text, options)
+ record = FileRecord.new(:text, options, &block)
+ record.name = symbolize(name)
+
+ new_line_type(record)
end
# Generate a file from a bunch of hash records.
@@ -227,20 +278,25 @@ module Puppet::Util::FileParsing
# Convert our parsed record into a text record.
def to_line(details)
- unless type = @record_types[details[:record_type]]
+ unless record = record_type(details[:record_type])
raise ArgumentError, "Invalid record type %s" % details[:record_type]
end
- case type[:type]
+ if record.respond_to?(:pre_gen)
+ details = details.dup
+ record.send(:pre_gen, details)
+ end
+
+ case record.type
when :text: return details[:line]
else
- joinchar = type[:joiner] || type[:separator]
+ joinchar = record.joiner
- line = type[:fields].collect { |field|
+ line = record.fields.collect { |field|
# If the field is marked absent, use the appropriate replacement
if details[field] == :absent or details[field].nil?
- if type[:optional].include?(field)
- type[:absent]
+ if record.optional.include?(field)
+ record.absent
else
raise ArgumentError, "Field %s is required" % field
end
@@ -249,7 +305,7 @@ module Puppet::Util::FileParsing
end
}.reject { |c| c.nil?}.join(joinchar)
- if regex = type[:rts]
+ if regex = record.rts
# If they say true, then use whitespace; else, use their regex.
if regex == true
regex = /\s+$/
@@ -272,7 +328,7 @@ module Puppet::Util::FileParsing
def valid_attr?(type, attr)
type = symbolize(type)
- if @record_types[type] and @record_types[type][:fields].include?(symbolize(attr))
+ if record = record_type(type) and record.fields.include?(symbolize(attr))
return true
else
if symbolize(attr) == :ensure
@@ -285,22 +341,23 @@ module Puppet::Util::FileParsing
private
# Define a new type of record.
- def new_line_type(name, type, options)
+ def new_line_type(record)
@record_types ||= {}
@record_order ||= []
- name = symbolize(name)
-
- if @record_types.include?(name)
- raise ArgumentError, "Line type %s is already defined" % name
+ if @record_types.include?(record.name)
+ raise ArgumentError, "Line type %s is already defined" % record.name
end
- options[:name] = name
- options[:type] = type
- @record_types[name] = options
- @record_order << name
+ @record_types[record.name] = record
+ @record_order << record.name
+
+ return record
+ end
- return options
+ # Retrive the record object.
+ def record_type(type)
+ @record_types[symbolize(type)]
end
end
diff --git a/lib/puppet/util/methodhelper.rb b/lib/puppet/util/methodhelper.rb
index 7de723c3b..63158ab67 100644
--- a/lib/puppet/util/methodhelper.rb
+++ b/lib/puppet/util/methodhelper.rb
@@ -15,7 +15,7 @@ module Puppet::Util::MethodHelper
begin
self.send(method, value)
rescue NoMethodError
- self.fail "Invalid parameter %s to object class %s" %
+ raise ArgumentError, "Invalid parameter %s to object class %s" %
[param,self.class.to_s]
end
end
diff --git a/test/ral/providers/cron/crontab.rb b/test/ral/providers/cron/crontab.rb
new file mode 100755
index 000000000..c4f1031cd
--- /dev/null
+++ b/test/ral/providers/cron/crontab.rb
@@ -0,0 +1,173 @@
+#!/usr/bin/env ruby
+
+$:.unshift("../../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppettest'
+require 'mocha'
+require 'puppettest/fileparsing'
+require 'puppet/type/cron'
+require 'puppet/provider/cron/crontab'
+
+class TestCronParsedProvider < Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::FileParsing
+
+ def setup
+ super
+ @provider = Puppet::Type.type(:cron).provider(:crontab)
+
+ @oldfiletype = @provider.filetype
+ end
+
+ def teardown
+ Puppet::Util::FileType.filetype(:ram).clear
+ @provider.filetype = @oldfiletype
+ @provider.clear
+ super
+ end
+
+ def test_parse_record
+ fields = [:month, :weekday, :monthday, :hour, :command, :minute]
+ {
+ "* * * * * /bin/echo" => {:command => "/bin/echo"},
+ "10 * * * * /bin/echo test" => {:minute => "10",
+ :command => "/bin/echo test"}
+ }.each do |line, should|
+ result = nil
+ assert_nothing_raised("Could not parse %s" % line.inspect) do
+ result = @provider.parse_line(line)
+ end
+ should[:record_type] = :crontab
+ fields.each do |field|
+ if should[field]
+ assert_equal(should[field], result[field],
+ "Did not parse %s in %s correctly" % [field, line.inspect])
+ else
+ assert_equal(:absent, result[field],
+ "Did not set %s absent in %s" % [field, line.inspect])
+ end
+ end
+ 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
+
+ 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
+
+ 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")
+
+ assert_equal(line1[:name], line1[:name], "incorrectly set first name")
+ assert_equal(line1[:environment], line1[:environment],
+ "incorrectly set first env")
+
+ assert_equal(line2[:name], line2[:name], "incorrectly set second name")
+ assert_equal(line2[:environment], line2[:environment],
+ "incorrectly set second env")
+ end
+
+ # A simple test to see if we can load the cron from disk.
+ def test_load
+ setme()
+ records = nil
+ assert_nothing_raised {
+ records = @provider.retrieve(@me)
+ }
+ assert_instance_of(Array, records, "did not get correct response")
+ end
+end
+
+# $Id$
diff --git a/test/ral/providers/parsedfile.rb b/test/ral/providers/parsedfile.rb
index 4f2751f39..8a3b0ca97 100755
--- a/test/ral/providers/parsedfile.rb
+++ b/test/ral/providers/parsedfile.rb
@@ -3,6 +3,7 @@
$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
+require 'mocha'
require 'puppettest/fileparsing'
require 'puppet/util/filetype'
require 'puppet/provider/parsedfile'
@@ -513,11 +514,20 @@ class TestParsedFile < Test::Unit::TestCase
end
cleanup { @type.unprovide(:record) }
- records = prov.parse("a d")
+ line = prov.parse_line("a d")
- line = records.find { |r| r[:name] == "a" }
- assert(line, "Could not find line")
+ assert_equal("a", line[:name], "field name was not set")
+ assert_equal(:absent, line[:one], "field one was not set to absent")
+
+ # Now use a different provider with a non-blank "absent"
+ prov = @type.provide(:cronstyle, :parent => Puppet::Provider::ParsedFile) do
+ record_line :cronstyle, :fields => %w{name one two},
+ :separator => "\s", :absent => "*"
+ end
+ cleanup { @type.unprovide(:cronstyle) }
+ line = prov.parse_line("a * d")
+ assert_equal("a", line[:name], "field name was not set")
assert_equal(:absent, line[:one], "field one was not set to absent")
end
@@ -595,6 +605,31 @@ class TestParsedFile < Test::Unit::TestCase
assert(bill.insync?,
"An invalid field marked the record out of sync")
end
+
+ # Make sure we call the prefetch hook at the right place.
+ def test_prefetch_hook
+ prov = @type.provide(:test, :parent => Puppet::Provider::ParsedFile,
+ :filetype => :ram, :default_target => :yayness) do
+
+ def self.prefetch_hook(records)
+ end
+
+ record_line :test, :fields => %w{name two}
+ end
+ cleanup do @type.unprovide(:test) end
+
+ target = "target"
+
+ records = [{:target => "nope"}]
+ targeted = {:target => "target"}
+ prov.send(:instance_variable_set, "@records", records)
+ prov.expects(:retrieve).with(target).returns([targeted])
+ prov.expects(:target_records).with(target).returns([targeted])
+
+ prov.expects(:prefetch_hook).with([targeted])
+
+ prov.prefetch_target(target)
+ end
end
# $Id$
diff --git a/test/ral/providers/provider.rb b/test/ral/providers/provider.rb
index d9b071882..e0401f277 100755
--- a/test/ral/providers/provider.rb
+++ b/test/ral/providers/provider.rb
@@ -106,6 +106,7 @@ class TestProvider < Test::Unit::TestCase
provider.command(:fake)
end
+ Puppet[:trace] = false
assert_raise(Puppet::DevError) do
provider.command(:nosuchcmd)
end
@@ -182,6 +183,33 @@ class TestProvider < Test::Unit::TestCase
assert(out =~ /Execution of/,
"Did not receive info wrapper on failure")
end
+
+ def test_mkmodelmethods
+ prov = newprovider
+ modeltype = Struct.new(:validproperties, :parameters)
+ m = modeltype.new([:prop1, :prop2], [:param1, :param2])
+ prov.model = m
+
+ assert_nothing_raised("could not call mkmodelmethods") do
+ prov.mkmodelmethods
+ end
+
+ obj = prov.new(nil)
+
+ %w{prop1 prop2 param1 param2}.each do |param|
+ assert(prov.public_method_defined?(param), "no getter for %s" % param)
+ assert(prov.public_method_defined?(param + "="), "no setter for %s" % param)
+
+ assert_equal(:absent, obj.send(param),
+ "%s did not default to :absent")
+ val = "testing %s" % param
+ assert_nothing_raised("Could not call setter for %s" % param) do
+ obj.send(param + "=", val)
+ end
+ assert_equal(val, obj.send(param),
+ "did not get correct value for %s" % param)
+ end
+ end
end
# $Id$
diff --git a/test/ral/providers/parsedsshkey.rb b/test/ral/providers/sshkey/parsed.rb
index 854a1342a..02cfe5b95 100755
--- a/test/ral/providers/parsedsshkey.rb
+++ b/test/ral/providers/sshkey/parsed.rb
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
+$:.unshift("../../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
require 'puppettest/fileparsing'
@@ -88,6 +88,24 @@ class TestParsedSSHKey < Test::Unit::TestCase
assert(key.name !~ /,/, "Aliases were not split out during parsing")
end
+
+ def test_hooks
+ result = nil
+ assert_nothing_raised("Could not call post hook") do
+ result = @provider.parse_line("one,two type key")
+ end
+ assert_equal("one", result[:name], "Did not call post hook")
+ assert_equal(%w{two}, result[:alias], "Did not call post hook")
+
+ assert_equal("one,two type key",
+ @provider.to_line(:record_type => :parsed,
+ :name => "one",
+ :alias => %w{two},
+ :type => "type",
+ :key => "key"),
+ "Did not use pre-hook when generating line"
+ )
+ end
end
# $Id$
diff --git a/test/ral/types/cron.rb b/test/ral/types/cron.rb
index 3baf6aac3..f4b9719ea 100755
--- a/test/ral/types/cron.rb
+++ b/test/ral/types/cron.rb
@@ -15,15 +15,6 @@ class TestCron < Test::Unit::TestCase
# god i'm lazy
@crontype = Puppet.type(:cron)
- @oldfiletype = @crontype.filetype
- @fakefiletype = Puppet::Util::FileType.filetype(:ram)
- @crontype.filetype = @fakefiletype
- end
-
- def teardown
- @crontype.filetype = @oldfiletype
- Puppet::Util::FileType.filetype(:ram).clear
- super
end
# Back up the user's existing cron tab if they have one.
@@ -107,13 +98,6 @@ class TestCron < Test::Unit::TestCase
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
diff --git a/test/util/fileparsing.rb b/test/util/fileparsing.rb
index 93f784d52..12343c38b 100755
--- a/test/util/fileparsing.rb
+++ b/test/util/fileparsing.rb
@@ -15,23 +15,26 @@ class TestUtilFileParsing < Test::Unit::TestCase
include Puppet::Util::FileParsing
end
- def test_lines
- parser = FParser.new
+ def setup
+ super
+ @parser = FParser.new
+ end
- assert_equal("\n", parser.line_separator,
+ def test_lines
+ assert_equal("\n", @parser.line_separator,
"Default separator was incorrect")
{"\n" => ["one two\nthree four", "one two\nthree four\n"],
"\t" => ["one two\tthree four", "one two\tthree four\t"],
}.each do |sep, tests|
assert_nothing_raised do
- parser.line_separator = sep
+ @parser.line_separator = sep
end
- assert_equal(sep, parser.line_separator,
+ assert_equal(sep, @parser.line_separator,
"Did not set separator")
tests.each do |test|
- assert_equal(["one two", "three four"], parser.lines(test),
+ assert_equal(["one two", "three four"], @parser.lines(test),
"Incorrectly parsed %s" % test.inspect)
end
end
@@ -39,9 +42,7 @@ class TestUtilFileParsing < Test::Unit::TestCase
# Make sure parse calls the appropriate methods or errors out
def test_parse
- parser = FParser.new
-
- parser.meta_def(:parse_line) do |line|
+ @parser.meta_def(:parse_line) do |line|
line.split(/\s+/)
end
@@ -49,7 +50,7 @@ class TestUtilFileParsing < Test::Unit::TestCase
should = [%w{one line}, %w{two line}]
ret = nil
assert_nothing_raised do
- ret = parser.parse(text)
+ ret = @parser.parse(text)
end
assert_equal(should, ret)
@@ -57,80 +58,78 @@ class TestUtilFileParsing < Test::Unit::TestCase
# Make sure we correctly handle different kinds of text lines.
def test_text_line
- parser = FParser.new
-
comment = "# this is a comment"
# Make sure it fails if no regex is passed
assert_raise(ArgumentError) do
- parser.text_line :comment
+ @parser.text_line :comment
end
# define a text matching comment record
assert_nothing_raised do
- parser.text_line :comment, :match => /^#/
+ @parser.text_line :comment, :match => /^#/
end
# Make sure it matches
assert_nothing_raised do
assert_equal({:record_type => :comment, :line => comment},
- parser.parse_line(comment))
+ @parser.parse_line(comment))
end
# But not something else
assert_nothing_raised do
- assert_nil(parser.parse_line("some other text"))
+ assert_nil(@parser.parse_line("some other text"))
end
# Now define another type and make sure we get the right one back
assert_nothing_raised do
- parser.text_line :blank, :match => /^\s*$/
+ @parser.text_line :blank, :match => /^\s*$/
end
# The comment should still match
assert_nothing_raised do
assert_equal({:record_type => :comment, :line => comment},
- parser.parse_line(comment))
+ @parser.parse_line(comment))
end
# As should our new line type
assert_nothing_raised do
assert_equal({:record_type => :blank, :line => ""},
- parser.parse_line(""))
+ @parser.parse_line(""))
end
end
def test_parse_line
- parser = FParser.new
+ Puppet[:trace] = false
comment = "# this is a comment"
# Make sure it fails if we don't have any record types defined
assert_raise(Puppet::DevError) do
- parser.parse_line(comment)
+ @parser.parse_line(comment)
end
# Now define a text matching comment record
assert_nothing_raised do
- parser.text_line :comment, :match => /^#/
+ @parser.text_line :comment, :match => /^#/
end
# And make sure we can't define another one with the same name
assert_raise(ArgumentError) do
- parser.text_line :comment, :match => /^"/
+ @parser.text_line :comment, :match => /^"/
end
result = nil
assert_nothing_raised("Did not parse text line") do
- result = parser.parse_line comment
+ result = @parser.parse_line comment
end
assert_equal({:record_type => :comment, :line => comment}, result)
# Make sure we just return nil on unmatched lines.
assert_nothing_raised("Did not parse text line") do
- result = parser.parse_line "No match for this"
+ result = @parser.parse_line "No match for this"
end
assert_nil(result, "Somehow matched an empty line")
@@ -138,63 +137,61 @@ class TestUtilFileParsing < Test::Unit::TestCase
# Now define another type of comment, and make sure both types get
# correctly returned as comments
assert_nothing_raised do
- parser.text_line :comment2, :match => /^"/
+ @parser.text_line :comment2, :match => /^"/
end
assert_nothing_raised("Did not parse old comment") do
assert_equal({:record_type => :comment, :line => comment},
- parser.parse_line(comment))
+ @parser.parse_line(comment))
end
comment = '" another type of comment'
assert_nothing_raised("Did not parse new comment") do
assert_equal({:record_type => :comment2, :line => comment},
- parser.parse_line(comment))
+ @parser.parse_line(comment))
end
# Now define two overlapping record types and make sure we keep the
# correct order. We do first match, not longest match.
assert_nothing_raised do
- parser.text_line :one, :match => /^y/
- parser.text_line :two, :match => /^yay/
+ @parser.text_line :one, :match => /^y/
+ @parser.text_line :two, :match => /^yay/
end
assert_nothing_raised do
assert_equal({:record_type => :one, :line => "yayness"},
- parser.parse_line("yayness"))
+ @parser.parse_line("yayness"))
end
end
def test_record_line
- parser = FParser.new
-
tabrecord = "tab separated content"
spacerecord = "space separated content"
# Make sure we always require an appropriate set of options
[{:separator => "\t"}, {}, {:fields => %w{record_type}}].each do |opts|
assert_raise(ArgumentError, "Accepted %s" % opts.inspect) do
- parser.record_line :record, opts
+ @parser.record_line :record, opts
end
end
# Verify that our default separator is tabs
tabs = nil
assert_nothing_raised do
- tabs = parser.record_line :tabs, :fields => [:name, :first, :second]
+ tabs = @parser.record_line :tabs, :fields => [:name, :first, :second]
end
# Make sure out tab line gets matched
tabshould = {:record_type => :tabs, :name => "tab", :first => "separated",
:second => "content"}
assert_nothing_raised do
- assert_equal(tabshould, parser.handle_record_line(tabrecord, tabs))
+ assert_equal(tabshould, @parser.handle_record_line(tabrecord, tabs))
end
# Now add our space-separated record type
spaces = nil
assert_nothing_raised do
- spaces = parser.record_line :spaces, :fields => [:name, :first, :second]
+ spaces = @parser.record_line :spaces, :fields => [:name, :first, :second]
end
# Now make sure both lines parse correctly
@@ -202,17 +199,15 @@ class TestUtilFileParsing < Test::Unit::TestCase
:first => "separated", :second => "content"}
assert_nothing_raised do
- assert_equal(tabshould, parser.handle_record_line(tabrecord, tabs))
- assert_equal(spaceshould, parser.handle_record_line(spacerecord, spaces))
+ assert_equal(tabshould, @parser.handle_record_line(tabrecord, tabs))
+ assert_equal(spaceshould, @parser.handle_record_line(spacerecord, spaces))
end
end
def test_to_line
- parser = FParser.new
-
- parser.text_line :comment, :match => /^#/
- parser.text_line :blank, :match => /^\s*$/
- parser.record_line :record, :fields => %w{name one two}, :joiner => "\t"
+ @parser.text_line :comment, :match => /^#/
+ @parser.text_line :blank, :match => /^\s*$/
+ @parser.record_line :record, :fields => %w{name one two}, :joiner => "\t"
johnny = {:record_type => :record, :name => "johnny", :one => "home",
:two => "yay"}
@@ -236,7 +231,7 @@ class TestUtilFileParsing < Test::Unit::TestCase
records.each do |name, details|
result = nil
assert_nothing_raised do
- result = parser.to_line(details)
+ result = @parser.to_line(details)
end
assert_equal(lines[name], result)
@@ -248,55 +243,53 @@ class TestUtilFileParsing < Test::Unit::TestCase
ordered_records = order.collect { |name| records[name] }
# Make sure we default to a trailing separator
- assert_equal(true, parser.trailing_separator,
+ assert_equal(true, @parser.trailing_separator,
"Did not default to a trailing separtor")
# Start without a trailing separator
- parser.trailing_separator = false
+ @parser.trailing_separator = false
assert_nothing_raised do
- assert_equal(file, parser.to_file(ordered_records))
+ assert_equal(file, @parser.to_file(ordered_records))
end
# Now with a trailing separator
file += "\n"
- parser.trailing_separator = true
+ @parser.trailing_separator = true
assert_nothing_raised do
- assert_equal(file, parser.to_file(ordered_records))
+ assert_equal(file, @parser.to_file(ordered_records))
end
# Now try it with a different separator, so we're not just catching
# defaults
file.gsub!("\n", "\t")
- parser.line_separator = "\t"
+ @parser.line_separator = "\t"
assert_nothing_raised do
- assert_equal(file, parser.to_file(ordered_records))
+ assert_equal(file, @parser.to_file(ordered_records))
end
end
# Make sure fields that are marked absent get replaced with the appropriate
# string.
def test_absent_fields
- parser = FParser.new
-
- options = nil
+ record = nil
assert_nothing_raised do
- options = parser.record_line :record, :fields => %w{one two three},
+ record = @parser.record_line :record, :fields => %w{one two three},
:optional => %w{two three}
end
- assert_equal("", options[:absent], "Did not set a default absent string")
+ assert_equal("", record.absent, "Did not set a default absent string")
result = nil
assert_nothing_raised do
- result = parser.to_line(:record_type => :record,
+ result = @parser.to_line(:record_type => :record,
:one => "a", :two => :absent, :three => "b")
end
assert_equal("a b", result, "Absent was not correctly replaced")
# Now try using a different replacement character
- options[:absent] = "*" # Because cron is a pain in my ass
+ record.absent = "*" # Because cron is a pain in my ass
assert_nothing_raised do
- result = parser.to_line(:record_type => :record,
+ result = @parser.to_line(:record_type => :record,
:one => "a", :two => :absent, :three => "b")
end
@@ -304,7 +297,7 @@ class TestUtilFileParsing < Test::Unit::TestCase
# Make sure we deal correctly with the string 'absent'
assert_nothing_raised do
- result = parser.to_line(:record_type => :record,
+ result = @parser.to_line(:record_type => :record,
:one => "a", :two => "b", :three => 'absent')
end
@@ -312,7 +305,7 @@ class TestUtilFileParsing < Test::Unit::TestCase
# And, of course, make sure we can swap things around.
assert_nothing_raised do
- result = parser.to_line(:record_type => :record,
+ result = @parser.to_line(:record_type => :record,
:one => "a", :two => "b", :three => :absent)
end
@@ -321,38 +314,36 @@ class TestUtilFileParsing < Test::Unit::TestCase
# Make sure we can specify a different join character than split character
def test_split_join_record_line
- parser = FParser.new
-
check = proc do |start, record, final|
# Check parsing first
- result = parser.parse_line(start)
+ result = @parser.parse_line(start)
[:one, :two].each do |param|
assert_equal(record[param], result[param],
"Did not correctly parse %s" % start.inspect)
end
# And generating
- assert_equal(final, parser.to_line(result),
+ assert_equal(final, @parser.to_line(result),
"Did not correctly generate %s from %s" %
[final.inspect, record.inspect])
end
# First try it with symmetric characters
- parser.record_line :symmetric, :fields => %w{one two},
+ @parser.record_line :symmetric, :fields => %w{one two},
:separator => " "
check.call "a b", {:one => "a", :two => "b"}, "a b"
- parser.clear_records
+ @parser.clear_records
# Now assymetric but both strings
- parser.record_line :asymmetric, :fields => %w{one two},
+ @parser.record_line :asymmetric, :fields => %w{one two},
:separator => "\t", :joiner => " "
check.call "a\tb", {:one => "a", :two => "b"}, "a b"
- parser.clear_records
+ @parser.clear_records
# And assymmetric with a regex
- parser.record_line :asymmetric2, :fields => %w{one two},
+ @parser.record_line :asymmetric2, :fields => %w{one two},
:separator => /\s+/, :joiner => " "
check.call "a\tb", {:one => "a", :two => "b"}, "a b"
@@ -361,11 +352,9 @@ class TestUtilFileParsing < Test::Unit::TestCase
# Make sure we correctly regenerate files.
def test_to_file
- parser = FParser.new
-
- parser.text_line :comment, :match => /^#/
- parser.text_line :blank, :match => /^\s*$/
- parser.record_line :record, :fields => %w{name one two}
+ @parser.text_line :comment, :match => /^#/
+ @parser.text_line :blank, :match => /^\s*$/
+ @parser.record_line :record, :fields => %w{name one two}
text = "# This is a comment
@@ -374,33 +363,29 @@ billy three four\n"
# Just parse and generate, to make sure it's isomorphic.
assert_nothing_raised do
- assert_equal(text, parser.to_file(parser.parse(text)),
+ assert_equal(text, @parser.to_file(@parser.parse(text)),
"parsing was not isomorphic")
end
end
def test_valid_attrs
- parser = FParser.new
-
- parser.record_line :record, :fields => %w{one two three}
+ @parser.record_line :record, :fields => %w{one two three}
- assert(parser.valid_attr?(:record, :one),
+ assert(@parser.valid_attr?(:record, :one),
"one was considered invalid")
- assert(parser.valid_attr?(:record, :ensure),
+ assert(@parser.valid_attr?(:record, :ensure),
"ensure was considered invalid")
- assert(! parser.valid_attr?(:record, :four),
+ assert(! @parser.valid_attr?(:record, :four),
"four was considered valid")
end
def test_record_blocks
- parser = FParser.new
-
options = nil
assert_nothing_raised do
# Just do a simple test
- options = parser.record_line :record,
+ options = @parser.record_line :record,
:fields => %w{name alias info} do |line|
line = line.dup
ret = {}
@@ -423,9 +408,6 @@ billy three four\n"
end
end
- assert(parser.respond_to?(:handle_record_line_record),
- "Parser did not define record method")
-
values = {
:name => "tcpmux",
:description => "TCP port service multiplexer",
@@ -446,7 +428,7 @@ billy three four\n"
}.each do |line, should|
result = nil
assert_nothing_raised do
- result = parser.handle_record_line(line, options)
+ result = @parser.handle_record_line(line, options)
end
assert(result, "Did not get a result back for '%s'" % line)
should.each do |field|
@@ -466,10 +448,8 @@ billy three four\n"
# Make sure we correctly handle optional fields. We'll skip this
# functionality until we really know we need it.
def test_optional_fields
- parser = FParser.new
-
assert_nothing_raised do
- parser.record_line :record,
+ @parser.record_line :record,
:fields => %w{one two three four},
:optional => %w{three four},
:absent => "*",
@@ -479,13 +459,13 @@ billy three four\n"
["a b c d", "a b * d", "a b * *", "a b c *"].each do |line|
record = nil
assert_nothing_raised do
- record = parser.parse_line(line)
+ record = @parser.parse_line(line)
end
# Now regenerate the line
newline = nil
assert_nothing_raised do
- newline = parser.to_line(record)
+ newline = @parser.to_line(record)
end
# And make sure they're equal
@@ -494,41 +474,39 @@ billy three four\n"
# Now make sure it pukes if we don't provide the required fields
assert_raise(ArgumentError) do
- parser.to_line(:record_type => :record, :one => "yay")
+ @parser.to_line(:record_type => :record, :one => "yay")
end
end
def test_record_rts
- parser = FParser.new
-
# Start with the default
assert_nothing_raised do
- parser.record_line :record,
+ @parser.record_line :record,
:fields => %w{one two three four},
:optional => %w{three four}
end
assert_equal("a b ",
- parser.to_line(:record_type => :record, :one => "a", :two => "b")
+ @parser.to_line(:record_type => :record, :one => "a", :two => "b")
)
# Now say yes to removing
- parser.clear_records
+ @parser.clear_records
assert_nothing_raised do
- parser.record_line :record,
+ @parser.record_line :record,
:fields => %w{one two three four},
:optional => %w{three four},
:rts => true
end
assert_equal("a b",
- parser.to_line(:record_type => :record, :one => "a", :two => "b")
+ @parser.to_line(:record_type => :record, :one => "a", :two => "b")
)
# Lastly, try a regex
- parser.clear_records
+ @parser.clear_records
assert_nothing_raised do
- parser.record_line :record,
+ @parser.record_line :record,
:fields => %w{one two three four},
:optional => %w{three four},
:absent => "*",
@@ -536,9 +514,138 @@ billy three four\n"
end
assert_equal("a b",
- parser.to_line(:record_type => :record, :one => "a", :two => "b")
+ @parser.to_line(:record_type => :record, :one => "a", :two => "b")
)
end
+
+ # Make sure the last field can contain the separator, as crontabs do, and
+ # that we roll them all up by default.
+ def test_field_rollups
+ @parser.record_line :yes, :fields => %w{name one two}
+
+ result = nil
+ assert_nothing_raised do
+ result = @parser.send(:parse_line, "Name One Two Three")
+ end
+ assert_equal("Two Three", result[:two],
+ "Did not roll up last fields by default")
+
+ @parser = FParser.new
+ assert_nothing_raised("Could not create record that rolls up fields") do
+ @parser.record_line :no, :fields => %w{name one two}, :rollup => false
+ end
+
+ result = nil
+ assert_nothing_raised do
+ result = @parser.send(:parse_line, "Name One Two Three")
+ end
+ assert_equal("Two", result[:two],
+ "Rolled up last fields when rollup => false")
+ end
+
+ def test_text_blocks
+ record = nil
+ assert_nothing_raised do
+ record = @parser.text_line :name, :match => %r{^#} do |line|
+ {:line => line.upcase}
+ end
+ end
+
+ assert(record.respond_to?(:process),
+ "Block was not used with text line")
+
+ assert_equal("YAYNESS", record.process("yayness")[:line],
+ "Did not call process method")
+ end
+
+ def test_hooks
+ record = nil
+ # First try it with a normal record
+ 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 }
+ end
+
+ assert(record.respond_to?(:post_parse), "did not create method for post-hook")
+ assert(record.respond_to?(:pre_gen), "did not create method for pre-hook")
+
+ result = nil
+ assert_nothing_raised("Could not process line with hooks") do
+ result = @parser.parse_line("one two")
+ end
+
+ assert(result[:posted], "Did not run post-hook")
+ old_result = result
+
+ # Now make sure our pre-gen hook is called
+ 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("one", old_result[:one], "passed original hash to pre hook")
+ end
+end
+
+class TestUtilFileRecord < Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::FileParsing
+
+ Record = Puppet::Util::FileParsing::FileRecord
+ def test_new_filerecord
+ [ [:fake, {}],
+ [nil, ]
+ ].each do |args|
+ assert_raise(ArgumentError, "Did not fail on %s" % args.inspect) do
+ Record.new(*args)
+ end
+ end
+
+ # Make sure the fields get turned into symbols
+ record = nil
+ assert_nothing_raised do
+ record = Record.new(:record, :fields => %w{one two})
+ end
+ assert_equal([:one, :two], record.fields,
+ "Did not symbolize fields")
+
+ # Make sure we fail on invalid fields
+ [:record_type, :target, :on_disk].each do |field|
+ assert_raise(ArgumentError, "Did not fail on invalid field %s" % field) {
+ Record.new(:record, :fields => [field])
+ }
+ end
+ end
+
+ def test_defaults
+ record = Record.new(:text, :match => %r{^#})
+ [:absent, :separator, :joiner, :optional].each do |field|
+ assert_nil(record.send(field), "%s was not nil" % field)
+ end
+
+ record = Record.new(:record, :fields => %w{fields})
+ {:absent => "", :separator => /\s+/, :joiner => " ",
+ :optional => []}.each do |field, default|
+ assert_equal(default, record.send(field), "%s was not default" % field)
+ end
+ end
+
+ def test_block
+ record = nil
+ assert_nothing_raised("Could not pass a block when creating record") do
+ record = Record.new(:record, :fields => %w{one}) do |line|
+ return line.upcase
+ end
+ end
+
+ line = "This is a line"
+
+ assert(record.respond_to?(:process),
+ "Record did not define :process method")
+
+ assert_equal(line.upcase, record.process(line),
+ "Record did not process line correctly")
+ end
end
# $Id$