summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/metatype/evaluation.rb101
-rw-r--r--lib/puppet/metatype/providers.rb5
-rwxr-xr-xlib/puppet/provider/mount/parsed.rb1
-rwxr-xr-xlib/puppet/provider/parsedfile.rb256
-rw-r--r--lib/puppet/transaction.rb1
-rwxr-xr-xlib/puppet/type/mount.rb10
-rwxr-xr-xlib/puppet/type/parsedtype.rb11
-rw-r--r--lib/puppet/util/classgen.rb17
-rw-r--r--lib/puppet/util/fileparsing.rb5
-rwxr-xr-xtest/providers/parsedfile.rb434
-rwxr-xr-xtest/types/mount.rb3
-rwxr-xr-xtest/util/classgen.rb8
12 files changed, 702 insertions, 150 deletions
diff --git a/lib/puppet/metatype/evaluation.rb b/lib/puppet/metatype/evaluation.rb
index dde86a28d..30a3e7a80 100644
--- a/lib/puppet/metatype/evaluation.rb
+++ b/lib/puppet/metatype/evaluation.rb
@@ -1,51 +1,4 @@
class Puppet::Type
- # retrieve the current value of all contained states
- def retrieve
- # it's important to use the method here, as it follows the order
- # in which they're defined in the object
- states().each { |state|
- state.retrieve
- }
- end
-
- # Retrieve the changes associated with all of the states.
- def statechanges
- # If we are changing the existence of the object, then none of
- # the other states matter.
- changes = []
- if @states.include?(:ensure) and ! @states[:ensure].insync?
- #self.info "ensuring %s from %s" %
- # [@states[:ensure].should, @states[:ensure].is]
- changes = [Puppet::StateChange.new(@states[:ensure])]
- # Else, if the 'ensure' state is correctly absent, then do
- # nothing
- elsif @states.include?(:ensure) and @states[:ensure].is == :absent
- #self.info "Object is correctly absent"
- return []
- else
- #if @states.include?(:ensure)
- # self.info "ensure: Is: %s, Should: %s" %
- # [@states[:ensure].is, @states[:ensure].should]
- #else
- # self.info "no ensure state"
- #end
- changes = states().find_all { |state|
- ! state.insync?
- }.collect { |state|
- Puppet::StateChange.new(state)
- }
- end
-
- if Puppet[:debug] and changes.length > 0
- self.debug("Changing " + changes.collect { |ch|
- ch.state.name
- }.join(",")
- )
- end
-
- changes
- end
-
# this method is responsible for collecting state changes
# we always descend into the children before we evaluate our current
# states
@@ -94,6 +47,13 @@ class Puppet::Type
return changes.flatten
end
+ # By default, try flushing the provider.
+ def flush
+ if self.provider and self.provider.respond_to?(:flush)
+ self.provider.flush
+ end
+ end
+
# if all contained objects are in sync, then we're in sync
# FIXME I don't think this is used on the type instances any more,
# it's really only used for testing
@@ -119,6 +79,53 @@ class Puppet::Type
#self.debug("%s sync status is %s" % [self,insync])
return insync
end
+
+ # retrieve the current value of all contained states
+ def retrieve
+ # it's important to use the method here, as it follows the order
+ # in which they're defined in the object
+ states().each { |state|
+ state.retrieve
+ }
+ end
+
+ # Retrieve the changes associated with all of the states.
+ def statechanges
+ # If we are changing the existence of the object, then none of
+ # the other states matter.
+ changes = []
+ if @states.include?(:ensure) and ! @states[:ensure].insync?
+ #self.info "ensuring %s from %s" %
+ # [@states[:ensure].should, @states[:ensure].is]
+ changes = [Puppet::StateChange.new(@states[:ensure])]
+ # Else, if the 'ensure' state is correctly absent, then do
+ # nothing
+ elsif @states.include?(:ensure) and @states[:ensure].is == :absent
+ #self.info "Object is correctly absent"
+ return []
+ else
+ #if @states.include?(:ensure)
+ # self.info "ensure: Is: %s, Should: %s" %
+ # [@states[:ensure].is, @states[:ensure].should]
+ #else
+ # self.info "no ensure state"
+ #end
+ changes = states().find_all { |state|
+ ! state.insync?
+ }.collect { |state|
+ Puppet::StateChange.new(state)
+ }
+ end
+
+ if Puppet[:debug] and changes.length > 0
+ self.debug("Changing " + changes.collect { |ch|
+ ch.state.name
+ }.join(",")
+ )
+ end
+
+ changes
+ end
end
# $Id$
diff --git a/lib/puppet/metatype/providers.rb b/lib/puppet/metatype/providers.rb
index c59b7f84a..b1ce820be 100644
--- a/lib/puppet/metatype/providers.rb
+++ b/lib/puppet/metatype/providers.rb
@@ -154,10 +154,13 @@ class Puppet::Type
def self.unprovide(name)
if @providers.has_key? name
+ rmclass(name,
+ :hash => @providers,
+ :prefix => "Provider"
+ )
if @defaultprovider and @defaultprovider.name == name
@defaultprovider = nil
end
- @providers.delete(name)
end
end
diff --git a/lib/puppet/provider/mount/parsed.rb b/lib/puppet/provider/mount/parsed.rb
index 3db4d7ac2..086cd5cd9 100755
--- a/lib/puppet/provider/mount/parsed.rb
+++ b/lib/puppet/provider/mount/parsed.rb
@@ -156,6 +156,7 @@ Puppet::Type.type(:mount).provide :parsed, :parent => Puppet::Provider::ParsedFi
when "Solaris": df = "#{command(:df)} -k"
end
%x{#{df}}.split("\n").find do |line|
+ p line
fs = line.split(/\s+/)[-1]
if platform == "Darwin"
fs == "/private/var/automount" + @model[:path] or
diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb
index 35267c673..f9e98d96a 100755
--- a/lib/puppet/provider/parsedfile.rb
+++ b/lib/puppet/provider/parsedfile.rb
@@ -1,44 +1,58 @@
require 'puppet'
-
+require 'puppet/util/fileparsing'
+
+# This provider can be used as the parent class for a provider that
+# parses and generates files. Its content must be loaded via the
+# 'prefetch' method, and the file will be written when 'flush' is called
+# on the provider instance. At this point, the file is written once
+# for every provider instance.
+#
+# Once the provider prefetches the data, it's the model's job to copy
+# that data over to the @is variables.
class Puppet::Provider::ParsedFile < Puppet::Provider
+ extend Puppet::Util::FileParsing
+
class << self
- attr_accessor :filetype, :fields
- attr_reader :path
- attr_writer :fileobj
+ attr_reader :filetype
+ attr_accessor :default_target
end
- # Override 'newstate' so that all states default to having the
- # correct parent type
- def self.newstate(name, options = {}, &block)
- options[:parent] ||= Puppet::State::ParsedParam
- super(name, options, &block)
- end
+ attr_accessor :state_hash
- # Add another type var.
- def self.initvars
- @instances = []
- super
+ def self.clear
+ @target_objects.clear
+ @records.clear
end
- # Add a non-object comment or whatever to our list of instances
- def self.comment(line)
- @instances << line
+ def self.filetype=(type)
+ if type.is_a?(Class)
+ @filetype = type
+ elsif klass = Puppet::FileType.filetype(type)
+ @filetype = klass
+ else
+ raise ArgumentError, "Invalid filetype %s" % type
+ end
end
- # Override the default Puppet::Type method, because instances
- # also need to be deleted from the @instances hash
- def self.delete(child)
- if @instances.include?(child)
- @instances.delete(child)
+ # Flush all of the targets for which there are modified records.
+ def self.flush(record)
+ return unless defined?(@modified) and ! @modified.empty?
+
+ # Make sure this record is on the list to be flushed.
+ unless record[:on_disk]
+ record[:on_disk] = true
+ @records << record
end
- super
- end
- # Initialize the object if necessary.
- def self.fileobj
- @fileobj ||= @filetype.new(@path)
+ @modified.sort { |a,b| a.to_s <=> b.to_s }.uniq.each do |target|
+ Puppet.debug "Flushing %s provider target %s" % [@model.name, target]
+ flush_target(target)
+ end
+ end
- @fileobj
+ # Flush all of the records relating to a specific target.
+ def self.flush_target(target)
+ target_object(target).write(to_file(target_records(target)))
end
# Return the header placed at the top of each generated file, warning
@@ -49,24 +63,93 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
# HEADER: is definitely not recommended.\n}
end
- # Parse a file
- #
- # Subclasses must override this method.
- def self.parse(text)
- raise Puppet::DevError, "Parse was not overridden in %s" %
- self.name
+ # Add another type var.
+ def self.initvars
+ @records = []
+
+ # Default to flat files
+ @filetype = Puppet::FileType.filetype(:flat)
+ super
+ end
+
+ # Create attribute methods for each of the model's non-metaparam attributes.
+ def self.model=(model)
+ [model.validstates, model.parameters].flatten.each do |attr|
+ define_method(attr) do @state_hash[attr] end
+
+ define_method(attr.to_s + "=") do |val|
+ # Mark that this target was modified.
+ modeltarget = @model[:target]
+
+ # If they're the same, then just mark that one as modified
+ if @state_hash[:target] and @state_hash[:target] == modeltarget
+ self.class.modified(modeltarget)
+ else
+ # Always mark the modeltarget as modified, and if there's
+ # and old state_hash target, mark it as modified and replace
+ # it.
+ self.class.modified(modeltarget)
+ if @state_hash[:target]
+ self.class.modified(@state_hash[:target])
+ end
+ @state_hash[:target] = modeltarget
+ end
+ @state_hash[attr] = val.to_s
+ end
+ end
+ @model = model
+ end
+
+ # Mark a target as modified so we know to flush it. This only gets
+ # used within the attr= methods.
+ def self.modified(target)
+ @modified ||= []
+ @modified << target
+ end
+
+ # Retrieve all of the data from disk. There are three ways to know
+ # while files to retrieve: We might have a list of file objects already
+ # set up, there might be instances of our associated model and they
+ # will have a path parameter set, and we will have a default path
+ # set. We need to turn those three locations into a list of files,
+ # prefetch each one, and make sure they're associated with each appropriate
+ # model instance.
+ def self.prefetch
+ # Reset the record list.
+ @records = []
+ targets().each do |target|
+ prefetch_target(target)
+ end
end
- # If they change the path, we need to get rid of our cache object
- def self.path=(path)
- @fileobj = nil
- @path = path
+ # Prefetch an individual target.
+ def self.prefetch_target(target)
+ @records += retrieve(target).each do |r|
+ r[:on_disk] = true
+ r[:target] = target
+ end
+
+ # Retrieve the current state of this target.
+ target_records(target).find_all { |i| i.is_a?(Hash) }.each do |record|
+ # Find any model instances whose names match our instances.
+ if instance = self.model[record[:name]]
+ instance.provider.state_hash = record
+ elsif self.respond_to?(:match)
+ if instance = self.match(record)
+ record[:name] = instance[:name]
+ instance.provider.state_hash = record
+ end
+ end
+ # Any unmatched records are assumed to be unmanaged, so
+ # we just leave them alone.
+ end
end
# Retrieve the text for the file. Returns nil in the unlikely
# event that it doesn't exist.
- def self.retrieve
- text = fileobj.read
+ def self.retrieve(path)
+ # XXX We need to be doing something special here in case of failure.
+ text = target_object(path).read
if text.nil? or text == ""
# there is no file
return []
@@ -76,73 +159,68 @@ class Puppet::Provider::ParsedFile < Puppet::Provider
end
# Write out the file.
- def self.store(instances)
- if instances.empty?
- Puppet.notice "No %s instances for %s" % [self.name, @path]
+ def self.store(records)
+ if records.empty?
+ Puppet.notice "No %s records for %s" % [self.name, @path]
else
- fileobj.write(self.to_file(instances))
+ target_object.write(self.to_file(instances))
end
end
- # Collect all Host instances convert them into literal text.
- def self.to_file(instances)
- str = self.header()
- unless instances.empty?
- # Reject empty hashes and those with :ensure == :absent
- str += instances.reject { |obj|
- obj.is_a? Hash and (obj.empty? or obj[:ensure] == :absent)
- }.collect { |obj|
- # If it's a hash, convert it, otherwise just write it out
- if obj.is_a?(Hash)
- to_record(obj)
- else
- obj.to_s
- end
- }.join("\n") + "\n"
+ # Initialize the object if necessary.
+ def self.target_object(target)
+ @target_objects ||= {}
+ @target_objects[target] ||= @filetype.new(target)
- return str
- else
- Puppet.notice "No %s instances" % self.name
- return ""
- end
+ @target_objects[target]
end
- # A Simple wrapper method that subclasses can override, so there's more control
- # over how instances are retrieved.
- def allinstances
- self.class.retrieve
+ # Find all of the records for a given target
+ def self.target_records(target)
+ @records.find_all { |r| r[:target] == target }
end
- def clear
- super
- @instances = nil
- end
+ # Find a list of all of the targets that we should be reading. This is
+ # used to figure out what targets we need to prefetch.
+ def self.targets
+ targets = []
+ # First get the default target
+ unless self.default_target
+ raise Puppet::DevError, "Parsed Providers must define a default target"
+ end
+ targets << self.default_target
- # Return a hash that maps to our info, if possible.
- def hash
- @instances = allinstances()
+ # Then get each of the file objects
+ targets += @target_objects.keys
- namevar = @model.class.namevar
- if @instances and h = @instances.find do |o|
- o.is_a? Hash and o[namevar] == @model[namevar]
- end
- @me = h
- else
- @me = {}
- if @instances.empty?
- @instances = [@me]
- else
- @instances << @me
- end
+ # Lastly, check the file from any model instances
+ self.model.each do |model|
+ targets << model[:target]
end
- return @me
+ targets.uniq
+ end
+
+ # Write our data to disk.
+ def flush
+ # Make sure we've got a target and name set.
+ @state_hash[:target] ||= @model[:target]
+ @state_hash[:name] ||= @model[:name]
+
+ self.class.flush(@state_hash)
end
def initialize(model)
super
- @instances = nil
+ # Initialize our data hash with the record type. The record type
+ # in the subclass has to create a record matching its name.
+ @state_hash = {:record_type => self.class.name}
+ end
+
+ # Have we been modified since the last flush?
+ def modified?
+ @state[:flush_needed]
end
def store(hash = nil)
diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index 05fbe6914..a7ea48a19 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -262,6 +262,7 @@ class Transaction
nil
end
}.reject { |o| o.nil? }.uniq.each do |klass|
+ # XXX We need to do something special here in case of failure.
if klass.respond_to?(:prefetch)
klass.prefetch
end
diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb
index b92ab735b..259d2f894 100755
--- a/lib/puppet/type/mount.rb
+++ b/lib/puppet/type/mount.rb
@@ -13,6 +13,9 @@ module Puppet
is entered into the mount table and mounted."
newvalue(:present, :event => :mount_created) do
+ # The parsedtype nature of the provider automatically
+ # creates the mount in the file, and we're not mounting,
+ # so we don't do anything here.
end
newvalue(:absent, :event => :mount_deleted) do
@@ -37,12 +40,15 @@ module Puppet
end
def retrieve
+ fail "called retrieve"
+ Puppet.warning @is.inspect
if provider.mounted?
- return :mounted
+ @is = :mounted
else
val = super()
- return val
+ @is = val
end
+
end
end
diff --git a/lib/puppet/type/parsedtype.rb b/lib/puppet/type/parsedtype.rb
index 4297946a9..607134c46 100755
--- a/lib/puppet/type/parsedtype.rb
+++ b/lib/puppet/type/parsedtype.rb
@@ -120,6 +120,8 @@ module Puppet
obj.is = [param, value]
}
end
+
+ return obj
end
# Override 'newstate' so that all states default to having the
@@ -130,14 +132,15 @@ module Puppet
end
def self.list
- suitableprovider.collect do |provider|
- puts provider.name
- provider.retrieve.collect { |i| p i; i.is_a? Hash }.collect { |i| hash2obj(i) }
+ ret = suitableprovider.collect do |provider|
+ provider.retrieve.find_all { |i| i.is_a? Hash }.collect { |i| hash2obj(i) }
end.flatten
end
def self.listbyname
- retrieve.collect { |i| i.is_a? Hash }.collect { |i| i[:name] }
+ suitableprovider.collect do |provider|
+ provider.retrieve.find_all { |i| i.is_a? Hash }.collect { |i| i[:name] }
+ end.flatten
end
# Make sure they've got an explicit :ensure class.
diff --git a/lib/puppet/util/classgen.rb b/lib/puppet/util/classgen.rb
index 8fa2a34cf..0c774eecd 100644
--- a/lib/puppet/util/classgen.rb
+++ b/lib/puppet/util/classgen.rb
@@ -50,7 +50,7 @@ module Puppet::Util::ClassGen
# Remove an existing class
def rmclass(name, options)
options = symbolize_options(options)
- const = name2const(name)
+ const = genconst_string(name, options)
retval = false
if const_defined? const
remove_const(const)
@@ -68,6 +68,16 @@ module Puppet::Util::ClassGen
private
+ # Generate the constant to create or remove.
+ def genconst_string(name, options)
+ unless const = options[:constant]
+ prefix = options[:prefix] || ""
+ const = prefix + name2const(name)
+ end
+
+ return const
+ end
+
# This does the actual work of creating our class or module. It's just a
# slightly abstract version of genclass.
def genthing(name, type, options, block)
@@ -120,10 +130,7 @@ module Puppet::Util::ClassGen
# Handle the setting and/or removing of the associated constant.
def handleclassconst(klass, name, options)
- unless const = options[:constant]
- prefix = options[:prefix] || ""
- const = prefix + name2const(name)
- end
+ const = genconst_string(name, options)
if const_defined? const
if options[:overwrite]
diff --git a/lib/puppet/util/fileparsing.rb b/lib/puppet/util/fileparsing.rb
index e4998cf7e..705192b11 100644
--- a/lib/puppet/util/fileparsing.rb
+++ b/lib/puppet/util/fileparsing.rb
@@ -108,10 +108,11 @@ 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 r == :record_type
- raise ArgumentError.new("Cannot have fields named record_type")
+ if invalidfields.include?(r)
+ raise ArgumentError.new("Cannot have fields named %s" % r)
end
r
end
diff --git a/test/providers/parsedfile.rb b/test/providers/parsedfile.rb
new file mode 100755
index 000000000..f7e5a4d48
--- /dev/null
+++ b/test/providers/parsedfile.rb
@@ -0,0 +1,434 @@
+#!/usr/bin/env ruby
+
+$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppettest'
+require 'puppettest/fileparsing'
+require 'puppet'
+require 'puppet/provider/parsedfile'
+require 'facter'
+
+class TestParsedFile < Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::FileParsing
+
+ Puppet::Type.newtype(:parsedfiletype) do
+ newstate(:one) do
+ newvalue(:a) {}
+ newvalue(:b) {}
+ end
+ newstate(:two) do
+ newvalue(:c) {}
+ newvalue(:d) {}
+ end
+
+ newparam(:name) do
+ end
+
+ newparam(:target) do
+ defaultto { @parent.class.defaultprovider.default_target }
+ end
+ end
+
+ # A simple block to skip the complexity of a full transaction.
+ def apply(model)
+ [:one, :two].each do |st|
+ model.provider.send(st.to_s + "=", model.should(st))
+ end
+ end
+
+ def mkmodel(name, options = {})
+ options[:one] ||= "a"
+ options[:two] ||= "c"
+ options[:name] ||= name
+
+ model = @type.create(options)
+ end
+
+ def mkprovider(name = :parsed)
+ @provider = @type.provide(name, :parent => Puppet::Provider::ParsedFile) do
+ record_line name, :fields => %w{name one two}
+ end
+ end
+
+ def setup
+ super
+ @type = Puppet::Type.type(:parsedfiletype)
+ end
+
+ def teardown
+ if defined? @provider
+ @type.unprovide(@provider.name)
+ @provider = nil
+ end
+ super
+ end
+
+ def test_create_provider
+ assert_nothing_raised do
+ mkprovider
+ end
+ end
+
+ def test_model_attributes
+ prov = nil
+ assert_nothing_raised do
+ prov = mkprovider
+ end
+
+ [:one, :two, :name].each do |attr|
+ assert(prov.method_defined?(attr), "Did not define %s" % attr)
+ end
+
+ # Now make sure they stay around
+ fakemodel = fakemodel(:parsedfiletype, "yay")
+
+ file = prov.new(fakemodel)
+
+ assert_nothing_raised do
+ file.name = :yayness
+ end
+
+ # The provider converts to strings
+ assert_equal("yayness", file.name)
+ end
+
+ def test_filetype
+ prov = mkprovider
+
+ flat = Puppet::FileType.filetype(:flat)
+ ram = Puppet::FileType.filetype(:ram)
+ assert_nothing_raised do
+ prov.filetype = :flat
+ end
+
+ assert_equal(flat, prov.filetype)
+
+ assert_nothing_raised do
+ prov.filetype = ram
+ end
+ assert_equal(ram, prov.filetype)
+ end
+
+ # Make sure we correctly create a new filetype object, but only when
+ # necessary.
+ def test_fileobject
+ prov = mkprovider
+
+ path = tempfile()
+ obj = nil
+ assert_nothing_raised do
+ obj = prov.target_object(path)
+ end
+
+ # The default filetype is 'flat'
+ assert_instance_of(Puppet::FileType.filetype(:flat), obj)
+
+ newobj = nil
+ assert_nothing_raised do
+ newobj = prov.target_object(path)
+ end
+
+ assert_equal(obj, newobj, "did not reuse file object")
+
+ # now make sure clear does the right thing
+ assert_nothing_raised do
+ prov.clear
+ end
+ assert_nothing_raised do
+ newobj = prov.target_object(path)
+ end
+
+ assert(obj != newobj, "did not reuse file object")
+ end
+
+ def test_retrieve
+ prov = mkprovider
+
+ prov.filetype = :ram
+
+ # Override the parse method with our own
+ prov.meta_def(:parse) do |text|
+ return [text]
+ end
+
+ path = :yayness
+ file = prov.target_object(path)
+ text = "a test"
+ file.write(text)
+
+ ret = nil
+ assert_nothing_raised do
+ ret = prov.retrieve(path)
+ end
+
+ assert_equal([text], ret)
+
+ # Now set the text to nil and make sure we get an empty array
+ file.write(nil)
+ assert_nothing_raised do
+ ret = prov.retrieve(path)
+ end
+
+ assert_equal([], ret)
+
+ # And the empty string should return an empty array
+ file.write("")
+ assert_nothing_raised do
+ ret = prov.retrieve(path)
+ end
+
+ assert_equal([], ret)
+ end
+
+ # Verify that prefetch will parse the file, create any necessary instances,
+ # and set the 'is' values appropriately.
+ def test_prefetch
+ prov = mkprovider
+
+ prov.filetype = :ram
+ prov.default_target = :default
+
+ # Create a couple of demo files
+ prov.target_object(:file1).write "bill b c"
+
+ prov.target_object(:file2).write "jill b d"
+
+ prov.target_object(:default).write "will b d"
+
+ # Create some models for some of those demo files
+ model = mkmodel "bill", :target => :file1
+ default = mkmodel "will", :target => :default
+
+ assert_nothing_raised do
+ prov.prefetch
+ end
+
+ # Make sure we prefetched our models.
+ assert_equal("b", model.provider.one)
+ assert_equal("b", default.provider.one)
+ assert_equal("d", default.provider.two)
+ end
+
+ # Make sure we can correctly prefetch on a target.
+ def test_prefetch_target
+ prov = mkprovider
+
+ prov.filetype = :ram
+ target = :yayness
+ prov.target_object(target).write "yay b d"
+
+ model = mkmodel "yay", :target => :yayness
+
+ assert_nothing_raised do
+ prov.prefetch_target(:yayness)
+ end
+
+ # Now make sure we correctly got the hash
+ mprov = model.provider
+ assert_equal("b", mprov.one)
+ assert_equal("d", mprov.two)
+ end
+
+ def test_prefetch_match
+ prov = mkprovider
+
+ prov.meta_def(:match) do |record|
+ # Look for matches on :one
+ self.model.find do |m|
+ m.should(:one).to_s == record[:one].to_s
+ end
+ end
+
+ prov.filetype = :ram
+ target = :yayness
+ prov.target_object(target).write "foo b d"
+
+ model = mkmodel "yay", :target => :yayness, :one => "b"
+
+ assert_nothing_raised do
+ prov.prefetch_target(:yayness)
+ end
+
+ # Now make sure we correctly got the hash
+ mprov = model.provider
+ assert_equal("yay", model[:name])
+ assert_equal("b", mprov.one)
+ assert_equal("d", mprov.two)
+ end
+
+ # We need to test that we're retrieving files from all three locations:
+ # from any existing target_objects, from the default file location, and
+ # from any existing model instances.
+ def test_targets
+ prov = mkprovider
+
+ files = {}
+
+ # Set the default target
+ default = tempfile()
+ files[:default] = default
+ prov.default_target = default
+
+ # Create a file object
+ inmem = tempfile()
+ files[:inmemory] = inmem
+ prov.target_object(inmem).write("inmem yay ness")
+
+ # Lastly, create a model
+ mtarget = tempfile()
+ files[:models] = mtarget
+ model = mkmodel "yay", :target => mtarget
+
+ assert(model[:target], "Did not get a value for target")
+
+ list = nil
+ assert_nothing_raised do
+ list = prov.targets
+ end
+
+ files.each do |name, file|
+ assert(list.include?(file), "Provider did not find %s file" % name)
+ end
+ end
+
+ # Make sure that flushing behaves correctly. This is what actually writes
+ # the data out to disk.
+ def test_flush
+ prov = mkprovider
+
+ prov.filetype = :ram
+ prov.default_target = :yayness
+
+ # Create some models.
+ one = mkmodel "one", :one => "a", :two => "c", :target => :yayness
+ two = mkmodel "two", :one => "b", :two => "d", :target => :yayness
+
+ # Write out a file with different data.
+ prov.target_object(:yayness).write "one b d\ntwo a c"
+
+ prov.prefetch
+
+ # Apply and flush the first model.
+ assert_nothing_raised do
+ apply(one)
+ end
+ assert_nothing_raised { one.flush }
+
+ # Make sure it changed our file
+ assert_equal("a", one.provider.one)
+ assert_equal("c", one.provider.two)
+
+ # And make sure it's right on disk
+ assert(prov.target_object(:yayness).read.include?("one a c"),
+ "Did not write out correct data")
+
+ # Make sure the second model has not been modified
+ assert_equal("a", two.provider.one, "Two was flushed early")
+ assert_equal("c", two.provider.two, "Two was flushed early")
+
+ # And on disk
+ assert(prov.target_object(:yayness).read.include?("two a c"),
+ "Wrote out other model")
+
+ # Now fetch the data again and make sure we're still right
+ assert_nothing_raised { prov.prefetch }
+ assert_equal("a", one.provider.one)
+ assert_equal("a", two.provider.one)
+
+ # Now flush the second model and make sure it goes well
+ assert_nothing_raised { apply(two) }
+ assert_nothing_raised { two.flush }
+
+ assert_equal("b", two.provider.one)
+ end
+
+ def test_creating_file
+ prov = mkprovider
+
+ prov.filetype = :ram
+ prov.default_target = :basic
+
+ model = mkmodel "yay", :target => :basic, :one => "a", :two => "c"
+
+ apply(model)
+
+ assert_nothing_raised do
+ model.flush
+ end
+
+ assert(prov.target_object(:basic).read.include?("yay a c"),
+ "Did not create file")
+
+ # Make a change
+ model.provider.one = "b"
+
+ # Flush it
+ assert_nothing_raised do
+ model.flush
+ end
+
+ # And make sure our model doesn't appear twice in the file.
+ assert_equal("yay b c\n",
+ prov.target_object(:basic).read)
+ end
+
+ # Make sure a record can switch targets.
+ def test_switching_targets
+ prov = mkprovider
+
+ prov.filetype = :ram
+ prov.default_target = :first
+
+ # Make three models, one for each target and one to switch
+ first = mkmodel "first", :target => :first
+ second = mkmodel "second", :target => :second
+ mover = mkmodel "mover", :target => :first
+
+ # Apply them all
+ [first, second, mover].each do |m|
+ assert_nothing_raised("Could not apply %s" % m[:name]) do
+ apply(m)
+ end
+ end
+
+ # Flush
+ assert_nothing_raised do
+ [first, second, mover].each do |m| m.flush end
+ end
+
+ check = proc do |target, name|
+ assert(prov.target_object(target).read.include?("%s a c" % name),
+ "Did not sync %s" % name)
+ end
+ # Make sure the data is there
+ check.call(:first, :first)
+ check.call(:second, :second)
+ check.call(:first, :mover)
+
+ # Now change the target for the mover
+ mover[:target] = :second
+
+ # Apply it
+ assert_nothing_raised do
+ apply(mover)
+ end
+
+ # Flush
+ assert_nothing_raised do
+ mover.flush
+ end
+
+ # Make sure the data is there
+ check.call(:first, :first)
+ check.call(:second, :second)
+ check.call(:second, :mover)
+
+ # And make sure the mover is no longer in the first file
+ assert(prov.target_object(:first) !~ /mover/,
+ "Mover was not removed from first file")
+ end
+end
+
+# $Id$
+
diff --git a/test/types/mount.rb b/test/types/mount.rb
index 2b69e73dd..880de4434 100755
--- a/test/types/mount.rb
+++ b/test/types/mount.rb
@@ -147,6 +147,9 @@ class TestMounts < Test::Unit::TestCase
assert_events([], obj)
obj.retrieve
+ assert_equal(:mounted, obj.is(:ensure))
+
+ obj.retrieve
assert(obj.provider.mounted?, "Object is not mounted")
end
diff --git a/test/util/classgen.rb b/test/util/classgen.rb
index 6f935c010..778bdbb4f 100755
--- a/test/util/classgen.rb
+++ b/test/util/classgen.rb
@@ -214,6 +214,14 @@ class TestPuppetUtilClassGen < Test::Unit::TestCase
assert(array.include?(mod),
"Class did not get added to array")
end
+
+ def test_genconst_string
+ const = nil
+ assert_nothing_raised do
+ const = GenTest.send(:genconst_string, :testing, :prefix => "Yayness")
+ end
+ assert_equal("YaynessTesting", const)
+ end
end
# $Id$