diff --git a/lib/puppet/metatype/metaparams.rb b/lib/puppet/metatype/metaparams.rb index df226b146..c9d3426ff 100644 --- a/lib/puppet/metatype/metaparams.rb +++ b/lib/puppet/metatype/metaparams.rb @@ -1,402 +1,401 @@ require 'puppet' require 'puppet/type' class Puppet::Type # Add all of the meta parameters. #newmetaparam(:onerror) do # desc "How to handle errors -- roll back innermost # transaction, roll back entire transaction, ignore, etc. Currently # non-functional." #end newmetaparam(:noop) do desc "Boolean flag indicating whether work should actually - be done. *true*/**false**" - munge do |noop| - if noop == "true" or noop == true - return true - elsif noop == "false" or noop == false - return false - else - self.fail("Invalid noop value '%s'" % noop) + be done." + + newvalues(:true, :false) + munge do |value| + case value + when true, :true, "true": @parent.noop = true + when false, :false, "false": @parent.noop = false end end end newmetaparam(:schedule) do desc "On what schedule the object should be managed. You must create a schedule object, and then reference the name of that object to use that for your schedule: schedule { daily: period => daily, range => \"2-4\" } exec { \"/usr/bin/apt-get update\": schedule => daily } The creation of the schedule object does not need to appear in the configuration before objects that use it." munge do |name| if schedule = Puppet.type(:schedule)[name] return schedule else return name end end end newmetaparam(:check) do desc "States which should have their values retrieved but which should not actually be modified. This is currently used internally, but will eventually be used for querying, so that you could specify that you wanted to check the install state of all packages, and then query the Puppet client daemon to get reports on all packages." munge do |args| # If they've specified all, collect all known states if args == :all args = @parent.class.states.collect do |state| state.name end end unless args.is_a?(Array) args = [args] end unless defined? @parent self.devfail "No parent for %s, %s?" % [self.class, self.name] end args.each { |state| unless state.is_a?(Symbol) state = state.intern end next if @parent.statedefined?(state) stateklass = @parent.class.validstate?(state) unless stateklass raise Puppet::Error, "%s is not a valid attribute for %s" % [state, self.class.name] end next unless stateklass.checkable? @parent.newstate(state) } end end # We've got four relationship metaparameters, so this method is used # to reduce code duplication between them. def store_relationship(param, values) # We need to support values passed in as an array or as a # resource reference. result = [] # 'values' could be an array or a reference. If it's an array, # it could be an array of references or an array of arrays. if values.is_a?(Puppet::Type) result << [values.class.name, values.title] else unless values.is_a?(Array) devfail "Relationships must be resource references" end if values[0].is_a?(String) or values[0].is_a?(Symbol) # we're a type/title array reference values[0] = symbolize(values[0]) result << values else # we're an array of stuff values.each do |value| if value.is_a?(Puppet::Type) result << [value.class.name, value.title] elsif value.is_a?(Array) value[0] = symbolize(value[0]) result << value else devfail "Invalid relationship %s" % value.inspect end end end end if existing = self[param] result = existing + result end result end newmetaparam(:loglevel) do desc "Sets the level that information will be logged. The log levels have the biggest impact when logs are sent to syslog (which is currently the default)." defaultto :notice newvalues(*Puppet::Log.levels) newvalues(:verbose) munge do |loglevel| val = super(loglevel) if val == :verbose val = :info end val end end newmetaparam(:alias) do desc "Creates an alias for the object. Puppet uses this internally when you provide a symbolic name: file { sshdconfig: path => $operatingsystem ? { solaris => \"/usr/local/etc/ssh/sshd_config\", default => \"/etc/ssh/sshd_config\" }, source => \"...\" } service { sshd: subscribe => file[sshdconfig] } When you use this feature, the parser sets ``sshdconfig`` as the name, and the library sets that as an alias for the file so the dependency lookup for ``sshd`` works. You can use this parameter yourself, but note that only the library can use these aliases; for instance, the following code will not work: file { \"/etc/ssh/sshd_config\": owner => root, group => root, alias => sshdconfig } file { sshdconfig: mode => 644 } There's no way here for the Puppet parser to know that these two stanzas should be affecting the same file. See the [language tutorial][] for more information. [language tutorial]: languagetutorial.html " munge do |aliases| unless aliases.is_a?(Array) aliases = [aliases] end @parent.info "Adding aliases %s" % aliases.collect { |a| a.inspect }.join(", ") aliases.each do |other| if obj = @parent.class[other] unless obj == @parent self.fail( "%s can not create alias %s: object already exists" % [@parent.title, other] ) end next end @parent.class.alias(other, @parent) end end end newmetaparam(:tag) do desc "Add the specified tags to the associated element. While all elements are automatically tagged with as much information as possible (e.g., each class and component containing the element), it can be useful to add your own tags to a given element. Tags are currently useful for things like applying a subset of a host's configuration: puppetd --test --tag mytag This way, when you're testing a configuration you can run just the portion you're testing." munge do |tags| tags = [tags] unless tags.is_a? Array tags.each do |tag| @parent.tag(tag) end end end class RelationshipMetaparam < Puppet::Parameter class << self attr_accessor :direction, :events, :callback, :subclasses end @subclasses = [] def self.inherited(sub) @subclasses << sub end def munge(rels) @parent.store_relationship(self.class.name, rels) end # Create edges from each of our relationships. :in # relationships are specified by the event-receivers, and :out # relationships are specified by the event generator. This # way 'source' and 'target' are consistent terms in both edges # and events -- that is, an event targets edges whose source matches # the event's source. The direction of the relationship determines # which resource is applied first and which resource is considered # to be the event generator. def to_edges @value.collect do |value| # we just have a name and a type, and we need to convert it # to an object... tname, name = value object = nil unless type = Puppet::Type.type(tname) self.fail "Could not find type %s" % tname.inspect end unless object = type[name] self.fail "Could not retrieve object '%s' of type '%s'" % [name,type] end self.debug("subscribes to %s" % [object]) # Are we requiring them, or vice versa? See the builddepends # method for further docs on this. if self.class.direction == :in source = object target = @parent else source = @parent target = object end # ok, both sides of the connection store some information # we store the method to call when a given subscription is # triggered, but the source object decides whether subargs = { :event => self.class.events } if method = self.class.callback subargs[:callback] = method end rel = Puppet::Relationship.new(source, target, subargs) end end end def self.relationship_params RelationshipMetaparam.subclasses end # For each object we require, subscribe to all events that it generates. We # might reduce the level of subscription eventually, but for now... newmetaparam(:require, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :NONE}) do desc "One or more objects that this object depends on. This is used purely for guaranteeing that changes to required objects happen before the dependent object. For instance: # Create the destination directory before you copy things down file { \"/usr/local/scripts\": ensure => directory } file { \"/usr/local/scripts/myscript\": source => \"puppet://server/module/myscript\", mode => 755, require => file[\"/usr/local/scripts\"] } Note that Puppet will autorequire everything that it can, and there are hooks in place so that it's easy for elements to add new ways to autorequire objects, so if you think Puppet could be smarter here, let us know. In fact, the above code was redundant -- Puppet will autorequire any parent directories that are being managed; it will automatically realize that the parent directory should be created before the script is pulled down. Currently, exec elements will autorequire their CWD (if it is specified) plus any fully qualified paths that appear in the command. For instance, if you had an ``exec`` command that ran the ``myscript`` mentioned above, the above code that pulls the file down would be automatically listed as a requirement to the ``exec`` code, so that you would always be running againts the most recent version. " end # For each object we require, subscribe to all events that it generates. # We might reduce the level of subscription eventually, but for now... newmetaparam(:subscribe, :parent => RelationshipMetaparam, :attributes => {:direction => :in, :events => :ALL_EVENTS, :callback => :refresh}) do desc "One or more objects that this object depends on. Changes in the subscribed to objects result in the dependent objects being refreshed (e.g., a service will get restarted). For instance: class nagios { file { \"/etc/nagios/nagios.conf\": source => \"puppet://server/module/nagios.conf\", alias => nagconf # just to make things easier for me } service { nagios: running => true, subscribe => file[nagconf] } } " end newmetaparam(:notify, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :ALL_EVENTS, :callback => :refresh}) do desc %{This parameter is the opposite of **subscribe** -- it sends events to the specified object: file { "/etc/sshd_config": source => "....", notify => service[sshd] } service { sshd: ensure => running } This will restart the sshd service if the sshd config file changes.} end newmetaparam(:before, :parent => RelationshipMetaparam, :attributes => {:direction => :out, :events => :NONE}) do desc %{This parameter is the opposite of **require** -- it guarantees that the specified object is applied later than the specifying object: file { "/var/nagios/configuration": source => "...", recurse => true, before => exec["nagios-rebuid"] } exec { "nagios-rebuild": command => "/usr/bin/make", cwd => "/var/nagios/configuration" } This will make sure all of the files are up to date before the make command is run.} end end # Puppet::Type # $Id$ diff --git a/lib/puppet/type/resources.rb b/lib/puppet/type/resources.rb index 7e81a08b3..5171cb58f 100644 --- a/lib/puppet/type/resources.rb +++ b/lib/puppet/type/resources.rb @@ -1,68 +1,71 @@ # Created by Luke Kanies on 2006-12-12. # Copyright (c) 2006. All rights reserved. require 'puppet' Puppet::Type.newtype(:resources) do @doc = "This is a metatype that can manage other resource types. Any metaparams specified here will be passed on to any generated resources, so you can purge umanaged resources but set ``noop`` to true so the purging is only logged and does not actually happen." newparam(:name) do desc "The name of the type to be managed." validate do |name| unless Puppet::Type.type(name) raise ArgumentError, "Could not find resource type '%s'" % name end end end newparam(:purge, :boolean => true) do desc "Purge unmanaged resources. This will delete any resource that is not specified in your configuration and is not required by any specified resources." newvalues(:true, :false) validate do |value| if [:true, true, "true"].include?(value) unless @parent.resource_type.respond_to?(:list) raise ArgumentError, "Purging resources of type %s is not supported" % @parent[:name] end unless @parent.resource_type.validstate?(:ensure) raise ArgumentError, "Purging is only supported on types that accept 'ensure'" end end end end # Generate any new resources we need to manage. def generate resource_type.list.find_all do |resource| ! resource.managed? end.each do |resource| begin resource[:ensure] = :absent rescue ArgumentError, Parse::Error => detail err "The 'ensure' attribute on %s resources does not accept 'absent' as a value" % [self[:name]] return [] end + @metaparams.each do |name, param| + resource[name] = param.value + end end end def resource_type unless defined? @resource_type unless type = Puppet::Type.type(self[:name]) raise Puppet::DevError, "Could not find resource type" end @resource_type = type end @resource_type end end # $Id$ \ No newline at end of file diff --git a/test/types/resources.rb b/test/types/resources.rb index f8a2f6640..0cb28377d 100755 --- a/test/types/resources.rb +++ b/test/types/resources.rb @@ -1,129 +1,143 @@ #!/usr/bin/env ruby # # Created by Luke Kanies on 2006-12-12. # Copyright (c) 2006. All rights reserved. $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ require 'puppettest' class TestResources < Test::Unit::TestCase include PuppetTest def add_purge_lister # Now define the list method class << @purgetype def list $purgemembers.values end end end def mk_purger(managed = false) @purgenum ||= 0 @purgenum += 1 obj = @purgetype.create :name => "purger%s" % @purgenum $purgemembers[obj[:name]] = obj if managed obj[:fake] = "testing" end obj end def mkpurgertype # Create a purgeable type $purgemembers = {} @purgetype = Puppet::Type.newtype(:purgetest) do newparam(:name, :namevar => true) {} newstate(:ensure) do newvalue(:absent) do $purgemembers[@parent[:name]] = @parent end newvalue(:present) do $purgemembers.delete(@parent[:name]) end end newstate(:fake) do def sync :faked end end end cleanup do Puppet::Type.rmtype(:purgetest) end end def setup super @type = Puppet::Type.type(:resources) end def test_initialize assert(@type, "Could not retrieve resources type") # Make sure we can't create them for types that don't exist assert_raise(ArgumentError) do @type.create :name => "thereisnotypewiththisname" end # Now make sure it works for a normal type usertype = nil assert_nothing_raised do usertype = @type.create :name => "user" end assert(usertype, "did not create user resource type") assert_equal(Puppet::Type.type(:user), usertype.resource_type, "resource_type was not set correctly") end def test_purge # Create a purgeable type mkpurgertype purger = nil assert_nothing_raised do - purger = @type.create :name => "purgetest" + purger = @type.create :name => "purgetest", :noop => true, :loglevel => :warning end assert(purger, "did not get purger manager") # Make sure we throw an error, because the purger type does # not support listing. # It should work when we set it to false assert_nothing_raised do purger[:purge] = false end # but not true assert_raise(ArgumentError) do purger[:purge] = true end add_purge_lister() assert_equal($purgemembers.values.sort, @purgetype.list.sort) # and it should now succeed assert_nothing_raised do purger[:purge] = true end assert(purger.purge?, "purge boolean was not enabled") # Okay, now let's try doing some purging, yo managed = [] unmanned = [] 3.times { managed << mk_purger(true) } # 3 managed 3.times { unmanned << mk_purger(false) } # 3 unmanaged managed.each do |m| assert(m.managed?, "managed resource was not considered managed") end unmanned.each do |u| assert(! u.managed?, "unmanaged resource was considered managed") end + + genned = nil + assert_nothing_raised do + genned = purger.generate + end + assert(genned, "Did not get any generated resources") + assert(! genned.empty?, "generated resource list was empty") # Now make sure the generate method only finds the unmanaged resources - assert_equal(unmanned.collect { |r| r.title }.sort, purger.generate.collect { |r| r.title }, + assert_equal(unmanned.collect { |r| r.title }.sort, genned.collect { |r| r.title }, "Did not return correct purge list") + + # Now make sure our metaparams carried over + genned.each do |res| + [:noop, :loglevel].each do |param| + assert_equal(purger[param], res[param], "metaparam %s did not carry over" % param) + end + end end end # $Id$ \ No newline at end of file diff --git a/test/types/type.rb b/test/types/type.rb index 821a9524b..ade0275b8 100755 --- a/test/types/type.rb +++ b/test/types/type.rb @@ -1,763 +1,779 @@ #!/usr/bin/env ruby $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ require 'puppet/type' require 'puppettest' class TestType < Test::Unit::TestCase include PuppetTest def test_typemethods Puppet::Type.eachtype { |type| name = nil assert_nothing_raised("Searching for name for %s caused failure" % type.to_s) { name = type.name } assert(name, "Could not find name for %s" % type.to_s) assert_equal( type, Puppet::Type.type(name), "Failed to retrieve %s by name" % name ) # Skip types with no parameters or valid states #unless ! type.parameters.empty? or ! type.validstates.empty? # next #end assert_nothing_raised { assert( type.namevar, "Failed to retrieve namevar for %s" % name ) assert_not_nil( type.states, "States for %s are nil" % name ) assert_not_nil( type.validstates, "Valid states for %s are nil" % name ) } } end def test_stringvssymbols file = nil path = tempfile() assert_nothing_raised() { system("rm -f %s" % path) file = Puppet.type(:file).create( :path => path, :ensure => "file", :recurse => true, :checksum => "md5" ) } assert_nothing_raised() { file.retrieve } assert_nothing_raised() { file.evaluate } Puppet.type(:file).clear assert_nothing_raised() { system("rm -f %s" % path) file = Puppet.type(:file).create( "path" => path, "ensure" => "file", "recurse" => true, "checksum" => "md5" ) } assert_nothing_raised() { file.retrieve } assert_nothing_raised() { file[:path] } assert_nothing_raised() { file["path"] } assert_nothing_raised() { file[:recurse] } assert_nothing_raised() { file["recurse"] } assert_nothing_raised() { file.evaluate } end # This was supposed to test objects whose name was a state, but that # fundamentally doesn't make much sense, and we now don't have any such # types. def disabled_test_nameasstate # currently groups are the only objects with the namevar as a state group = nil assert_nothing_raised { group = Puppet.type(:group).create( :name => "testing" ) } assert_equal("testing", group.name, "Could not retrieve name") end # Verify that values get merged correctly def test_mergestatevalues file = tempfile() # Create the first version assert_nothing_raised { Puppet.type(:file).create( :path => file, :owner => ["root", "bin"] ) } # Make sure no other statements are allowed assert_raise(Puppet::Error) { Puppet.type(:file).create( :path => file, :group => "root" ) } end # Verify that aliasing works def test_aliasing file = tempfile() baseobj = nil assert_nothing_raised { baseobj = Puppet.type(:file).create( :name => file, :ensure => "file", :alias => ["funtest"] ) } # Verify our adding ourselves as an alias isn't an error. assert_nothing_raised { baseobj[:alias] = file } assert_instance_of(Puppet.type(:file), Puppet.type(:file)["funtest"], "Could not retrieve alias") end # Verify that requirements don't depend on file order def test_prereqorder one = tempfile() two = tempfile() twoobj = nil oneobj = nil assert_nothing_raised("Could not create prereq that doesn't exist yet") { twoobj = Puppet.type(:file).create( :name => two, :require => [:file, one] ) } assert_nothing_raised { oneobj = Puppet.type(:file).create( :name => one ) } comp = newcomp(twoobj, oneobj) assert_nothing_raised { comp.finalize } assert(twoobj.requires?(oneobj), "Requirement was not created") end # Verify that names are aliases, not equivalents def test_nameasalias file = nil # Create the parent dir, so we make sure autorequiring the parent dir works parentdir = tempfile() dir = Puppet.type(:file).create( :name => parentdir, :ensure => "directory" ) assert_apply(dir) path = File.join(parentdir, "subdir") name = "a test file" transport = Puppet::TransObject.new(name, "file") transport[:path] = path transport[:ensure] = "file" assert_nothing_raised { file = transport.to_type } assert_equal(path, file[:path]) assert_equal(name, file.title) assert_nothing_raised { file.retrieve } assert_apply(file) assert(Puppet.type(:file)[name], "Could not look up object by name") end def test_ensuredefault user = nil assert_nothing_raised { user = Puppet.type(:user).create( :name => "pptestAA", :check => [:uid] ) } # make sure we don't get :ensure for unmanaged files assert(! user.state(:ensure), "User got an ensure state") assert_nothing_raised { user = Puppet.type(:user).create( :name => "pptestAA", :comment => "Testingness" ) } # but make sure it gets added once we manage them assert(user.state(:ensure), "User did not add ensure state") assert_nothing_raised { user = Puppet.type(:user).create( :name => "pptestBB", :comment => "A fake user" ) } # and make sure managed objects start with them assert(user.state(:ensure), "User did not get an ensure state") end # Make sure removal works def test_remove objects = {} top = Puppet.type(:component).create(:name => "top") objects[top.class] = top base = tempfile() # now make a two-tier, 5 piece tree %w{a b}.each do |letter| name = "comp%s" % letter comp = Puppet.type(:component).create(:name => name) top.push comp objects[comp.class] = comp 5.times do |i| file = base + letter + i.to_s obj = Puppet.type(:file).create(:name => file, :ensure => "file") comp.push obj objects[obj.class] = obj end end assert_nothing_raised do top.remove end objects.each do |klass, obj| assert_nil(klass[obj.name], "object %s was not removed" % obj.name) end end # Verify that objects can't be their own children. def test_object_recursion comp = Puppet.type(:component).create(:name => "top") file = Puppet.type(:file).create(:path => tempfile, :ensure => :file) assert_raise(Puppet::DevError) do comp.push(comp) end assert_raise(Puppet::DevError) do file.push(file) end assert_raise(Puppet::DevError) do comp.parent = comp end assert_raise(Puppet::DevError) do file.parent = file end assert_nothing_raised { comp.push(file) } assert_raise(Puppet::DevError) do file.push(comp) end assert_raise(Puppet::DevError) do comp.parent = file end end def test_loadplugins names = %w{loadedplugin1 loadplugin2 loadplugin3} dirs = [] 3.times { dirs << tempfile() } # Set plugindest to something random Puppet[:plugindest] = tempfile() Puppet[:pluginpath] = dirs.join(":") names.each do |name| dir = dirs.shift Dir.mkdir(dir) # Create an extra file for later [name, name + "2ness"].each do |n| file = File.join(dir, n + ".rb") File.open(file, "w") do |f| f.puts %{Puppet::Type.newtype('#{n}') do newparam(:argument) do isnamevar end end } end end assert(Puppet::Type.type(name), "Did not get loaded plugin") assert_nothing_raised { Puppet::Type.type(name).create( :name => "myname" ) } end # Now make sure the plugindest got added to our pluginpath assert(Puppet[:pluginpath].split(":").include?(Puppet[:plugindest]), "Plugin dest did not get added to plugin path") # Now make sure it works with just a single path, using the extra files # created above. Puppet[:pluginpath] = Puppet[:pluginpath].split(":")[0] assert(Puppet::Type.type("loadedplugin12ness"), "Did not get loaded plugin") end def test_newtype_methods assert_nothing_raised { Puppet::Type.newtype(:mytype) do newparam(:wow) do isnamevar end end } assert(Puppet::Type.respond_to?(:newmytype), "new method did not get created") obj = nil assert_nothing_raised { obj = Puppet::Type.newmytype(:wow => "yay") } assert(obj.is_a?(Puppet::Type.type(:mytype)), "Obj is not the correct type") # Now make the type again, just to make sure it works on refreshing. assert_nothing_raised { Puppet::Type.newtype(:mytype) do newparam(:yay) do isnamevar end end } obj = nil # Make sure the old class was thrown away and only the new one is sitting # around. assert_raise(Puppet::Error) { obj = Puppet::Type.newmytype(:wow => "yay") } assert_nothing_raised { obj = Puppet::Type.newmytype(:yay => "yay") } # Now make sure that we don't replace existing, non-type methods parammethod = Puppet::Type.method(:newparam) assert_nothing_raised { Puppet::Type.newtype(:param) do newparam(:rah) do isnamevar end end } assert_equal(parammethod, Puppet::Type.method(:newparam), "newparam method got replaced by newtype") end def test_newstate_options # Create a type with a fake provider providerclass = Class.new do def method_missing(method, *args) return method end end self.class.const_set("ProviderClass", providerclass) type = Puppet::Type.newtype(:mytype) do newparam(:name) do isnamevar end def provider @provider ||= ProviderClass.new @provider end end # Now make a state with no options. state = nil assert_nothing_raised do state = type.newstate(:noopts) do end end # Now create an instance obj = type.create(:name => :myobj) inst = state.new(:parent => obj) # And make sure it's correctly setting @is ret = nil assert_nothing_raised { ret = inst.retrieve } assert_equal(:noopts, inst.is) # Now create a state with a different way of doing it state = nil assert_nothing_raised do state = type.newstate(:setretrieve, :retrieve => :yayness) end inst = state.new(:parent => obj) # And make sure it's correctly setting @is ret = nil assert_nothing_raised { ret = inst.retrieve } assert_equal(:yayness, inst.is) end def test_name_vs_title path = tempfile() trans = nil assert_nothing_raised { trans = Puppet::TransObject.new(path, :file) } file = nil assert_nothing_raised { file = Puppet::Type.newfile(trans) } assert(file.respond_to?(:title), "No 'title' method") assert(file.respond_to?(:name), "No 'name' method") assert_equal(file.title, file.name, "Name and title were not marked equal") assert_nothing_raised { file.title = "My file" } assert_equal("My file", file.title) assert_equal(path, file.name) end # Make sure the title is sufficiently differentiated from the namevar. def test_title_at_creation_with_hash file = nil fileclass = Puppet::Type.type(:file) path = tempfile() assert_nothing_raised do file = fileclass.create( :title => "Myfile", :path => path ) end assert_equal("Myfile", file.title, "Did not get correct title") assert_equal(path, file[:name], "Did not get correct name") file = nil Puppet::Type.type(:file).clear # Now make sure we can specify both and still get the right answers assert_nothing_raised do file = fileclass.create( :title => "Myfile", :name => path ) end assert_instance_of(fileclass, file) assert_equal("Myfile", file.title, "Did not get correct title") assert_equal(path, file[:name], "Did not get correct name") end # Make sure the "create" class method behaves appropriately. def test_class_create title = "Myfile" validate = proc do |element| assert(element, "Did not create file") assert_instance_of(Puppet::Type.type(:file), element) assert_equal(title, element.title, "Title is not correct") end type = :file args = {:path => tempfile(), :owner => "root"} trans = Puppet::TransObject.new(title, type) args.each do |name, val| trans[name] = val end # First call it on the appropriate typeclass obj = nil assert_nothing_raised do obj = Puppet::Type.type(:file).create(trans) end validate.call(obj) # Now try it using the class method on Type oldid = obj.object_id obj = nil Puppet::Type.type(:file).clear assert_nothing_raised { obj = Puppet::Type.create(trans) } validate.call(obj) assert(oldid != obj.object_id, "Got same object back") # Now try the same things with hashes instead of a transobject oldid = obj.object_id obj = nil Puppet::Type.type(:file).clear hash = { :type => :file, :title => "Myfile", :path => tempfile(), :owner => "root" } # First call it on the appropriate typeclass obj = nil assert_nothing_raised do obj = Puppet::Type.type(:file).create(hash) end validate.call(obj) assert_equal(:file, obj.should(:type), "Type param did not pass through") assert(oldid != obj.object_id, "Got same object back") # Now try it using the class method on Type oldid = obj.object_id obj = nil Puppet::Type.type(:file).clear assert_nothing_raised { obj = Puppet::Type.create(hash) } validate.call(obj) assert(oldid != obj.object_id, "Got same object back") assert_nil(obj.should(:type), "Type param passed through") end def test_multiplenames obj = nil path = tempfile() assert_raise ArgumentError do obj = Puppet::Type.type(:file).create( :name => path, :path => path ) end end def test_title_and_name obj = nil path = tempfile() fileobj = Puppet::Type.type(:file) assert_nothing_raised do obj = fileobj.create( :title => "myfile", :path => path ) end assert_equal(obj, fileobj["myfile"], "Could not retrieve obj by title") assert_equal(obj, fileobj[path], "Could not retrieve obj by name") end # Make sure default providers behave correctly def test_defaultproviders # Make a fake type type = Puppet::Type.newtype(:defaultprovidertest) do newparam(:name) do end end basic = type.provide(:basic) do defaultfor :operatingsystem => :somethingelse, :operatingsystemrelease => :yayness end assert_equal(basic, type.defaultprovider) type.defaultprovider = nil greater = type.provide(:greater) do defaultfor :operatingsystem => Facter.value("operatingsystem") end assert_equal(greater, type.defaultprovider) end # Make sure that we can have multiple isomorphic objects with the same name, # but not with non-isomorphic objects. def test_isomorphic_names # First do execs, since they're not isomorphic. echo = Puppet::Util.binary "echo" exec1 = exec2 = nil assert_nothing_raised do exec1 = Puppet::Type.type(:exec).create( :title => "exec1", :command => "#{echo} funtest" ) end assert_nothing_raised do exec2 = Puppet::Type.type(:exec).create( :title => "exec2", :command => "#{echo} funtest" ) end assert_apply(exec1, exec2) # Now do files, since they are. This should fail. file1 = file2 = nil path = tempfile() assert_nothing_raised do file1 = Puppet::Type.type(:file).create( :title => "file1", :path => path, :content => "yayness" ) end # This will fail, but earlier systems will catch it. assert_raise(Puppet::Error) do file2 = Puppet::Type.type(:file).create( :title => "file2", :path => path, :content => "rahness" ) end assert(file1, "Did not create first file") assert_nil(file2, "Incorrectly created second file") end def test_tags obj = Puppet::Type.type(:file).create(:path => tempfile()) tags = [:some, :test, :tags] obj.tags = tags assert_equal(tags + [:file], obj.tags) end def disabled_test_list Puppet::Type.loadall Puppet::Type.eachtype do |type| next if type.name == :symlink next if type.name == :component next if type.name == :tidy assert(type.respond_to?(:list), "%s does not respond to list" % type.name) end end def test_to_hash file = Puppet::Type.newfile :path => tempfile(), :owner => "luke", :recurse => true, :loglevel => "warning" hash = nil assert_nothing_raised do hash = file.to_hash end [:path, :owner, :recurse, :loglevel].each do |param| assert(hash[param], "Hash did not include %s" % param) end end # Make sure that classes behave like hashes. def test_class_hash_behaviour path = tempfile() filetype = Puppet::Type.type(:file) one = Puppet::Type.newfile :path => path assert_equal(one, filetype[path], "Did not get file back") assert_raise(Puppet::Error) do filetype[path] = one end end def test_ref path = tempfile() file = Puppet::Type.newfile(:path => path) assert_equal("file[#{path}]", file.ref) exec = Puppet::Type.newexec(:title => "yay", :command => "/bin/echo yay") assert_equal("exec[yay]", exec.ref) end + + def test_noop_metaparam + file = Puppet::Type.newfile :path => tempfile + assert(!file.noop, "file incorrectly in noop") + + assert_nothing_raised do + file[:noop] = true + end + assert(file.noop, "file should be in noop") + + # Now set the main one + Puppet[:noop] = true + assert(file.noop, "file should be in noop") + file[:noop] = false + assert(file.noop, "file should be in noop") + end end # $Id$