diff options
author | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-11-11 07:40:16 +0000 |
---|---|---|
committer | luke <luke@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-11-11 07:40:16 +0000 |
commit | 0643113a7a8127ce559aa0cce0b81df5e99d386c (patch) | |
tree | 15e693db02840a656ed8b43027fcb55d257d7c8c | |
parent | 7c8614b0589f7c843d17b9d16720419817394cee (diff) | |
download | puppet-0643113a7a8127ce559aa0cce0b81df5e99d386c.tar.gz puppet-0643113a7a8127ce559aa0cce0b81df5e99d386c.tar.xz puppet-0643113a7a8127ce559aa0cce0b81df5e99d386c.zip |
An intermediate commit. All of the classes that use parsedfile are assuredly broken, since I have basically completely rewritten it. These classes have been a thorn in my side almost since I created them, yet they have been significantly less functional that I wanted. So, I decided to do the rewrite I have been putting off, just to spend all of the maintenance time now so I do not spend 3 days on them every release.
Tomorrow I will be porting all of the existing types (including cron, hopefully) over to this new base. This will also make it possible to add other types of providers to these classes; we should be able to reuse the netinfo provider for os x types, and we should be able to create a cron provider that writes to /etc/crontab instead of user crontabs.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1856 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r-- | lib/puppet/metatype/evaluation.rb | 101 | ||||
-rw-r--r-- | lib/puppet/metatype/providers.rb | 5 | ||||
-rwxr-xr-x | lib/puppet/provider/mount/parsed.rb | 1 | ||||
-rwxr-xr-x | lib/puppet/provider/parsedfile.rb | 256 | ||||
-rw-r--r-- | lib/puppet/transaction.rb | 1 | ||||
-rwxr-xr-x | lib/puppet/type/mount.rb | 10 | ||||
-rwxr-xr-x | lib/puppet/type/parsedtype.rb | 11 | ||||
-rw-r--r-- | lib/puppet/util/classgen.rb | 17 | ||||
-rw-r--r-- | lib/puppet/util/fileparsing.rb | 5 | ||||
-rwxr-xr-x | test/providers/parsedfile.rb | 434 | ||||
-rwxr-xr-x | test/types/mount.rb | 3 | ||||
-rwxr-xr-x | test/util/classgen.rb | 8 |
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$ |