diff options
-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$ |