diff --git a/Rakefile b/Rakefile index d5b65e49a..f67ad923e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,140 +1,136 @@ # Rakefile for Puppet -*- ruby -*- $: << File.expand_path(File.join(File.dirname(__FILE__), 'lib')) begin require 'rake/reductive' rescue LoadError $stderr.puts "You must have the Reductive build library in your RUBYLIB." exit(14) end TESTHOSTS = %w{rh3a fedora1 centos1 freebsd1 culain} project = Rake::RedLabProject.new("puppet") do |p| p.summary = "System Automation and Configuration Management Software" p.description = "Puppet is a declarative language for expressing system configuration, a client and server for distributing it, and a library for realizing the configuration." p.filelist = [ 'install.rb', '[A-Z]*', 'lib/puppet.rb', 'lib/puppet/**/*.rb', 'lib/puppet/**/*.py', 'test/**/*.rb', 'bin/**/*', 'ext/**/*', 'examples/**/*', 'conf/**/*' ] p.filelist.exclude("bin/pi") p.add_dependency('facter', '1.1.0') #p.epmhosts = %w{culain} #p.sunpkghost = "sol10b" #p.rpmhost = "fedora1" end if project.has?(:gem) # Make our gem task. This actually just fills out the spec. project.mkgemtask do |task| task.require_path = 'lib' # Use these for libraries. task.bindir = "bin" # Use these for applications. task.executables = ["puppet", "puppetd", "puppetmasterd", "puppetdoc", "puppetca", "puppetrun", "ralsh"] task.default_executable = "puppet" task.autorequire = 'puppet' #### Documentation and testing. task.has_rdoc = true #s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a task.rdoc_options << '--title' << 'Puppet - Configuration Management' << '--main' << 'README' << '--line-numbers' task.test_file = "test/Rakefile" end end if project.has?(:epm) project.mkepmtask do |task| task.bins = FileList.new("bin/puppet", "bin/puppetca") task.sbins = FileList.new("bin/puppetmasterd", "bin/puppetd") task.rubylibs = FileList.new('lib/**/*') end end rule(/_is_runnable$/) do |t| available = false executable = t.name.sub(/_is_runnable$/, '') ENV['PATH'].split(':').each do |elem| available = true if File.executable? File.join(elem, executable) end unless available puts "You do not have #{executable} available in your path" exit 1 end end -file "debian" => :bzr_is_runnable do - system("bzr get http://www.hezmatt.org/~mpalmer/bzr/puppet.debian.svn debian") || exit(1) -end - task :check_build_deps => 'dpkg-checkbuilddeps_is_runnable' do system("dpkg-checkbuilddeps") || exit(1) end task :debian_packages => [ "debian", :check_build_deps, :fakeroot_is_runnable ] do system("fakeroot debian/rules clean") || exit(1) system("fakeroot debian/rules binary") || exit(1) end def dailyfile(package) "#{downdir}/#{package}/#{package}-daily-#{stamp}.tgz" end def daily(package) edir = "/tmp/daily-export" Dir.mkdir edir Dir.chdir(edir) do sh %{svn export http://reductivelabs.com/svn/#{package}/trunk #{package} >/dev/null} sh %{tar cf - #{package} | gzip -c > #{dailyfile(package)}} end FileUtils.rm_rf(edir) end def downdir ENV['DOWNLOAD_DIR'] || "/opt/rl/docroots/reductivelabs.com/htdocs/downloads" end def stamp [Time.now.year, Time.now.month, Time.now.day].collect { |i| i.to_s}.join end pdaily = dailyfile("puppet") fdaily = dailyfile("facter") file pdaily do daily("puppet") end file fdaily do daily("facter") end task :daily => [pdaily, fdaily] task :dailyclean do Dir.glob("#{downdir}/*/*daily*.tgz").each do |file| puts "Removing %s" % file File.unlink(file) end end diff --git a/bin/puppetrun b/bin/puppetrun index 5f99d6325..fe981dadf 100755 --- a/bin/puppetrun +++ b/bin/puppetrun @@ -1,404 +1,404 @@ #!/usr/bin/env ruby # # = Synopsis # # Trigger a puppetd run on a set of hosts. # # = Usage # # puppetrun [-a|--all] [-c|--class ] [-d|--debug] [-f|--foreground] # [-h|--help] [--host ] [--no-fqdn] [--ignoreschedules] # [-t|--tag ] [--test] # # = Description # # This script can be used to connect to a set of machines running +puppetd+ # and trigger them to run their configurations. The most common usage would # be to specify a class of hosts and a set of tags, and +puppetrun+ would # look up in LDAP all of the hosts matching that class, then connect to # each host and trigger a run of all of the objects with the specified tags. # # If you are not storing your host configurations in LDAP, you can specify # hosts manually. # # You will most likely have to run +puppetrun+ as root to get access to # the SSL certificates. # # +puppetrun+ reads +puppetmaster+'s configuration file, so that it can copy # things like LDAP settings. # # = Usage Notes # # +puppetrun+ is useless unless +puppetd+ is listening. See its documentation # for more information, but the gist is that you must enable +listen+ on the # +puppetd+ daemon, either using +--listen+ on the command line or adding # 'listen: true' in its config file. In addition, you need to set the daemons # up to specifically allow connections by creating the +namespaceauth+ file, # normally at '/etc/puppet/namespaceauth.conf'. This file specifies who has # access to each namespace; if you create the file you must add every namespace # you want any Puppet daemon to allow -- it is currently global to all Puppet # daemons. # # An example file looks like this:: # # [fileserver] # allow *.madstop.com # # [puppetmaster] # allow *.madstop.com # # [puppetrunner] # allow culain.madstop.com # # This is what you would install on your Puppet master; non-master hosts could # leave off the 'fileserver' and 'puppetmaster' namespaces. # # Expect more documentation on this eventually. # # = Options # # Note that any configuration parameter that's valid in the configuration file # is also a valid long argument. For example, 'ssldir' is a valid configuration # parameter, so you can specify '--ssldir ' as an argument. # # See the configuration file documentation at # http://reductivelabs.com/projects/puppet/reference/configref.html for # the full list of acceptable parameters. A commented list of all # configuration options can also be generated by running puppetmasterdd with # '--genconfig'. # # # all:: # Connect to all available hosts. Requires LDAP support at this point. # # class:: # Specify a class of machines to which to connect. This only works if you # have LDAP configured, at the moment. # # debug:: # Enable full debugging. # # foreground:: # Run each configuration in the foreground; that is, when connecting to a host, # do not return until the host has finished its run. The default is false. # # help:: # Print this help message # # host:: # A specific host to which to connect. This flag can be specified more # than once. # # ignoreschedules:: # Whether the client should ignore schedules when running its configuration. # This can be used to force the client to perform work it would not normally # perform so soon. The default is false. # # parallel:: # How parallel to make the connections. Parallelization is provided by forking # for each client to which to connect. The default is 1, meaning serial execution. # # tag:: # Specify a tag for selecting the objects to apply. Does not work with the # --test option. # # # test:: # Print the hosts you would connect to but do not actually connect. This # option requires LDAP support at this point. # # = Example # # sudo puppetrun -p 10 --host host1 --host host2 -t remotefile -t webserver # # = Author # # Luke Kanies # # = Copyright # # Copyright (c) 2005 Reductive Labs, LLC # Licensed under the GNU Public License [:INT, :TERM].each do |signal| trap(signal) do $stderr.puts "Cancelling" exit(1) end end begin require 'rubygems' rescue LoadError # Nothing; we were just doing this just in case end begin require 'ldap' rescue LoadError $stderr.puts "Failed to load ruby LDAP library. LDAP functionality will not be available" end require 'puppet' require 'puppet/network/client' require 'getoptlong' # Look up all nodes matching a given class in LDAP. def ldapnodes(klass, fqdn = true) unless defined? @ldap setupldap() end hosts = [] filter = nil if klass == :all filter = "objectclass=puppetclient" else filter = "puppetclass=#{klass}" end @ldap.search(Puppet[:ldapbase], 2, filter, "cn") do |entry| # Skip the default host entry if entry.dn =~ /cn=default,/ $stderr.puts "Skipping default host entry" next end if fqdn hosts << entry.dn.sub("cn=",'').sub(/ou=hosts,/i, '').gsub(",dc=",".") else hosts << entry.get_values("cn")[0] end end return hosts end def setupldap begin @ldap = Puppet::Parser::Interpreter.ldap() rescue => detail $stderr.puts "Could not connect to LDAP: %s" % detail exit(34) end end flags = [ [ "--all", "-a", GetoptLong::NO_ARGUMENT ], [ "--tag", "-t", GetoptLong::REQUIRED_ARGUMENT ], [ "--class", "-c", GetoptLong::REQUIRED_ARGUMENT ], [ "--foreground", "-f", GetoptLong::NO_ARGUMENT ], [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], [ "--help", "-h", GetoptLong::NO_ARGUMENT ], [ "--host", GetoptLong::REQUIRED_ARGUMENT ], [ "--parallel", "-p", GetoptLong::REQUIRED_ARGUMENT ], [ "--no-fqdn", "-n", GetoptLong::NO_ARGUMENT ], [ "--test", GetoptLong::NO_ARGUMENT ], [ "--version", "-V", GetoptLong::NO_ARGUMENT ] ] # Add all of the config parameters as valid options. Puppet.settings.addargs(flags) result = GetoptLong.new(*flags) options = { :ignoreschedules => false, :foreground => false, :parallel => 1, :debug => false, :test => false, :all => false, :verbose => true, :fqdn => true } hosts = [] classes = [] tags = [] Puppet::Util::Log.newdestination(:console) begin result.each { |opt,arg| case opt when "--version" puts "%s" % Puppet.version exit when "--ignoreschedules" options[:ignoreschedules] = true when "--no-fqdn" options[:fqdn] = false when "--all" options[:all] = true when "--test" options[:test] = true when "--tag" tags << arg when "--class" classes << arg when "--host" hosts << arg when "--help" if Puppet.features.usage? RDoc::usage && exit else puts "No help available unless you have RDoc::usage installed" exit end when "--parallel" begin options[:parallel] = Integer(arg) rescue $stderr.puts "Could not convert %s to an integer" % arg.inspect exit(23) end when "--foreground" options[:foreground] = true when "--debug" options[:debug] = true else Puppet.settings.handlearg(opt, arg) end } rescue GetoptLong::InvalidOption => detail $stderr.puts "Try '#{$0} --help'" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end # Now parse the config config = File.join(Puppet[:confdir], "puppetmasterd.conf") Puppet.parse_config(config) if File.exists? config Puppet.settings.parse(config) end -if Puppet[:ldapnodes] +if Puppet[:node_terminus] = "ldap" if options[:all] hosts = ldapnodes(:all, options[:fqdn]) puts "all: %s" % hosts.join(", ") else classes.each do |klass| list = ldapnodes(klass, options[:fqdn]) puts "%s: %s" % [klass, list.join(", ")] hosts += list end end elsif ! classes.empty? $stderr.puts "You must be using LDAP to specify host classes" exit(24) end if tags.empty? tags = "" else tags = tags.join(",") end children = {} # If we get a signal, then kill all of our children and get out. [:INT, :TERM].each do |signal| trap(signal) do Puppet.notice "Caught #{signal}; shutting down" children.each do |pid, host| Process.kill("INT", pid) end waitall exit(1) end end if options[:test] puts "Skipping execution in test mode" exit(0) end todo = hosts.dup failures = [] # Now do the actual work go = true while go # If we don't have enough children in process and we still have hosts left to # do, then do the next host. if children.length < options[:parallel] and ! todo.empty? host = todo.shift pid = fork do # First make sure the client is up out = %x{ping -c 1 #{host}} unless $? == 0 $stderr.print "Could not contact %s\n" % host next end client = Puppet::Network::Client.runner.new( :Server => host, :Port => Puppet[:puppetport] ) print "Triggering %s\n" % host begin result = client.run(tags, options[:ignoreschedules], options[:foreground]) rescue => detail $stderr.puts "Host %s failed: %s\n" % [host, detail] exit(2) end case result when "success": exit(0) when "running": $stderr.puts "Host %s is already running" % host exit(3) else $stderr.puts "Host %s returned unknown answer '%s'" % [host, result] exit(12) end end children[pid] = host else # Else, see if we can reap a process. begin pid = Process.wait if host = children[pid] # Remove our host from the list of children, so the parallelization # continues working. children.delete(pid) if $?.exitstatus != 0 failures << host end print "%s finished with exit code %s\n" % [host, $?.exitstatus] else $stderr.puts "Could not find host for PID %s with status %s" % [pid, $?.exitstatus] end rescue Errno::ECHILD # There are no children left, so just exit unless there are still # children left to do. next unless todo.empty? if failures.empty? puts "Finished" exit(0) else puts "Failed: %s" % failures.join(", ") exit(3) end end end end diff --git a/examples/root/etc/puppet/puppetmasterd.conf b/examples/root/etc/puppet/puppet.conf similarity index 59% rename from examples/root/etc/puppet/puppetmasterd.conf rename to examples/root/etc/puppet/puppet.conf index b3182075c..151364ebd 100644 --- a/examples/root/etc/puppet/puppetmasterd.conf +++ b/examples/root/etc/puppet/puppet.conf @@ -1,7 +1,10 @@ +[puppetd] +report = true +factsync = true +pluginsync = true + [puppetmasterd] reports = store,rrdgraph,tagmail,log - -[ldap] -ldapnodes = true +node_terminus = ldap ldapserver = culain.madstop.com ldapbase = dc=madstop,dc=com diff --git a/examples/root/etc/puppet/puppetd.conf b/examples/root/etc/puppet/puppetd.conf deleted file mode 100644 index ce988cfa1..000000000 --- a/examples/root/etc/puppet/puppetd.conf +++ /dev/null @@ -1,4 +0,0 @@ -[puppet] -report = true -factsync = true -pluginsync = true diff --git a/lib/puppet/provider/package/openbsd.rb b/lib/puppet/provider/package/openbsd.rb index ce69dd432..f76c37176 100755 --- a/lib/puppet/provider/package/openbsd.rb +++ b/lib/puppet/provider/package/openbsd.rb @@ -1,87 +1,87 @@ require 'puppet/provider/package' # Packaging on OpenBSD. Doesn't work anywhere else that I know of. Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Package do desc "OpenBSD's form of ``pkg_add`` support." commands :pkginfo => "pkg_info", :pkgadd => "pkg_add", :pkgdelete => "pkg_delete" defaultfor :operatingsystem => :openbsd confine :operatingsystem => :openbsd def self.instances packages = [] begin execpipe(listcmd()) do |process| # our regex for matching pkg_info output regex = %r{^(\S+)-([^-\s]+)\s+(.+)} fields = [:name, :ensure, :description] hash = {} # now turn each returned line into a package object process.each { |line| if match = regex.match(line) fields.zip(match.captures) { |field,value| hash[field] = value } yup = nil name = hash[:name] hash[:provider] = self.name packages << new(hash) hash = {} else # Print a warning on lines we can't match, but move # on, since it should be non-fatal warning("Failed to match line %s" % line) end } end return packages rescue Puppet::ExecutionFailure return nil end end def self.listcmd [command(:pkginfo), " -a"] end def install should = @resource.should(:ensure) unless @resource[:source] raise Puppet::Error, "You must specify a package source for BSD packages" end pkgadd @resource[:source] end def query hash = {} info = pkginfo @resource[:name] # Search for the version info - if info =~ /Information for #{@resource[:name]}-(\S+)/ + if info =~ /Information for (inst:)?#{@resource[:name]}-(\S+)/ hash[:ensure] = $1 else return nil end # And the description if info =~ /Comment:\s*\n(.+)/ hash[:description] = $1 end return hash end def uninstall pkgdelete @resource[:name] end end diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb index be5c2ed72..3e34c0b60 100755 --- a/lib/puppet/type/host.rb +++ b/lib/puppet/type/host.rb @@ -1,88 +1,101 @@ module Puppet newtype(:host) do ensurable newproperty(:ip) do desc "The host's IP address, IPv4 or IPv6." + + validate do |value| + unless value =~ /((([0-9a-fA-F]+:){7}[0-9a-fA-F]+)|(([0-9a-fA-F]+:)*[0-9a-fA-F]+)?::(([0-9a-fA-F]+:)*[0-9a-fA-F]+)?)|((25[0-5]|2[0-4][\d]|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})/ + raise Puppet::Error, "Invalid IP address" + end + end + end newproperty(:alias) do desc "Any alias the host might have. Multiple values must be specified as an array. Note that this state has the same name as one of the metaparams; using this state to set aliases will make those aliases available in your Puppet scripts and also on disk." def insync?(is) is == @should end def is_to_s(currentvalue = @is) currentvalue = [currentvalue] unless currentvalue.is_a? Array currentvalue.join(" ") end def retrieve is = super case is when String is = is.split(/\s*,\s*/) when Symbol: is = [is] when Array # nothing else raise Puppet::DevError, "Invalid @is type %s" % is.class end return is end # We actually want to return the whole array here, not just the first # value. def should if defined? @should if @should == [:absent] return :absent else return @should end else return nil end end def should_to_s(newvalue = @should) newvalue.join(" ") end validate do |value| if value =~ /\s/ raise Puppet::Error, "Aliases cannot include whitespace" end end end newproperty(:target) do desc "The file in which to store service information. Only used by those providers that write to disk (i.e., not NetInfo)." defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) @resource.class.defaultprovider.default_target else nil end } end newparam(:name) do desc "The host name." isnamevar + + validate do |value| + unless value =~ /^\w+-?[\w+]?\.?[\w+.{1}]*\w+$/ + raise Puppet::Error, "Invalid host name" + end + end end @doc = "Installs and manages host entries. For most systems, these entries will just be in ``/etc/hosts``, but some systems (notably OS X) will have different solutions." end end diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index 9173eaa1c..24b187e5f 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -1,350 +1,350 @@ # Define the different packaging systems. Each package system is implemented # in a module, which then gets used to individually extend each package object. # This allows packages to exist on the same machine using different packaging # systems. module Puppet newtype(:package) do @doc = "Manage packages. There is a basic dichotomy in package support right now: Some package types (e.g., yum and apt) can retrieve their own package files, while others (e.g., rpm and sun) cannot. For those package formats that cannot retrieve their own files, you can use the ``source`` parameter to point to the correct file. Puppet will automatically guess the packaging format that you are using based on the platform you are on, but you can override it using the ``provider`` parameter; each provider defines what it requires in order to function, and you must meet those requirements to use a given provider." feature :installable, "The provider can install packages.", :methods => [:install] feature :uninstallable, "The provider can uninstall packages.", :methods => [:uninstall] feature :upgradeable, "The provider can upgrade to the latest version of a package. This feature is used by specifying ``latest`` as the desired value for the package.", :methods => [:update, :latest] feature :purgeable, "The provider can purge packages. This generally means that all traces of the package are removed, including existing configuration files. This feature is thus destructive and should be used with the utmost care.", :methods => [:purge] feature :versionable, "The provider is capable of interrogating the package database for installed version(s), and can select which out of a set of available versions of a package to install if asked." ensurable do desc "What state the package should be in. *latest* only makes sense for those packaging formats that can retrieve new packages on their own and will throw an error on those that cannot. For those packaging systems that allow you to specify package versions, specify them here. Similarly, *purged* is only useful for packaging systems that support the notion of managing configuration files separately from 'normal' system files." attr_accessor :latest newvalue(:present, :event => :package_installed) do provider.install end newvalue(:absent, :event => :package_removed) do provider.uninstall end newvalue(:purged, :event => :package_purged, :required_features => :purgeable) do provider.purge end # Alias the 'present' value. aliasvalue(:installed, :present) newvalue(:latest, :required_features => :upgradeable) do # Because yum always exits with a 0 exit code, there's a retrieve # in the "install" method. So, check the current state now, # to compare against later. current = self.retrieve begin provider.update rescue => detail self.fail "Could not update: %s" % detail end if current == :absent :package_installed else :package_changed end end newvalue(/./, :required_features => :versionable) do begin provider.install rescue => detail self.fail "Could not update: %s" % detail end if self.retrieve == :absent :package_installed else :package_changed end end defaultto :installed # Override the parent method, because we've got all kinds of # funky definitions of 'in sync'. def insync?(is) @should ||= [] @latest = nil unless defined? @latest @lateststamp ||= (Time.now.to_i - 1000) # Iterate across all of the should values, and see how they # turn out. @should.each { |should| case should when :present return true unless [:absent, :purged].include?(is) when :latest # Short-circuit packages that are not present return false if is == :absent or is == :purged # Don't run 'latest' more than about every 5 minutes if @latest and ((Time.now.to_i - @lateststamp) / 60) < 5 #self.debug "Skipping latest check" else begin @latest = provider.latest @lateststamp = Time.now.to_i rescue => detail error = Puppet::Error.new("Could not get latest version: %s" % detail.to_s) error.set_backtrace(detail.backtrace) raise error end end case is when @latest: return true when :present: # This will only happen on retarded packaging systems # that can't query versions. return true else self.debug "is is %s, latest %s is %s" % [is.inspect, @resource.name, @latest.inspect] end when :absent return true if is == :absent or is == :purged when :purged return true if is == :purged when is return true end } return false end # This retrieves the current state. LAK: I think this method is unused. def retrieve return provider.properties[:ensure] end # Provide a bit more information when logging upgrades. def should_to_s(newvalue = @should) if @latest @latest.to_s else super(newvalue) end end end newparam(:name) do desc "The package name. This is the name that the packaging system uses internally, which is sometimes (especially on Solaris) a name that is basically useless to humans. If you want to abstract package installation, then you can use aliases to provide a common name to packages:: # In the 'openssl' class - $ssl = $operationgsystem ? { + $ssl = $operatingsystem ? { solaris => SMCossl, default => openssl } # It is not an error to set an alias to the same value as the # object name. package { $ssl: ensure => installed, alias => openssl } . etc. . - $ssh = $operationgsystem ? { + $ssh = $operatingsystem ? { solaris => SMCossh, default => openssh } # Use the alias to specify a dependency, rather than # having another selector to figure it out again. package { $ssh: ensure => installed, alias => openssh, require => package[openssl] } " isnamevar end newparam(:source) do desc "Where to find the actual package. This must be a local file (or on a network file system) or a URL that your specific packaging type understands; Puppet will not retrieve files for you." validate do |value| unless value =~ /^#{File::SEPARATOR}/ or value =~ /\w+:\/\// self.fail( "Package sources must be fully qualified files or URLs, depending on the platform." ) end end end newparam(:instance) do desc "A read-only parameter set by the package." end newparam(:status) do desc "A read-only parameter set by the package." end newparam(:type) do desc "Deprecated form of ``provider``." munge do |value| warning "'type' is deprecated; use 'provider' instead" @resource[:provider] = value @resource[:provider] end end newparam(:adminfile) do desc "A file containing package defaults for installing packages. This is currently only used on Solaris. The value will be validated according to system rules, which in the case of Solaris means that it should either be a fully qualified path or it should be in /var/sadm/install/admin." end newparam(:responsefile) do desc "A file containing any necessary answers to questions asked by the package. This is currently used on Solaris and Debian. The value will be validated according to system rules, but it should generally be a fully qualified path." end newparam(:configfiles) do desc "Whether configfiles should be kept or replaced. Most packages types do not support this parameter." defaultto :keep newvalues(:keep, :replace) end newparam(:category) do desc "A read-only parameter set by the package." end newparam(:platform) do desc "A read-only parameter set by the package." end newparam(:root) do desc "A read-only parameter set by the package." end newparam(:vendor) do desc "A read-only parameter set by the package." end newparam(:description) do desc "A read-only parameter set by the package." end newparam(:allowcdrom) do desc "Tells apt to allow cdrom sources in the sources.list file. Normally apt will bail if you try this." newvalues(:true, :false) end autorequire(:file) do autos = [] [:responsefile, :adminfile].each { |param| if val = self[param] autos << val end } if source = self[:source] if source =~ /^#{File::SEPARATOR}/ autos << source end end autos end # This only exists for testing. def clear if obj = @parameters[:ensure] obj.latest = nil end end # The 'query' method returns a hash of info if the package # exists and returns nil if it does not. def exists? @provider.get(:ensure) != :absent end def initialize(params) self.initvars provider = nil [:provider, "use"].each { |label| if params.include?(label) provider = params[label] params.delete(label) end } if provider self[:provider] = provider else self.setdefaults(:provider) end super(params) unless @parameters.include?(:provider) raise Puppet::DevError, "No package provider set" end end def retrieve @provider.properties.inject({}) do |props, ary| name, value = ary if prop = @parameters[name] props[prop] = value end props end end end # Puppet.type(:package) end diff --git a/test/ral/types/host.rb b/test/ral/types/host.rb index 088f93c1f..16ed20bd4 100755 --- a/test/ral/types/host.rb +++ b/test/ral/types/host.rb @@ -1,165 +1,174 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../lib/puppettest' require 'puppettest' require 'test/unit' require 'facter' class TestHost < Test::Unit::TestCase include PuppetTest def setup super @hosttype = Puppet::Type.type(:host) @provider = @hosttype.defaultprovider # Make sure they aren't using something funky like netinfo unless @provider.name == :parsed @hosttype.defaultprovider = @hosttype.provider(:parsed) end cleanup do @hosttype.defaultprovider = nil end if @provider.respond_to?(:default_target=) @default_file = @provider.default_target cleanup do @provider.default_target = @default_file end @target = tempfile() @provider.default_target = @target end end def mkhost if defined? @hcount @hcount += 1 else @hcount = 1 end @catalog ||= mk_catalog host = nil assert_nothing_raised { host = Puppet.type(:host).create( :name => "fakehost%s" % @hcount, :ip => "192.168.27.%s" % @hcount, :alias => "alias%s" % @hcount, :catalog => @catalog ) } return host end def test_list assert_nothing_raised do @hosttype.defaultprovider.prefetch end count = 0 @hosttype.each do |h| count += 1 end assert_equal(0, count, "Found hosts in empty file somehow") end # Darwin will actually write to netinfo here. if Facter.value(:operatingsystem) != "Darwin" or Process.uid == 0 def test_simplehost host = nil # We want to actually use the netinfo provider on darwin if Facter.value(:operatingsystem) == "Darwin" Puppet::Type.type(:host).defaultprovider = nil end assert_nothing_raised { host = Puppet.type(:host).create( :name => "culain", :ip => "192.168.0.3" ) } current_values = nil assert_nothing_raised { current_values = host.retrieve } assert_events([:host_created], host) assert_nothing_raised { current_values = host.retrieve } assert_equal(:present, current_values[host.property(:ensure)]) host[:ensure] = :absent assert_events([:host_removed], host) assert_nothing_raised { current_values = host.retrieve } assert_equal(:absent, current_values[host.property(:ensure)]) end def test_moddinghost # We want to actually use the netinfo provider on darwin if Facter.value(:operatingsystem) == "Darwin" Puppet::Type.type(:host).defaultprovider = nil end host = mkhost() if Facter.value(:operatingsystem) == "Darwin" assert_equal(:netinfo, host[:provider], "Got incorrect provider") end cleanup do host[:ensure] = :absent assert_apply(host) end assert_events([:host_created], host) current_values = nil assert_nothing_raised { current_values = host.retrieve } # This was a hard bug to track down. assert_instance_of(String, current_values[host.property(:ip)]) host[:alias] = %w{madstop kirby yayness} assert_events([:host_changed], host) assert_nothing_raised { current_values = host.retrieve } assert_equal(%w{madstop kirby yayness}, current_values[host.property(:alias)]) host[:ensure] = :absent assert_events([:host_removed], host) end + + def test_invalid_ipaddress + host = mkhost() + + assert_raise(Puppet::Error) { + host[:ip] = "abc.def.ghi.jkl" + } end + end + def test_aliasisproperty assert_equal(:property, @hosttype.attrtype(:alias)) end def test_multivalues host = mkhost assert_raise(Puppet::Error) { host[:alias] = "puppetmasterd yayness" } end def test_puppetalias host = mkhost() assert_nothing_raised { host[:alias] = "testing" } same = host.class["testing"] assert(same, "Could not retrieve by alias") end end