#!/usr/bin/env ruby
$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
require 'mocha'
require 'puppettest/fileparsing'
require 'puppet/util/filetype'
require 'puppet/provider/parsedfile'
require 'facter'
class TestParsedFile < Test::Unit::TestCase
include PuppetTest
include PuppetTest::FileParsing
Puppet::Type.newtype(:testparsedfiletype) do
ensurable
newproperty(:one) do
newvalue(:a)
newvalue(:b)
end
newproperty(:two) do
newvalue(:c)
newvalue(:d)
end
newparam(:name) do
end
# The target should always be a property, not a parameter.
newproperty(:target) do
defaultto { @resource.class.defaultprovider.default_target }
end
end
# A simple block to skip the complexity of a full transaction.
def apply(resource)
[:one, :two, :ensure, :target].each do |st|
Puppet.info "Setting %s: %s => %s" %
[resource[:name], st, resource.should(st)]
resource.provider.send(st.to_s + "=", resource.should(st))
end
end
def mkresource(name, options = {})
options[:one] ||= "a"
options[:two] ||= "c"
options[:name] ||= name
resource = @type.create(options)
end
def mkprovider(name = :parsed)
@provider = @type.provide(name, :parent => Puppet::Provider::ParsedFile,
:filetype => :ram, :default_target => "yayness") do
record_line name, :fields => %w{name one two}
end
end
def setup
super
@type = Puppet::Type.type(:testparsedfiletype)
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_resource_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
fakeresource = fakeresource(:testparsedfiletype, "yay")
file = prov.new(fakeresource)
assert(file, "Could not make provider")
assert_nothing_raised("Could not set provider name") do
file.name = :yayness
end
# The provider converts to strings
assert_equal(:yayness, file.name)
end
def test_filetype
prov = mkprovider
flat = Puppet::Util::FileType.filetype(:flat)
ram = Puppet::Util::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 'ram'
assert_instance_of(Puppet::Util::FileType.filetype(:ram), 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\njill b d"
prov.target_object(:default).write "will b d\n"
# Create some resources for some of those demo files
bill = mkresource "bill", :target => :file1, :one => "b", :two => "c"
will = mkresource "will", :target => :default, :one => "b", :two => "d"
resources = {"bill" => bill, "will" => will}
prov_ids = {"bill" => bill.provider.object_id, "will" => will.provider.object_id}
assert_nothing_raised do
prov.prefetch(resources)
end
assert(bill.provider.object_id != prov_ids["bill"], "provider was not replaced in resource")
assert(will.provider.object_id != prov_ids["will"], "provider was not replaced in resource")
# Make sure we prefetched our resources.
assert_equal("b", bill.provider.one, "did not prefetch resource from file1")
assert_equal("c", bill.provider.two, "did not prefetch resource from file1")
assert_equal("b", will.provider.one, "did not prefetch resource from default")
assert_equal("d", will.provider.two, "did not prefetch resource from default")
# Now modify our resources and write them out, making sure that prefetching
# hasn't somehow destroyed this ability
bill[:one] = "a"
will[:one] = "a"
assert_apply(bill)
assert_apply(will)
prov.prefetch(resources)
assert_equal("a", bill.provider.one, "did not prefetch resource from file1")
assert_equal("a", will.provider.one, "did not prefetch resource from default")
assert_equal("bill a c\njill b d\n", prov.target_object(:file1).read,
"Did not write changed resource correctly")
assert_equal("will a d\n", prov.target_object(:default).read,
"Did not write changed default resource correctly")
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"
records = nil
assert_nothing_raised do
records = prov.prefetch_target(:yayness)
end
# Now make sure we correctly got the hash
record = records.find { |r| r[:name] == "yay" }
assert(record, "Did not get record in prefetch_target")
assert_equal("b", record[:one])
assert_equal("d", record[:two])
end
def test_prefetch_match
prov = mkprovider
prov.meta_def(:match) do |record, resources|
# Look for matches on :one
if res = resources.find { |name, resource| resource.should(:one).to_s == record[:one].to_s }
res[1]
else
nil
end
end
prov.filetype = :ram
target = :yayness
prov.target_object(target).write "foo b d"
resource = mkresource "yay", :target => :yayness, :one => "b"
assert_nothing_raised do
prov.prefetch("yay" => resource)
end
# Now make sure we correctly got the hash
mprov = resource.provider
assert_equal("yay", resource[: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 resource 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 resource with separate is and should values
mtarget = tempfile()
files[:resources] = mtarget
resource = mkresource "yay", :target => mtarget
assert(resource.should(:target), "Did not get a value for target")
list = nil
# First run it without the resource
assert_nothing_raised do
list = prov.targets
end
# Make sure it got the first two, but not the resources file
files.each do |name, file|
if name == :resources
assert(! list.include?(file), "Provider somehow found resource target when no resource was passed")
else
assert(list.include?(file), "Provider did not find %s file" % name)
end
end
# Now list with the resource passed
assert_nothing_raised do
list = prov.targets("yay" => resource)
end
# And make sure we get all three files
files.each do |name, file|
assert(list.include?(file), "Provider did not find %s file when resource was passed" % 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 resources.
one = mkresource "one", :one => "a", :two => "c", :target => :yayness
two = mkresource "two", :one => "b", :two => "d", :target => :yayness
resources = {"one" => one, "two" => two}
# Write out a file with different data.
prov.target_object(:yayness).write "one b d\ntwo a c"
prov.prefetch(resources)
# Apply and flush the first resource.
assert_nothing_raised do
apply(one)
end
assert_nothing_raised { one.flush }
# Make sure it didn't clear out our property hash
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 resource 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 resource")
# Now fetch the data again and make sure we're still right
assert_nothing_raised { prov.prefetch(resources) }
assert_equal("a", one.provider.one)
assert_equal("a", two.provider.one)
# Now flush the second resource and make sure it goes well
assert_nothing_raised { apply(two) }
assert_nothing_raised { two.flush }
# And make sure it didn't clear our hash
assert_equal(:b, two.provider.one)
end
# Make sure it works even if the file does not currently exist
def test_creating_file
prov = mkprovider
prov.clear
prov.default_target = :basic
resource = mkresource "yay", :target => :basic, :one => "a", :two => "c"
assert_equal(:present, resource.should(:ensure))
apply(resource)
assert_nothing_raised do
resource.flush
end
assert_equal("yay a c\n", prov.target_object(:basic).read,
"Did not create file")
# Make a change
resource.provider.one = "b"
# Flush it
assert_nothing_raised do
resource.flush
end
# And make sure our resource doesn't appear twice in the file.
assert_equal("yay b c\n", prov.target_object(:basic).read,
"Wrote record to file twice")
end
# Make sure a record can switch targets.
def test_switching_targets
prov = mkprovider
prov.filetype = :ram
prov.default_target = :first
# Make three resources, one for each target and one to switch
first = mkresource "first", :target => :first
second = mkresource "second", :target => :second
mover = mkresource "mover", :target => :first
[first, second, mover].each do |m|
assert_nothing_raised("Could not apply %s" % m[:name]) do
apply(m)
end
end
# Flush.
[first, second, mover].each do |m|
assert_nothing_raised do
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
# Make sure that 'ensure' correctly calls 'sync' on all properties.
def test_ensure
prov = mkprovider
prov.filetype = :ram
prov.default_target = :first
# Make two resources, one that starts on disk and one that doesn't
ondisk = mkresource "ondisk", :target => :first
notdisk = mkresource "notdisk", :target => :first
prov.target_object(:first).write "ondisk a c\n"
prov.prefetch("ondisk" => ondisk, "notdisk" => notdisk)
assert_equal(:present, notdisk.should(:ensure),
"Did not get default ensure value")
# Try creating the object
assert_nothing_raised { notdisk.provider.create() }
# Now make sure all of the data is copied over correctly.
notdisk.class.validproperties.each do |property|
assert_equal(notdisk.should(property), notdisk.provider.property_hash[property],
"%s was not copied over during creation" % property)
end
# Flush it to disk and make sure it got copied down
assert_nothing_raised do
notdisk.flush
end
assert(prov.target_object(:first).read =~ /^notdisk/,
"Did not write out object to disk")
assert(prov.target_object(:first).read =~ /^ondisk/,
"Lost object on disk")
# Make sure our on-disk resource behaves appropriately.
assert_equal(:present, ondisk.provider.ensure)
# Now destroy the object
assert_nothing_raised { notdisk.provider.destroy() }
assert_nothing_raised { notdisk.flush }
# And make sure it's no longer present
assert(prov.target_object(:first).read !~ /^notdisk/,
"Did not remove thing from disk")
assert(prov.target_object(:first).read =~ /^ondisk/,
"Lost object on disk")
assert_equal(:present, ondisk.provider.ensure)
end
def test_absent_fields
prov = @type.provide(:record, :parent => Puppet::Provider::ParsedFile) do
record_line :record, :fields => %w{name one two},
:separator => "\s"
end
cleanup { @type.unprovide(:record) }
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")
# 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
# This test is because in x2puppet I was having problems where multiple
# retrievals somehow destroyed the 'is' values.
def test_value_retrieval
prov = mkprovider
prov.default_target = :yayness
prov.target_object(:yayness).write "bill a c\njill b d"
list = @type.instances
bill = list.find { |r| r[:name] == "bill" }
jill = list.find { |r| r[:name] == "jill" }
assert(bill, "Could not find bill")
assert(jill, "Could not find jill")
prov = bill.provider
4.times do |i|
assert(prov.one, "Did not get a value for 'one' on try %s" % (i + 1))
end
# First make sure we can retrieve values multiple times from the
# provider
bills_values = nil
assert_nothing_raised do
bills_values = bill.retrieve
end
assert(bills_values[bill.property(:one)],
"Bill does not have a value for 'one'")
assert(bills_values[bill.property(:one)],
"Bill does not have a value for 'one' on second try")
assert_nothing_raised do
bill.retrieve
end
assert(bills_values[bill.property(:one)],
"bill's value for 'one' disappeared")
end
# Make sure that creating a new resource finds existing records in memory
def test_initialize_finds_records
prov = mkprovider
prov.default_target = :yayness
prov.target_object(:yayness).write "bill a c\njill b d"
prov.prefetch
# Now make a resource
bill = nil
assert_nothing_raised do
bill = @type.create :name => "bill"
end
assert_equal("a", bill.provider.one,
"Record was not found in memory")
end
# Make sure invalid fields always show up as insync
def test_invalid_fields
prov = @type.provide(:test, :parent => Puppet::Provider::ParsedFile,
:filetype => :ram, :default_target => :yayness) do
record_line :test, :fields => %w{name two}
end
cleanup do @type.unprovide(:test) end
bill = nil
assert_nothing_raised do
bill = @type.create :name => "bill",
:one => "a", :two => "c"
end
assert_apply(bill)
prov.prefetch
current_value = nil
assert_nothing_raised do
current_value = bill.retrieve
end
assert(bill.insync?(current_value),
"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)
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(:prefetch_hook).with([targeted]).returns([targeted])
prov.prefetch_target(target)
end
# #529
def test_keep_content_with_target
mkprovider
@provider.filetype = :flat
dpath = tempfile
opath = tempfile
@provider.default_target = dpath
dtarget = @provider.target_object(dpath)
otarget = @provider.target_object(opath)
dtarget.write("dname a c\n")
otarget.write("oname b d\n")
# Now make a resource that targets elsewhat.
res = @type.create(:name => "test", :one => "a", :two => "c",
:target => opath)
assert(res.property(:target), "Target is a parameter, not a property")
assert_apply(res)
assert_equal("oname b d\ntest a c\n", otarget.read,
"did not get correct results in specified target")
end
end
# $Id: parsedfile.rb 2750 2007-08-06 17:59:37Z luke $
syntax highlighted by Code2HTML, v. 0.9.1