summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xexamples/root/etc/init.d/sleeper2
-rw-r--r--lib/puppet/autoload.rb101
-rw-r--r--lib/puppet/client.rb21
-rw-r--r--lib/puppet/client/master.rb2
-rwxr-xr-xlib/puppet/daemon.rb37
-rw-r--r--lib/puppet/element.rb3
-rwxr-xr-xlib/puppet/filetype.rb8
-rw-r--r--lib/puppet/networkclient.rb4
-rw-r--r--lib/puppet/provider.rb134
-rw-r--r--lib/puppet/provider/group/groupadd.rb29
-rw-r--r--lib/puppet/provider/group/netinfo.rb15
-rw-r--r--lib/puppet/provider/group/pw.rb31
-rw-r--r--lib/puppet/provider/nameservice.rb319
-rw-r--r--lib/puppet/provider/nameservice/netinfo.rb211
-rw-r--r--lib/puppet/provider/nameservice/objectadd.rb45
-rw-r--r--lib/puppet/provider/nameservice/pw.rb22
-rwxr-xr-xlib/puppet/provider/package/apple.rb55
-rwxr-xr-xlib/puppet/provider/package/apt.rb112
-rwxr-xr-xlib/puppet/provider/package/blastwave.rb138
-rwxr-xr-xlib/puppet/provider/package/darwinport.rb95
-rwxr-xr-xlib/puppet/provider/package/dpkg.rb115
-rwxr-xr-xlib/puppet/provider/package/freebsd.rb52
-rwxr-xr-xlib/puppet/provider/package/gem.rb108
-rwxr-xr-xlib/puppet/provider/package/openbsd.rb110
-rwxr-xr-xlib/puppet/provider/package/ports.rb104
-rwxr-xr-xlib/puppet/provider/package/rpm.rb120
-rwxr-xr-xlib/puppet/provider/package/sun.rb179
-rwxr-xr-xlib/puppet/provider/package/sunfreeware.rb15
-rwxr-xr-xlib/puppet/provider/package/yum.rb53
-rwxr-xr-xlib/puppet/provider/service/base.rb135
-rwxr-xr-xlib/puppet/provider/service/debian.rb (renamed from lib/puppet/type/service/debian.rb)18
-rwxr-xr-xlib/puppet/provider/service/init.rb (renamed from lib/puppet/type/service/init.rb)45
-rwxr-xr-xlib/puppet/provider/service/redhat.rb (renamed from lib/puppet/type/service/redhat.rb)23
-rwxr-xr-xlib/puppet/provider/service/smf.rb (renamed from lib/puppet/type/service/smf.rb)34
-rw-r--r--lib/puppet/provider/user/netinfo.rb91
-rw-r--r--lib/puppet/provider/user/pw.rb41
-rw-r--r--lib/puppet/provider/user/useradd.rb46
-rw-r--r--lib/puppet/transaction.rb5
-rw-r--r--lib/puppet/type.rb297
-rwxr-xr-xlib/puppet/type/cron.rb44
-rwxr-xr-xlib/puppet/type/group.rb91
-rwxr-xr-xlib/puppet/type/nameservice.rb78
-rw-r--r--lib/puppet/type/package.rb248
-rwxr-xr-xlib/puppet/type/package/apple.rb41
-rwxr-xr-xlib/puppet/type/package/apt.rb107
-rwxr-xr-xlib/puppet/type/package/blastwave.rb136
-rwxr-xr-xlib/puppet/type/package/darwinport.rb97
-rwxr-xr-xlib/puppet/type/package/dpkg.rb113
-rwxr-xr-xlib/puppet/type/package/freebsd.rb19
-rwxr-xr-xlib/puppet/type/package/gem.rb119
-rwxr-xr-xlib/puppet/type/package/openbsd.rb112
-rwxr-xr-xlib/puppet/type/package/ports.rb103
-rwxr-xr-xlib/puppet/type/package/rpm.rb121
-rwxr-xr-xlib/puppet/type/package/sun.rb174
-rwxr-xr-xlib/puppet/type/package/sunfreeware.rb7
-rwxr-xr-xlib/puppet/type/package/yum.rb52
-rwxr-xr-xlib/puppet/type/parsedtype.rb6
-rw-r--r--lib/puppet/type/service.rb319
-rwxr-xr-xlib/puppet/type/service/base.rb12
-rw-r--r--lib/puppet/type/state.rb230
-rwxr-xr-xlib/puppet/type/user.rb204
-rw-r--r--lib/puppet/type/yumrepo.rb28
-rw-r--r--lib/puppet/type/zone.rb8
-rw-r--r--lib/puppet/util.rb97
-rw-r--r--test/other/autoload.rb111
-rw-r--r--test/other/provider.rb165
-rwxr-xr-xtest/providers/group.rb246
-rw-r--r--test/providers/nameservice.rb36
-rw-r--r--test/providers/package.rb86
-rw-r--r--test/providers/user.rb528
-rwxr-xr-xtest/puppet/utiltest.rb42
-rw-r--r--test/puppettest.rb107
-rw-r--r--test/server/pelement.rb5
-rw-r--r--test/types/basic.rb2
-rwxr-xr-xtest/types/cron.rb12
-rwxr-xr-xtest/types/group.rb302
-rw-r--r--test/types/package.rb129
-rw-r--r--test/types/query.rb3
-rw-r--r--test/types/service.rb16
-rw-r--r--test/types/state.rb39
-rw-r--r--test/types/type.rb83
-rwxr-xr-xtest/types/user.rb370
82 files changed, 4994 insertions, 2829 deletions
diff --git a/examples/root/etc/init.d/sleeper b/examples/root/etc/init.d/sleeper
index 121d19940..a4e28a2fb 100755
--- a/examples/root/etc/init.d/sleeper
+++ b/examples/root/etc/init.d/sleeper
@@ -7,7 +7,7 @@ path=`echo $script | sed 's/etc..*/bin/'`
PATH=$PATH:$path
-ps=`facter ps | cut -d ' ' -f3-`
+ps=`facter ps`
if [ -z "$ps" ]; then
ps="ps -ef"
diff --git a/lib/puppet/autoload.rb b/lib/puppet/autoload.rb
new file mode 100644
index 000000000..4e5d07e31
--- /dev/null
+++ b/lib/puppet/autoload.rb
@@ -0,0 +1,101 @@
+# I have no idea what's going on here, but...
+unless defined? MissingSourceFile
+ class MissingSourceFile < RuntimeError
+ end
+end
+# Autoload paths, either based on names or all at once.
+class Puppet::Autoload
+ include Puppet::Util
+
+ @autoloaders = {}
+
+ attr_accessor :object, :path, :objwarn, :wrap
+
+
+ class << self
+ attr_reader :autoloaders
+ private :autoloaders
+ end
+ Puppet::Util.classproxy self, :autoloaders, "[]", "[]=", :clear
+
+ attr_reader :loaded
+ private :loaded
+
+ Puppet::Util.proxy self, :loaded, :clear
+
+ def initialize(obj, path, options = {})
+ @path = path.to_s
+ @object = obj
+
+ self.class[obj] = self
+
+ options.each do |opt, value|
+ opt = opt.intern if opt.is_a? String
+ begin
+ self.send(opt.to_s + "=", value)
+ rescue NoMethodError
+ raise ArgumentError, "%s is not a valid option" % opt
+ end
+ end
+
+ unless defined? @wrap
+ @wrap = true
+ end
+
+ @loaded = {}
+ end
+
+ def load(name)
+ name = symbolize(name)
+
+ path = File.join(@path, name.to_s + ".rb")
+
+ begin
+ Kernel.load path, @wrap
+ @loaded[name] = true
+ return true
+ rescue MissingSourceFile
+ return false
+ rescue LoadError => detail
+ # I have no idea what's going on here, but different versions
+ # of ruby are raising different errors on missing files.
+ unless detail.to_s =~ /^no such file/i
+ warn "Could not autoload %s: %s" % [name, detail]
+ end
+ return false
+ end
+ end
+
+ def loaded?(name)
+ name = symbolize(name)
+ @loaded[name]
+ end
+
+ def loadall
+ # Load every instance of everything we can find.
+ $:.each do |dir|
+ fdir = File.join(dir, @path)
+ if FileTest.exists?(fdir) and FileTest.directory?(fdir)
+ Dir.glob("#{fdir}/*.rb").each do |file|
+ # Load here, rather than require, so that facts
+ # can be reloaded. This has some short-comings, I
+ # believe, but it works as long as real classes
+ # aren't used.
+ name = File.basename(file).sub(".rb", '').intern
+ next if @loaded.include? name
+ begin
+ Kernel.load file, @wrap
+ @loaded[name] = true
+ rescue => detail
+ #if Puppet[:debug]
+ # puts detail.backtrace
+ #end
+ warn "Could not autoload %s: %s" % [file.inspect, detail]
+ end
+ end
+ end
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/client.rb b/lib/puppet/client.rb
index 9ee0d55d8..cf522a994 100644
--- a/lib/puppet/client.rb
+++ b/lib/puppet/client.rb
@@ -26,9 +26,15 @@ module Puppet
def initcerts
unless self.readcert
- unless self.requestcert
- return nil
- end
+ #if self.is_a? Puppet::Client::CA
+ unless self.requestcert
+ return nil
+ end
+ #else
+ # return nil
+ #end
+ #unless self.requestcert
+ #end
end
# unless we have a driver, we're a local client and we can't add
@@ -95,6 +101,15 @@ module Puppet
end
end
+ # Are we a local client?
+ def local?
+ if defined? @local and @local
+ true
+ else
+ false
+ end
+ end
+
# A wrapper method to run and then store the last run time
def runnow
if self.stopping
diff --git a/lib/puppet/client/master.rb b/lib/puppet/client/master.rb
index 505a2109b..906f8be3c 100644
--- a/lib/puppet/client/master.rb
+++ b/lib/puppet/client/master.rb
@@ -508,7 +508,7 @@ class Puppet::Client::MasterClient < Puppet::Client
File.basename(object[:path]).sub(".rb",'')
load object[:path]
rescue => detail
- Puppet.warning "Could not load %s: %s" %
+ Puppet.warning "Could not reload plugin %s: %s" %
[object[:path], detail]
end
end
diff --git a/lib/puppet/daemon.rb b/lib/puppet/daemon.rb
index 13ae3f6a2..c341bf219 100755
--- a/lib/puppet/daemon.rb
+++ b/lib/puppet/daemon.rb
@@ -157,24 +157,45 @@ module Puppet
}
end
+
unless defined? @driver
Puppet.err "Cannot request a certificate without a defined target"
return false
end
- Puppet.info "Creating a new certificate request for %s" % @fqdn
- name = OpenSSL::X509::Name.new([["CN", @fqdn]])
+ unless defined? @csr
+ Puppet.info "Creating a new certificate request for %s" % @fqdn
+ name = OpenSSL::X509::Name.new([["CN", @fqdn]])
- @csr = OpenSSL::X509::Request.new
- @csr.version = 0
- @csr.subject = name
- @csr.public_key = @key.public_key
- @csr.sign(@key, OpenSSL::Digest::MD5.new)
+ @csr = OpenSSL::X509::Request.new
+ @csr.version = 0
+ @csr.subject = name
+ @csr.public_key = @key.public_key
+ @csr.sign(@key, OpenSSL::Digest::MD5.new)
+ end
Puppet.info "Requesting certificate"
+ # We can only request a client with a CA client, so we need
+ # to create one if we don't already have one (or if we're not a CA
+ # server).
+ caclient = nil
+ if @driver.is_a? Puppet::Client::CA or @driver.is_a? Puppet::Server::CA
+ caclient = @driver
+ else
+ # Create a CA client with which to request the cert.
+ if @driver.local?
+ raise Puppet::DevError,
+ "Incorrect setup for a local CA request"
+ end
+ caclient = Puppet::Client::CA.new(
+ :Port => @driver.puppet_port,
+ :Server => @driver.puppet_server
+ )
+ end
+
begin
- cert, cacert = @driver.getcert(@csr.to_pem)
+ cert, cacert = caclient.getcert(@csr.to_pem)
rescue => detail
if Puppet[:debug]
puts detail.backtrace
diff --git a/lib/puppet/element.rb b/lib/puppet/element.rb
index 7abc18fda..cc682f2e4 100644
--- a/lib/puppet/element.rb
+++ b/lib/puppet/element.rb
@@ -11,6 +11,7 @@ class Puppet::Element
class << self
attr_accessor :doc, :nodoc
+ include Puppet::Util
end
# all of our subclasses must respond to each of these methods...
@@ -22,7 +23,7 @@ class Puppet::Element
@@interface_methods.each { |method|
self.send(:define_method,method) {
raise Puppet::DevError, "%s has not overridden %s" %
- [self.class,method]
+ [self.class.name,method]
}
}
diff --git a/lib/puppet/filetype.rb b/lib/puppet/filetype.rb
index 168d8e472..7a4067c9d 100755
--- a/lib/puppet/filetype.rb
+++ b/lib/puppet/filetype.rb
@@ -145,6 +145,10 @@ module Puppet
# Handle Linux-style cron tabs.
newfiletype(:crontab) do
def initialize(user)
+ self.path = user
+ end
+
+ def path=(user)
begin
uid = Puppet::Util.uid(user)
rescue Puppet::Error => detail
@@ -153,7 +157,7 @@ module Puppet
# We have to have the user name, not the uid, because some
# systems *cough*linux*cough* require it that way
- @path = user
+ @path = uid
end
# Read a specific @path's cron tab.
@@ -177,7 +181,7 @@ module Puppet
private
# Only add the -u flag when the @path is different. Fedora apparently
- # does not think I should be allowed to set the @path to my
+ # does not think I should be allowed to set the @path to my own user name
def cmdbase
cmd = nil
if @path == Process.uid
diff --git a/lib/puppet/networkclient.rb b/lib/puppet/networkclient.rb
index fa5a21957..82822533a 100644
--- a/lib/puppet/networkclient.rb
+++ b/lib/puppet/networkclient.rb
@@ -30,6 +30,7 @@ module Puppet
Puppet.err "Could not load client network libs: %s" % $noclientnetworking
else
class NetworkClient < XMLRPC::Client
+ attr_accessor :puppet_server, :puppet_port
@clients = {}
# Create a netclient for each handler
@@ -126,6 +127,9 @@ module Puppet
hash[:Server] ||= "localhost"
hash[:Port] ||= Puppet[:masterport]
+ @puppet_server = hash[:Server]
+ @puppet_port = hash[:Port]
+
@puppetserver = hash[:Server]
super(
diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb
new file mode 100644
index 000000000..44851c16b
--- /dev/null
+++ b/lib/puppet/provider.rb
@@ -0,0 +1,134 @@
+# The container class for implementations.
+class Puppet::Provider
+ include Puppet::Util
+
+ Puppet::Util.logmethods(self, true)
+
+ class << self
+ # Include the util module so we have access to things like 'binary'
+ include Puppet::Util
+ attr_accessor :name, :model, :doc
+ end
+
+ attr_accessor :model
+
+ def self.command(name)
+ name = symbolize(name)
+
+ if command = @commands[name]
+ return command
+ elsif superclass.respond_to? :command and command = superclass.command(name)
+ return command
+ else
+ raise Puppet::DevError, "No command %s defined for provider %s" %
+ [name, self.name]
+ end
+ end
+
+ # Define one or more binaries we'll be using
+ def self.commands(hash)
+ hash.each do |name, path|
+ name = symbolize(name)
+ path = binary(path) unless path =~ /^\//
+ @commands[name] = path
+ confine :exists => path
+ end
+ end
+
+ def self.confine(hash)
+ hash.each do |p,v|
+ if v.is_a? Array
+ @confines[p] += v
+ else
+ @confines[p] << v
+ end
+ end
+ end
+
+ # Does this implementation match all of the default requirements?
+ def self.default?
+ if @defaults.find do |fact, value|
+ Facter[fact].value.to_s.downcase.intern != value.to_s.downcase.intern
+ end
+ return false
+ else
+ return true
+ end
+ end
+
+ # Store how to determine defaults.
+ def self.defaultfor(hash)
+ hash.each do |d,v|
+ @defaults[d] = v
+ end
+ end
+
+ def self.defaultnum
+ @defaults.length
+ end
+
+ # Specify a documentation string.
+ def self.desc(str)
+ @doc = str
+ end
+
+ def self.initvars
+ @defaults = {}
+ @commands = {}
+ @confines = Hash.new do |hash, key|
+ hash[key] = []
+ end
+ end
+
+ self.initvars
+
+ # Check whether this implementation is suitable for our platform.
+ def self.suitable?
+ # A single false result is sufficient to turn the whole thing down.
+ @confines.each do |check, values|
+ case check
+ when :exists:
+ values.each do |value|
+ unless value and FileTest.exists? value
+ return false
+ end
+ end
+ when :true:
+ values.each do |v|
+ return false unless v
+ end
+ when :false:
+ values.each do |v|
+ return false if v
+ end
+ else # Just delegate everything else to facter
+ result = Facter.send(check).to_s.downcase.intern
+
+ found = values.find do |v|
+ result == v.to_s.downcase.intern
+ end
+ return false unless found
+ end
+ end
+
+ return true
+ end
+
+ def command(name)
+ self.class.command(name)
+ end
+
+ def initialize(model)
+ @model = model
+ end
+
+ def name
+ @model.name
+ end
+
+ def to_s
+ "%s(provider=%s)" % [@model.to_s, self.class.name]
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/group/groupadd.rb b/lib/puppet/provider/group/groupadd.rb
new file mode 100644
index 000000000..4c204ddf6
--- /dev/null
+++ b/lib/puppet/provider/group/groupadd.rb
@@ -0,0 +1,29 @@
+require 'puppet/provider/nameservice/objectadd'
+
+Puppet::Type.type(:group).provide :groupadd, :parent => Puppet::Provider::NameService::ObjectAdd do
+ desc "Group management via ``groupadd`` and its ilk. The default
+ for most platforms"
+
+ commands :add => "groupadd", :delete => "groupdel", :modify => "groupmod"
+
+ verify :gid, "GID must be an integer" do |value|
+ value.is_a? Integer
+ end
+
+ def addcmd
+ cmd = [command(:add)]
+ if gid = @model[:gid]
+ unless gid == :absent
+ cmd << flag(:gid) << "'%s'" % @model[:gid]
+ end
+ end
+ if @model[:allowdupe] == :true
+ cmd << "-o"
+ end
+ cmd << @model[:name]
+
+ return cmd.join(" ")
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/group/netinfo.rb b/lib/puppet/provider/group/netinfo.rb
new file mode 100644
index 000000000..4be1e4f35
--- /dev/null
+++ b/lib/puppet/provider/group/netinfo.rb
@@ -0,0 +1,15 @@
+# Manage NetInfo POSIX objects. Probably only used on OS X, but I suppose
+# it could be used elsewhere.
+require 'puppet/provider/nameservice/netinfo'
+
+Puppet::Type.type(:group).provide :netinfo, :parent => Puppet::Provider::NameService::NetInfo do
+ desc "Group management using NetInfo."
+ NIREPORT = binary("nireport")
+ NIUTIL = binary("niutil")
+ confine :exists => NIREPORT
+ confine :exists => NIUTIL
+
+ defaultfor :operatingsystem => :darwin
+end
+
+# $Id$
diff --git a/lib/puppet/provider/group/pw.rb b/lib/puppet/provider/group/pw.rb
new file mode 100644
index 000000000..48934a3c5
--- /dev/null
+++ b/lib/puppet/provider/group/pw.rb
@@ -0,0 +1,31 @@
+require 'puppet/provider/nameservice/pw'
+
+Puppet::Type.type(:group).provide :pw, :parent => Puppet::Provider::NameService::PW do
+ desc "Group management via ``pw``. Only works on FreeBSD."
+
+ commands :pw => "/usr/sbin/pw"
+ defaultfor :operatingsystem => :freebsd
+
+ verify :gid, "GID must be an integer" do |value|
+ value.is_a? Integer
+ end
+
+ def addcmd
+ cmd = [command(:pw), "groupadd", @model[:name]]
+ if gid = @model[:gid]
+ unless gid == :absent
+ cmd << flag(:gid) << "'%s'" % @model[:gid]
+ end
+ end
+
+ # Apparently, contrary to the man page, groupadd does
+ # not accept -o.
+ #if @parent[:allowdupe] == :true
+ # cmd << "-o"
+ #end
+
+ return cmd.join(" ")
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/nameservice.rb b/lib/puppet/provider/nameservice.rb
new file mode 100644
index 000000000..96c1b6f4d
--- /dev/null
+++ b/lib/puppet/provider/nameservice.rb
@@ -0,0 +1,319 @@
+require 'puppet'
+
+# This is the parent class of all NSS classes. They're very different in
+# their backend, but they're pretty similar on the front-end. This class
+# provides a way for them all to be as siilar as possible.
+class Puppet::Provider::NameService < Puppet::Provider
+ class << self
+
+ def list
+ objects = []
+ listbyname do |name|
+ obj = nil
+ check = model.validstates
+ if obj = model[name]
+ obj[:check] = check
+ else
+ # unless it exists, create it as an unmanaged object
+ obj = model.create(:name => name, :check => check)
+ end
+
+ next unless obj # In case there was an error somewhere
+
+ objects << obj
+ yield obj if block_given?
+ end
+
+ objects
+ end
+
+ def option(name, option)
+ name = name.intern if name.is_a? String
+ if defined? @options and @options.include? name and @options[name].include? option
+ return @options[name][option]
+ else
+ return nil
+ end
+ end
+
+ def options(name, hash)
+ unless model.validstate?(name)
+ raise Puppet::DevError, "%s is not a valid state for %s" %
+ [name, model.name]
+ end
+ @options ||= {}
+ @options[name] ||= {}
+
+ # Set options individually, so we can call the options method
+ # multiple times.
+ hash.each do |param, value|
+ @options[name][param] = value
+ end
+ end
+
+ # List everything out by name. Abstracted a bit so that it works
+ # for both users and groups.
+ def listbyname
+ names = []
+ Etc.send("set%sent" % section())
+ begin
+ while ent = Etc.send("get%sent" % section())
+ names << ent.name
+ if block_given?
+ yield ent.name
+ end
+ end
+ ensure
+ Etc.send("end%sent" % section())
+ end
+
+ return names
+ end
+
+ # This is annoying, but there really aren't that many options,
+ # and this *is* built into Ruby.
+ def section
+ unless defined? @model
+ raise Puppet::DevError,
+ "Cannot determine Etc section without a model"
+
+ end
+
+ if @model.name == :group
+ "gr"
+ else
+ "pw"
+ end
+ end
+
+ def disabled_validate(name, value)
+ name = name.intern if name.is_a? String
+ if @checks.include? name
+ block = @checks[name][:block]
+ unless block.call(value)
+ raise ArgumentError, "Invalid value %s: %s" %
+ [value, @checks[name][:error]]
+ end
+ end
+ end
+
+ def verify(name, error, &block)
+ name = name.intern if name.is_a? String
+ @checks ||= {}
+ @checks[name] = {:error => error, :block => block}
+ end
+
+ private
+
+ def op(state)
+ @ops[state.name] || ("-" + state.name)
+ end
+ end
+
+ # Autogenerate either a uid or a gid. This is hard-coded: we can only
+ # generate one field type per class.
+ def autogen
+ highest = 0
+
+ group = method = nil
+ case @model.class.name
+ when :user: group = :passwd; method = :uid
+ when :group: group = :group; method = :gid
+ else
+ raise Puppet::DevError, "Invalid model name %s" % model
+ end
+
+ # Make sure we don't use the same value multiple times
+ if defined? @@prevauto
+ @@prevauto += 1
+ else
+ Etc.send(group) { |obj|
+ if obj.gid > highest
+ unless obj.send(method) > 65000
+ highest = obj.send(method)
+ end
+ end
+ }
+
+ @@prevauto = highest + 1
+ end
+
+ return @@prevauto
+ end
+
+ def autogen_gid
+ autogen(@model.class.name)
+ end
+
+ def autogen_uid
+ autogen(@model.class.name)
+ end
+
+ def create
+ self.ensure = :present
+ end
+
+ def delete
+ self.ensure = :absent
+ end
+
+ def ensure
+ if exists?
+ :present
+ else
+ :absent
+ end
+ end
+
+ # This is only used when creating or destroying the object.
+ def ensure=(value)
+ cmd = nil
+ event = nil
+ case value
+ when :absent
+ # we need to remove the object...
+ unless exists?
+ info "already absent"
+ # the object already doesn't exist
+ return nil
+ end
+
+ # again, needs to be set by the ind. state or its
+ # parent
+ cmd = self.deletecmd
+ type = "delete"
+ when :present
+ if exists?
+ info "already exists"
+ # The object already exists
+ return nil
+ end
+
+ # blah blah, define elsewhere, blah blah
+ cmd = self.addcmd
+ type = "create"
+ end
+
+ begin
+ execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not %s %s %s: %s" %
+ [type, @model.class.name, @model.name, detail]
+ end
+ end
+
+ # Does our object exist?
+ def exists?
+ if getinfo(true)
+ return true
+ else
+ return false
+ end
+ end
+
+ # Retrieve a specific value by name.
+ def get(param)
+ if hash = getinfo(false)
+ return hash[param]
+ else
+ return nil
+ end
+ end
+
+ # Retrieve what we can about our object
+ def getinfo(refresh)
+ if @objectinfo.nil? or refresh == true
+ @etcmethod ||= ("get" + self.class.section().to_s + "nam").intern
+ begin
+ @objectinfo = Etc.send(@etcmethod, @model[:name])
+ rescue ArgumentError => detail
+ @objectinfo = nil
+ end
+ end
+
+ # Now convert our Etc struct into a hash.
+ if @objectinfo
+ return info2hash(@objectinfo)
+ else
+ return nil
+ end
+ end
+
+ # The list of all groups the user is a member of. Different
+ # user mgmt systems will need to override this method.
+ def groups
+ groups = []
+
+ # Reset our group list
+ Etc.setgrent
+
+ user = @model[:name]
+
+ # Now iterate across all of the groups, adding each one our
+ # user is a member of
+ while group = Etc.getgrent
+ members = group.mem
+
+ if members.include? user
+ groups << group.name
+ end
+ end
+
+ # We have to close the file, so each listing is a separate
+ # reading of the file.
+ Etc.endgrent
+
+ groups.join(",")
+ end
+
+ # Convert the Etc struct into a hash.
+ def info2hash(info)
+ hash = {}
+ self.class.model.validstates.each do |param|
+ method = posixmethod(param)
+ if info.respond_to? method
+ hash[param] = info.send(posixmethod(param))
+ end
+ end
+
+ return hash
+ end
+
+ def initialize(model)
+ super
+
+ @objectinfo = nil
+ end
+
+ #
+ def method_missing(name, *args)
+ name = name.to_s
+
+ # Make sure it's a valid state. We go up our class structure instead of
+ # our model's because the model is fake during testing.
+ unless self.class.model.validstate?(name.sub("=",''))
+ raise Puppet::DevError, "%s is not a valid %s state" %
+ [name, @model.class.name]
+ end
+
+ # Each class has to override this manually
+ if name =~ /=/
+ set(name.to_s.sub("=", ''), *args)
+ else
+ return get(name.intern) || :absent
+ end
+ end
+
+ def set(param, value)
+ #self.class.validate(param, value)
+ cmd = modifycmd(param, value)
+ begin
+ execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not set %s on %s[%s]: %s" %
+ [param, @model.class.name, @model.name, detail]
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/nameservice/netinfo.rb b/lib/puppet/provider/nameservice/netinfo.rb
new file mode 100644
index 000000000..d3a51bcf0
--- /dev/null
+++ b/lib/puppet/provider/nameservice/netinfo.rb
@@ -0,0 +1,211 @@
+# Manage NetInfo POSIX objects. Probably only used on OS X, but I suppose
+# it could be used elsewhere.
+
+require 'puppet'
+require 'puppet/provider/nameservice'
+
+class Puppet::Provider::NameService
+class NetInfo < Puppet::Provider::NameService
+ # Attempt to flush the database, but this doesn't seem to work at all.
+ def self.flush
+ begin
+ output = execute("/usr/sbin/lookupd -flushcache 2>&1")
+ rescue Puppet::ExecutionFailure
+ # Don't throw an error; it's just a failed cache flush
+ Puppet.err "Could not flush lookupd cache: %s" % output
+ end
+ end
+
+ # Similar to posixmethod, what key do we use to get data? Defaults
+ # to being the object name.
+ def self.netinfodir
+ if defined? @netinfodir
+ return @netinfodir
+ else
+ return @model.name.to_s + "s"
+ end
+ end
+
+ def self.finish
+ case self.name
+ when :uid:
+ noautogen
+ when :gid:
+ noautogen
+ end
+ end
+
+ # How to add an object.
+ def addcmd
+ creatorcmd("-create")
+ end
+
+ def creatorcmd(arg)
+ cmd = ["niutil"]
+ cmd << arg
+
+ cmd << "/" << "/%s/%s" %
+ [self.class.netinfodir(), @model[:name]]
+ return cmd.join(" ")
+ end
+
+ def deletecmd
+ creatorcmd("-destroy")
+ end
+
+ def ensure=(arg)
+ super
+
+ # Because our stupid type can't create the whole thing at once,
+ # we have to do this hackishness. Yay.
+ if arg == :present
+ if @model.class.name == :user
+ notice modifycmd(:groups, @model[:groups]).inspect
+ end
+ # We need to generate the id if it's missing.
+ @model.class.validstates.each do |name|
+ next if name == :ensure
+ unless val = @model[name]
+ if (@model.class.name == :user and name == :uid) or
+ (@model.class.name == :group and name == :gid)
+ val = autogen()
+ else
+ # No value, and it's not required, so skip it.
+ info "No value for %s" % name
+ next
+ end
+ end
+ self.send(name.to_s + "=", val)
+ end
+ end
+ end
+
+ #def exists?
+ # if self.report(:name)
+ # return true
+ # else
+ # return false
+ # end
+ #end
+
+ # Retrieve a specific value by name.
+ def get(param)
+ hash = getinfo(false)
+ if hash
+ return hash[param]
+ else
+ return :absent
+ end
+ end
+
+ # Retrieve everything about this object at once, instead of separately.
+ def getinfo(refresh = false)
+ if refresh or (! defined? @infohash or ! @infohash)
+ states = [:name] + self.class.model.validstates
+ states.delete(:ensure) if states.include? :ensure
+ @infohash = report(*states)
+ end
+
+ return @infohash
+ end
+
+ def modifycmd(param, value)
+ cmd = ["niutil"]
+
+ cmd << "-createprop" << "/" << "/%s/%s" %
+ [self.class.netinfodir, @model[:name]]
+
+ if key = netinfokey(param)
+ cmd << key << "'%s'" % value
+ else
+ raise Puppet::DevError,
+ "Could not find netinfokey for state %s" %
+ self.class.name
+ end
+ cmd.join(" ")
+ end
+
+ # Determine the flag to pass to our command.
+ def netinfokey(name)
+ name = symbolize(name)
+ self.class.option(name, :key) || name
+ end
+
+ # Retrieve the data, yo.
+ # FIXME This should retrieve as much information as possible,
+ # rather than retrieving it one at a time.
+ def report(*params)
+ dir = self.class.netinfodir()
+ cmd = ["nireport", "/", "/%s" % dir]
+
+ # We require the name in order to know if we match. There's no
+ # way to just report on our individual object, we have to get the
+ # whole list.
+ params.unshift :name unless params.include? :name
+
+ params.each do |param|
+ if key = netinfokey(param)
+ cmd << key.to_s
+ else
+ raise Puppet::DevError,
+ "Could not find netinfokey for state %s" %
+ self.class.name
+ end
+ end
+ self.debug "Executing %s" % cmd.join(" ").inspect
+
+ %x{#{cmd.join(" ")} 2>&1}.split("\n").each { |line|
+ values = line.split(/\t/)
+
+ hash = {}
+ params.zip(values).each do |param, value|
+ next if value == '#NoValue#'
+ hash[param] = if value =~ /^[-0-9]+$/
+ Integer(value)
+ else
+ value
+ end
+ end
+
+ if hash[:name] == @model[:name]
+ return hash
+ else
+ next
+ end
+
+#
+# if line =~ /^(\w+)\s+(.+)$/
+# name = $1
+# value = $2.sub(/\s+$/, '')
+#
+# if name == @model[:name]
+# if value =~ /^[-0-9]+$/
+# return Integer(value)
+# else
+# return value
+# end
+# end
+ }
+
+ return nil
+ end
+
+ def retrieve
+ raise "wtf?"
+ @is = report() || :absent
+ end
+
+ def setuserlist(group, list)
+ cmd = "niutil -createprop / /groups/%s users %s" %
+ [group, list.join(",")]
+ begin
+ output = execute(cmd)
+ rescue Puppet::Execution::Failure => detail
+ raise Puppet::Error, "Failed to set user list on %s: %s" %
+ [group, detail]
+ end
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/nameservice/objectadd.rb b/lib/puppet/provider/nameservice/objectadd.rb
new file mode 100644
index 000000000..056ac5e84
--- /dev/null
+++ b/lib/puppet/provider/nameservice/objectadd.rb
@@ -0,0 +1,45 @@
+require 'puppet/provider/nameservice'
+
+class Puppet::Provider::NameService
+class ObjectAdd < Puppet::Provider::NameService
+ # Does the object already exist?
+ def self.exists?(obj)
+ if obj.getinfo(true)
+ return true
+ else
+ return false
+ end
+ end
+
+ def deletecmd
+ [command(:delete), @model[:name]].join(" ")
+ end
+
+ # Determine the flag to pass to our command.
+ def flag(name)
+ name = name.intern if name.is_a? String
+ self.class.option(name, :flag) || "-" + name.to_s[0,1]
+ end
+
+ def modifycmd(param, value)
+ cmd = [command(:modify),
+ flag(param),
+ "'%s'" % value]
+ if @model[:allowdupe] == :true
+ cmd << "-o"
+ end
+ cmd << @model[:name]
+
+ return cmd.join(" ")
+ end
+
+ def posixmethod(name)
+ name = name.intern if name.is_a? String
+ method = self.class.option(name, :method) || name
+
+ return method
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/nameservice/pw.rb b/lib/puppet/provider/nameservice/pw.rb
new file mode 100644
index 000000000..e151a258d
--- /dev/null
+++ b/lib/puppet/provider/nameservice/pw.rb
@@ -0,0 +1,22 @@
+require 'puppet/provider/nameservice/objectadd'
+
+class Puppet::Provider::NameService
+class PW < ObjectAdd
+ def deletecmd
+ "#{command(:pw)} #{@model.class.name.to_s}del %s" % @model[:name]
+ end
+
+ def modifycmd(param, value)
+ cmd = [
+ command(:pw),
+ "#{@model.class.name.to_s}mod",
+ @model[:name],
+ flag(param),
+ "'%s'" % value
+ ]
+ return cmd.join(" ")
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/apple.rb b/lib/puppet/provider/package/apple.rb
new file mode 100755
index 000000000..a9a86652d
--- /dev/null
+++ b/lib/puppet/provider/package/apple.rb
@@ -0,0 +1,55 @@
+# OS X Packaging sucks. We can install packages, but that's about it.
+Puppet::Type.type(:package).provide :apple do
+ desc "Package management based on OS X's builtin packaging system. This is
+ essentially the simplest and least functional package system in existence --
+ it only supports installation; no deletion or upgrades."
+
+ confine :exists => "/Library/Receipts"
+ confine :exists => "/usr/sbin/installer"
+
+ defaultfor :operatingsystem => :darwin
+
+ def self.listbyname
+ Dir.entries("/Library/Receipts").find { |f|
+ f =~ /\.pkg$/
+ }.collect { |f|
+ name = f.sub(/\.pkg/, '')
+ yield name if block_given?
+
+ name
+ }
+ end
+
+ def self.list
+ listbyname.collect do |name|
+ Puppet.type(:package).installedpkg(
+ :name => name,
+ :provider => :apple,
+ :ensure => :installed
+ )
+ end
+ end
+
+ def query
+ if FileTest.exists?("/Library/Receipts/#{@model[:name]}.pkg")
+ return {:name => @model[:name], :ensure => :present}
+ else
+ return nil
+ end
+ end
+
+ def install
+ source = nil
+ unless source = @model[:source]
+ self.fail "Mac OS X packages must specify a package source"
+ end
+
+ begin
+ output = execute("/usr/sbin/installer -pkg #{source} -target / 2>&1")
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/apt.rb b/lib/puppet/provider/package/apt.rb
new file mode 100755
index 000000000..7f2cef375
--- /dev/null
+++ b/lib/puppet/provider/package/apt.rb
@@ -0,0 +1,112 @@
+Puppet::Type.type(:package).provide :apt, :parent => :dpkg do
+ desc "Package management via ``apt-get``."
+
+ APT = "/usr/bin/apt-get"
+
+ confine :exists => APT
+
+ defaultfor :operatingsystem => :debian
+
+ ENV['DEBIAN_FRONTEND'] = "noninteractive"
+
+ # A derivative of DPKG; this is how most people actually manage
+ # Debian boxes, and the only thing that differs is that it can
+ # install packages from remote sites.
+
+ def checkforcdrom
+ unless defined? @@checkedforcdrom
+ if FileTest.exists? "/etc/apt/sources.list"
+ if File.read("/etc/apt/sources.list") =~ /^[^#]*cdrom:/
+ @@checkedforcdrom = true
+ else
+ @@checkedforcdrom = false
+ end
+ else
+ # This is basically a pathalogical case, but we'll just
+ # ignore it
+ @@checkedforcdrom = false
+ end
+ end
+
+ if @@checkedforcdrom and @model[:allowcdrom] != :true
+ raise Puppet::Error,
+ "/etc/apt/sources.list contains a cdrom source; not installing. Use 'allowcdrom' to override this failure."
+ end
+ end
+
+ # Install a package using 'apt-get'. This function needs to support
+ # installing a specific version.
+ def install
+ should = @model.should(:ensure)
+
+ checkforcdrom()
+
+ str = @model[:name]
+ case should
+ when true, false, Symbol
+ # pass
+ else
+ # Add the package version
+ str += "=%s" % should
+ end
+ cmd = "/usr/bin/apt-get -q -y install %s" % str
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ # What's the latest package version available?
+ def latest
+ cmd = "/usr/bin/apt-cache showpkg %s" % @model[:name]
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+
+ if output =~ /Versions:\s*\n((\n|.)+)^$/
+ versions = $1
+ version = versions.split(/\n/).collect { |version|
+ if version =~ /^([^\(]+)\(/
+ $1
+ else
+ self.warning "Could not match version '%s'" % version
+ nil
+ end
+ }.reject { |vers| vers.nil? }.sort[-1]
+
+ unless version
+ self.debug "No latest version"
+ if Puppet[:debug]
+ print output
+ end
+ end
+
+ return version
+ else
+ self.err "Could not match string"
+ end
+ end
+
+ def update
+ self.install
+ end
+
+ def uninstall
+ cmd = "/usr/bin/apt-get -y -q remove %s" % @model[:name]
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ def versionable?
+ true
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/blastwave.rb b/lib/puppet/provider/package/blastwave.rb
new file mode 100755
index 000000000..b6c449415
--- /dev/null
+++ b/lib/puppet/provider/package/blastwave.rb
@@ -0,0 +1,138 @@
+# Packaging using Blastwave's pkg-get program.
+Puppet::Type.type(:package).provide :blastwave, :parent => :sun do
+ desc "Package management using Blastwave.org's ``pkg-get`` command on Solaris."
+ if FileTest.executable?("/opt/csw/bin/pkg-get")
+ PKGGET = "/opt/csw/bin/pkg-get"
+ elsif pkgget = binary("pkg-get")
+ PKGGET = pkgget
+ else
+ PKGGET = nil
+ end
+
+ confine :exists => PKGGET
+
+ # This is so stupid, but then, so is blastwave.
+ ENV["PAGER"] = "/usr/bin/cat"
+
+ def self.extended(mod)
+ unless PKGGET
+ raise Puppet::Error,
+ "The pkg-get command is missing; blastwave packaging unavailable"
+ end
+
+ unless FileTest.exists?("/var/pkg-get/admin")
+ Puppet.notice "It is highly recommended you create '/var/pkg-get/admin'."
+ Puppet.notice "See /var/pkg-get/admin-fullauto"
+ end
+ end
+
+ def self.list(hash = {})
+ blastlist(hash).each do |bhash|
+ bhash.delete(:avail)
+ Puppet::Type.type(:package).installedpkg(bhash)
+ end
+ end
+
+ # Turn our blastwave listing into a bunch of hashes.
+ def self.blastlist(hash)
+ command = "#{PKGGET} -c"
+
+ if hash[:justme]
+ command += " " + hash[:justme]
+ end
+
+ begin
+ output = execute(command)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not get package listing: %s" %
+ detail
+ end
+
+ list = output.split("\n").collect do |line|
+ next if line =~ /^#/
+ next if line =~ /^WARNING/
+ next if line =~ /localrev\s+remoterev/
+
+ blastsplit(line)
+ end.reject { |h| h.nil? }
+
+ if hash[:justme]
+ return list[0]
+ else
+ list.reject! { |h|
+ h[:ensure] == :absent
+ }
+ return list
+ end
+
+ end
+
+ # Split the different lines into hashes.
+ def self.blastsplit(line)
+ if line =~ /\s*(\S+)\s+((\[Not installed\])|(\S+))\s+(\S+)/
+ hash = {}
+ hash[:name] = $1
+ hash[:ensure] = if $2 == "[Not installed]"
+ :absent
+ else
+ $2
+ end
+ hash[:avail] = $5
+
+ if hash[:avail] == "SAME"
+ hash[:avail] = hash[:ensure]
+ end
+ hash[:provider] = :blastwave
+
+ return hash
+ else
+ Puppet.warning "Cannot match %s" % line
+ return nil
+ end
+ end
+
+ def install
+ begin
+ execute("#{PKGGET} -f install #{@model[:name]}")
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error,
+ "Could not install %s: %s" %
+ [@model[:name], detail]
+ end
+ end
+
+ # Retrieve the version from the current package file.
+ def latest
+ hash = self.class.blastlist(:justme => @model[:name])
+ hash[:avail]
+ end
+
+ def query
+ hash = self.class.blastlist(:justme => @model[:name])
+
+ {:ensure => hash[:ensure]}
+ end
+
+ # Remove the old package, and install the new one
+ def update
+ begin
+ execute("#{PKGGET} -f upgrade #{@model[:name]}")
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error,
+ "Could not upgrade %s: %s" %
+ [@model[:name], detail]
+ end
+ end
+
+ def uninstall
+ begin
+ execute("#{PKGGET} -f remove #{@model[:name]}")
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error,
+ "Could not remove %s: %s" %
+ [@model[:name], detail]
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/darwinport.rb b/lib/puppet/provider/package/darwinport.rb
new file mode 100755
index 000000000..ccf0ae82d
--- /dev/null
+++ b/lib/puppet/provider/package/darwinport.rb
@@ -0,0 +1,95 @@
+Puppet::Type.type(:package).provide :darwinport do
+ desc "Package management using DarwinPorts on OS X."
+
+ PORT = "/opt/local/bin/port"
+ confine :exists => PORT, :operatingsystem => "Darwin"
+
+ def self.eachpkgashash
+ # list out all of the packages
+ open("| #{PORT} list installed") { |process|
+ regex = %r{(\S+)\s+@(\S+)\s+(\S+)}
+ fields = [:name, :version, :location]
+ hash = {}
+
+ # now turn each returned line into a package object
+ process.each { |line|
+ hash.clear
+
+ if match = regex.match(line)
+ fields.zip(match.captures) { |field,value|
+ hash[field] = value
+ }
+
+ hash.delete :location
+ hash[:ensure] = hash[:version]
+ yield hash.dup
+ else
+ raise Puppet::DevError,
+ "Failed to match dpkg line %s" % line
+ end
+ }
+ }
+ end
+
+ def self.list
+ packages = []
+
+ eachpkgashash do |hash|
+ pkg = Puppet.type(:package).installedpkg(hash)
+ packages << pkg
+ end
+
+ return packages
+ end
+
+ def install
+ should = @model[:ensure]
+
+ # Seems like you can always say 'upgrade'
+ cmd = "#{PORT} upgrade #{@model[:name]}"
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ def query
+ version = nil
+ self.class.eachpkgashash do |hash|
+ if hash[:name] == @model[:name]
+ return hash
+ end
+ end
+
+ return nil
+ end
+
+ def latest
+ info = %x{#{PORT} search '^#{@model[:name]}$' 2>/dev/null}
+
+ if $? != 0 or info =~ /^Error/
+ return nil
+ end
+
+ ary = info.split(/\s+/)
+ version = ary[2].sub(/^@/, '')
+
+ return version
+ end
+
+ def uninstall
+ cmd = "#{PORT} uninstall #{@model[:name]}"
+ output = %x{#{cmd} 2>&1}
+ if $? != 0
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ def update
+ return install()
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/dpkg.rb b/lib/puppet/provider/package/dpkg.rb
new file mode 100755
index 000000000..a747a884c
--- /dev/null
+++ b/lib/puppet/provider/package/dpkg.rb
@@ -0,0 +1,115 @@
+Puppet::Type.type(:package).provide :dpkg do
+ desc "Package management via ``dpkg``. Because this only uses ``dpkg``
+ and not ``apt``, you must specify the source of any packages you want
+ to manage."
+
+ DPKG = "/usr/bin/dpkg"
+
+ confine :exists => DPKG
+
+ def self.list
+ packages = []
+
+ # dpkg only prints as many columns as you have available
+ # which means we don't get all of the info
+ # stupid stupid
+ oldcol = ENV["COLUMNS"]
+ ENV["COLUMNS"] = "500"
+
+ # list out all of the packages
+ open("| #{DPKG} -l") { |process|
+ # our regex for matching dpkg output
+ regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}
+ fields = [:status, :name, :version, :description]
+ hash = {}
+
+ 5.times { process.gets } # throw away the header
+
+ # now turn each returned line into a package object
+ process.each { |line|
+ if match = regex.match(line)
+ hash.clear
+
+ fields.zip(match.captures) { |field,value|
+ hash[field] = value
+ }
+
+ hash[:provider] = self.name
+
+ packages.push Puppet.type(:package).installedpkg(hash)
+ else
+ raise Puppet::DevError,
+ "Failed to match dpkg line %s" % line
+ end
+ }
+ }
+ ENV["COLUMNS"] = oldcol
+
+ return packages
+ end
+
+ def query
+ packages = []
+
+ # dpkg only prints as many columns as you have available
+ # which means we don't get all of the info
+ # stupid stupid
+ oldcol = ENV["COLUMNS"]
+ ENV["COLUMNS"] = "500"
+ fields = [:desired, :status, :error, :name, :version, :description]
+
+ hash = {}
+ # list out our specific package
+ open("| #{DPKG} -l %s 2>/dev/null" % @model[:name]) { |process|
+ # our regex for matching dpkg output
+ regex = %r{^(.)(.)(.)\s(\S+)\s+(\S+)\s+(.+)$}
+
+ # we only want the last line
+ lines = process.readlines
+ # we've got four header lines, so we should expect all of those
+ # plus our output
+ if lines.length < 5
+ return nil
+ end
+
+ line = lines[-1]
+
+ if match = regex.match(line)
+ fields.zip(match.captures) { |field,value|
+ hash[field] = value
+ }
+ #packages.push Puppet.type(:package).installedpkg(hash)
+ else
+ raise Puppet::DevError,
+ "failed to match dpkg line %s" % line
+ end
+ }
+ ENV["COLUMNS"] = oldcol
+
+ if hash[:error] != " "
+ raise Puppet::PackageError.new(
+ "Package %s, version %s is in error state: %s" %
+ [hash[:name], hash[:version], hash[:error]]
+ )
+ end
+
+ if hash[:status] == "i"
+ hash[:ensure] = :present
+ else
+ hash[:ensure] = :absent
+ end
+
+ return hash
+ end
+
+ def uninstall
+ cmd = "#{DPKG} -r %s" % @model[:name]
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/freebsd.rb b/lib/puppet/provider/package/freebsd.rb
new file mode 100755
index 000000000..cd484d47a
--- /dev/null
+++ b/lib/puppet/provider/package/freebsd.rb
@@ -0,0 +1,52 @@
+Puppet::Type.type(:package).provide :freebsd, :parent => :openbsd do
+ desc "The specific form of package management on FreeBSD. This is an
+ extremely quirky packaging system, in that it freely mixes between
+ ports and packages. Apparently all of the tools are written in Ruby,
+ so there are plans to rewrite this support to directly use those
+ libraries."
+
+ commands :info => "/usr/sbin/pkg_info",
+ :add => "/usr/sbin/pkg_add",
+ :delete => "/usr/sbin/pkg_delete"
+
+ def self.listcmd
+ command(:info)
+ end
+
+ def install
+ should = @model[:ensure]
+
+ if @model[:source]
+ return super
+ end
+
+ cmd = command(:add) + " -r " + @model[:name]
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ def query
+ self.class.list
+
+ if @model[:version]
+ return :listed
+ else
+ return nil
+ end
+ end
+
+ def uninstall
+ cmd = "#{command(:delete)} %s-%s" % [@model[:name], @model[:version]]
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb
new file mode 100755
index 000000000..fba8b4702
--- /dev/null
+++ b/lib/puppet/provider/package/gem.rb
@@ -0,0 +1,108 @@
+# Ruby gems support.
+Puppet::Type.type(:package).provide :gem do
+ desc "Ruby Gem support. By default uses remote gems, but you can specify
+ the path to a local gem via ``source``."
+ GEM = binary("gem")
+
+ confine :exists => GEM
+
+ def self.gemlist(hash)
+ command = "#{GEM} list "
+
+ if hash[:local]
+ command += "--local "
+ else
+ command += "--remote "
+ end
+
+ if name = hash[:justme]
+ command += name
+ end
+ begin
+ list = execute(command).split("\n\n").collect do |set|
+ if gemhash = gemsplit(set)
+ gemhash[:provider] = :gem
+ gemhash[:ensure] = gemhash[:version][0]
+ gemhash
+ else
+ nil
+ end
+ end.reject { |p| p.nil? }
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not list gems: %s" % detail
+ end
+
+ if hash[:justme]
+ return list.shift
+ else
+ return list
+ end
+ end
+
+ def self.gemsplit(desc)
+ case desc
+ when /^\*\*\*/: return nil
+ when /^(\S+)\s+\((.+)\)\n/
+ name = $1
+ version = $2.split(/,\s*/)
+ return {
+ :name => name,
+ :version => version
+ }
+ else
+ Puppet.warning "Could not match %s" % desc
+ nil
+ end
+ end
+
+ def self.list(justme = false)
+ gemlist(:local => true).each do |hash|
+ Puppet::Type.type(:package).installedpkg(hash)
+ end
+ end
+
+ def install(useversion = true)
+ command = "#{GEM} install "
+ if @model[:version] and useversion
+ command += "-v %s " % @model[:version]
+ end
+ if source = @model[:source]
+ command += source
+ else
+ command += @model[:name]
+ end
+ begin
+ execute(command)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not install %s: %s" %
+ [@model[:name], detail]
+ end
+ end
+
+ def latest
+ # This always gets the latest version available.
+ hash = self.class.gemlist(:justme => @model[:name])
+
+ return hash[:version][0]
+ end
+
+ def query
+ self.class.gemlist(:justme => @model[:name], :local => true)
+ end
+
+ def uninstall
+ begin
+ # Remove everything, including the binaries.
+ execute("#{GEM} uninstall -x -a #{@model[:name]}")
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::Error, "Could not uninstall %s: %s" %
+ [@model[:name], detail]
+ end
+ end
+
+ def update
+ self.install(false)
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/openbsd.rb b/lib/puppet/provider/package/openbsd.rb
new file mode 100755
index 000000000..05dee8656
--- /dev/null
+++ b/lib/puppet/provider/package/openbsd.rb
@@ -0,0 +1,110 @@
+# Packaging on OpenBSD. Doesn't work anywhere else that I know of.
+Puppet::Type.type(:package).provide :openbsd do
+ desc "OpenBSD's form of ``pkg_add`` support."
+
+ commands :info => "pkg_info", :add => "pkg_add", :delete => "pkg_delete"
+
+ defaultfor :operatingsystem => :openbsd
+
+ def self.list
+ packages = []
+
+ begin
+ execpipe(listcmd()) do |process|
+ # our regex for matching pkg_info output
+ regex = %r{^(\S+)-([^-\s]+)\s+(.+)}
+ fields = [:name, :version, :description]
+ hash = {}
+
+ # now turn each returned line into a package object
+ process.each { |line|
+ hash.clear
+ if match = regex.match(line)
+ fields.zip(match.captures) { |field,value|
+ hash[field] = value
+ }
+ yup = nil
+ name = hash[:name]
+ hash[:ensure] = :present
+
+ hash[:provider] = self.name
+
+ pkg = Puppet.type(:package).installedpkg(hash)
+ packages << pkg
+ 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
+ # Mark as absent any packages we didn't find
+ Puppet.type(:package).each do |pkg|
+ unless packages.include? pkg
+ pkg.is = [:ensure, :absent]
+ end
+ end
+
+ return packages
+ rescue Puppet::ExecutionFailure
+ return nil
+ end
+ end
+
+ def self.listcmd
+ "#{command(:info)} -a"
+ end
+
+ def install
+ should = @model[:ensure]
+
+ unless @model[:source]
+ raise Puppet::Error,
+ "You must specify a package source for BSD packages"
+ end
+
+ cmd = command(:add) + " " + @model[:source]
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ def query
+ hash = {}
+ begin
+ # list out our specific package
+ info = execute("#{command(:info)} #{@model[:name]}")
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(info)
+ end
+
+ # Search for the version info
+ if info =~ /Information for #{@model[:name]}-(\S+)/
+ hash[:version] = $1
+ hash[:ensure] = :present
+ else
+ return nil
+ end
+
+ # And the description
+ if info =~ /Comment:\s*\n(.+)/
+ hash[:description] = $1
+ end
+
+ return hash
+ end
+
+ def uninstall
+ cmd = "#{command(:delete)} %s" % @model[:name]
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/ports.rb b/lib/puppet/provider/package/ports.rb
new file mode 100755
index 000000000..6a46dcc03
--- /dev/null
+++ b/lib/puppet/provider/package/ports.rb
@@ -0,0 +1,104 @@
+Puppet::Type.type(:package).provide :ports, :parent => :freebsd do
+ desc "Support for FreeBSD's ports. Again, this still mixes packages
+ and ports."
+ commands :upgrade => "/usr/local/sbin/portupgrade",
+ :version => "/usr/local/sbin/portversion",
+ :uninstall => "/usr/local/sbin/pkg_deinstall",
+ :info => "/usr/sbin/pkg_info"
+
+ defaultfor :operatingsystem => :freebsd
+
+ # I hate ports
+ %w{INTERACTIVE UNAME}.each do |var|
+ if ENV.include?(var)
+ ENV.delete(var)
+ end
+ end
+
+ def install
+ # -p: create a package
+ # -N: install if the package is missing, otherwise upgrade
+ # -P: prefer binary packages
+ cmd = "#{command(:upgrade)} -p -N -P #{@model[:name]}"
+
+ begin
+ output = execute(cmd)
+ if output =~ /\*\* No such /
+ raise Puppet::PackageError, "Could not find package %s" % @model[:name]
+ end
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ # If there are multiple packages, we only use the last one
+ def latest
+ cmd = "#{command(:version)} -v #{@model[:name]}"
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+ line = output.split("\n").pop
+
+ unless line =~ /^(\S+)\s+(\S)\s+(.+)$/
+ # There's no "latest" version, so just return a placeholder
+ return :latest
+ end
+
+ pkgstuff = $1
+ match = $2
+ info = $3
+
+ unless pkgstuff =~ /^(\w+)-([0-9].+)$/
+ raise Puppet::PackageError,
+ "Could not match package info '%s'" % pkgstuff
+ end
+
+ name, version = $1, $2
+
+ if match == "=" or match == ">"
+ # we're up to date or more recent
+ return version
+ end
+
+ # Else, we need to be updated; we need to pull out the new version
+
+ unless info =~ /\((\w+) has (.+)\)/
+ raise Puppet::PackageError,
+ "Could not match version info '%s'" % info
+ end
+
+ source, newversion = $1, $2
+
+ debug "Newer version in %s" % source
+ return newversion
+ end
+
+ def query
+ self.class.list
+
+ if @model[:version] and @model.is(:ensure) != :absent
+ return :listed
+ else
+ return nil
+ end
+ end
+
+ def uninstall
+ cmd = "#{command(:uninstall)} #{@model[:name]}"
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::PackageError.new(output)
+ end
+
+ end
+
+ def update
+ install()
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb
new file mode 100755
index 000000000..25ae0385b
--- /dev/null
+++ b/lib/puppet/provider/package/rpm.rb
@@ -0,0 +1,120 @@
+# RPM packaging. Should work anywhere that has rpm installed.
+Puppet::Type.type(:package).provide :rpm do
+ desc "RPM packaging support; should work anywhere with a working ``rpm``
+ binary."
+
+ VERSIONSTRING = "%{VERSION}-%{RELEASE}"
+
+ commands :rpm => "rpm"
+
+ def self.list
+ packages = []
+
+ # list out all of the packages
+ begin
+ execpipe("#{command(:rpm)} -q -a --qf '%{NAME} #{VERSIONSTRING}\n'") { |process|
+ # our regex for matching dpkg output
+ regex = %r{^(\S+)\s+(\S+)}
+ fields = [:name, :ensure]
+ hash = {}
+
+ # now turn each returned line into a package object
+ process.each { |line|
+ if match = regex.match(line)
+ hash.clear
+
+ fields.zip(match.captures) { |field,value|
+ hash[field] = value
+ }
+ hash[:provider] = self.name
+ packages.push Puppet.type(:package).installedpkg(hash)
+ else
+ raise "failed to match rpm line %s" % line
+ end
+ }
+ }
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error, "Failed to list packages"
+ end
+
+ return packages
+ end
+
+ def query
+ fields = {
+ :name => "NAME",
+ :version => "VERSION",
+ :description => "DESCRIPTION"
+ }
+
+ cmd = "#{command(:rpm)} -q #{@model[:name]} --qf '%s\n'" %
+ "%{NAME} #{VERSIONSTRING}"
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ return nil
+ end
+
+ regex = %r{^(\S+)\s+(\S+)}
+ #fields = [:name, :ensure, :description]
+ fields = [:name, :version]
+ hash = {}
+ if match = regex.match(output)
+ fields.zip(match.captures) { |field,value|
+ hash[field] = value
+ }
+ else
+ raise Puppet::DevError,
+ "Failed to match rpm output '%s'" %
+ output
+ end
+
+ hash[:ensure] = :present
+
+ return hash
+ end
+
+ # Here we just retrieve the version from the file specified in the source.
+ def latest
+ unless source = @model[:source]
+ @model.fail "RPMs must specify a package source"
+ end
+
+ cmd = "#{command(:rpm)} -q --qf '#{VERSIONSTRING}' -p #{@model[:source]}"
+ version = execfail(cmd, Puppet::Error)
+
+ return version
+ end
+
+ def install
+ source = nil
+ unless source = @model[:source]
+ @model.fail "RPMs must specify a package source"
+ end
+
+ flag = "-i"
+ if @model.is(:ensure) != :absent
+ flag = "-U"
+ end
+ output = %x{#{command(:rpm)} #{flag} #{source} 2>&1}
+
+ unless $? == 0
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ def uninstall
+ cmd = "#{command(:rpm)} -e %s" % @model[:name]
+ output = %x{#{cmd}}
+ if $? != 0
+ raise output
+ end
+ end
+
+ def update
+ self.install
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/sun.rb b/lib/puppet/provider/package/sun.rb
new file mode 100755
index 000000000..590e8f84a
--- /dev/null
+++ b/lib/puppet/provider/package/sun.rb
@@ -0,0 +1,179 @@
+# Sun packaging. No one else uses these package tools, AFAIK.
+
+Puppet::Type.type(:package).provide :sun do
+ desc "Sun's packaging system. Requires that you specify the source for
+ the packages you're managing."
+ commands :info => "/usr/bin/pkginfo",
+ :add => "/usr/sbin/pkgadd",
+ :rm => "/usr/sbin/pkgrm"
+
+ defaultfor :operatingsystem => :solaris
+
+ def self.list
+ packages = []
+ hash = {}
+ names = {
+ "PKGINST" => :name,
+ "NAME" => nil,
+ "CATEGORY" => :category,
+ "ARCH" => :platform,
+ "VERSION" => :ensure,
+ "BASEDIR" => :root,
+ "HOTLINE" => nil,
+ "EMAIL" => nil,
+ "VENDOR" => :vendor,
+ "DESC" => :description,
+ "PSTAMP" => nil,
+ "INSTDATE" => nil,
+ "STATUS" => nil,
+ "FILES" => nil
+ }
+
+ cmd = "#{command(:info)} -l"
+
+ # list out all of the packages
+ execpipe(cmd) { |process|
+ # we're using the long listing, so each line is a separate
+ # piece of information
+ process.each { |line|
+ case line
+ when /^$/:
+ hash[:provider] = :sun
+
+ packages.push Puppet.type(:package).installedpkg(hash)
+ hash.clear
+ when /\s*(\w+):\s+(.+)/:
+ name = $1
+ value = $2
+ if names.include?(name)
+ unless names[name].nil?
+ hash[names[name]] = value
+ end
+ else
+ raise "Could not find %s" % name
+ end
+ when /\s+\d+.+/:
+ # nothing; we're ignoring the FILES info
+ end
+ }
+ }
+ return packages
+ end
+
+ # Get info on a package, optionally specifying a device.
+ def info2hash(device = nil)
+ names = {
+ "PKGINST" => :name,
+ "NAME" => nil,
+ "CATEGORY" => :category,
+ "ARCH" => :platform,
+ "VERSION" => :ensure,
+ "BASEDIR" => :root,
+ "HOTLINE" => nil,
+ "EMAIL" => nil,
+ "VSTOCK" => nil,
+ "VENDOR" => :vendor,
+ "DESC" => :description,
+ "PSTAMP" => nil,
+ "INSTDATE" => nil,
+ "STATUS" => nil,
+ "FILES" => nil
+ }
+
+ hash = {}
+ cmd = "#{command(:info)} -l"
+ if device
+ cmd += " -d #{device}"
+ end
+ cmd += " #{@model[:name]}"
+
+ begin
+ # list out all of the packages
+ execpipe(cmd) { |process|
+ # we're using the long listing, so each line is a separate
+ # piece of information
+ process.each { |line|
+ case line
+ when /^$/: # ignore
+ when /\s*([A-Z]+):\s+(.+)/:
+ name = $1
+ value = $2
+ if names.include?(name)
+ unless names[name].nil?
+ hash[names[name]] = value
+ end
+ end
+ when /\s+\d+.+/:
+ # nothing; we're ignoring the FILES info
+ end
+ }
+ }
+ return hash
+ rescue Puppet::ExecutionFailure
+ return nil
+ end
+ end
+
+ def install
+ unless @model[:source]
+ raise Puppet::Error, "Sun packages must specify a package source"
+ end
+ cmd = [command(:add)]
+
+ if @model[:adminfile]
+ cmd << " -a " + @model[:adminfile]
+ end
+
+ if @model[:responsefile]
+ cmd << " -r " + @model[:responsefile]
+ end
+
+ cmd += ["-d", @model[:source]]
+ cmd += ["-n", @model[:name]]
+ cmd = cmd.join(" ")
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::PackageError.new(output)
+ end
+ end
+
+ # Retrieve the version from the current package file.
+ def latest
+ hash = info2hash(@model[:source])
+ hash[:ensure]
+ end
+
+ def query
+ info2hash()
+ end
+
+ def uninstall
+ command = "#{command(:rm)} -n "
+
+ if @model[:adminfile]
+ command += " -a " + @model[:adminfile]
+ end
+
+ command += " " + @model[:name]
+ begin
+ execute(command)
+ rescue ExecutionFailure => detail
+ raise Puppet::Error,
+ "Could not uninstall %s: %s" %
+ [@model[:name], detail]
+ end
+ end
+
+ # Remove the old package, and install the new one. This will probably
+ # often fail.
+ def update
+ if @model.is(:ensure) != :absent
+ self.uninstall
+ end
+ self.install
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/sunfreeware.rb b/lib/puppet/provider/package/sunfreeware.rb
new file mode 100755
index 000000000..e6c3870f7
--- /dev/null
+++ b/lib/puppet/provider/package/sunfreeware.rb
@@ -0,0 +1,15 @@
+# At this point, it's an exact copy of the Blastwave stuff.
+Puppet::Type.type(:package).provide :sunfreeware, :parent => :blastwave do
+ desc "Package management using sunfreeware.com's ``pkg-get`` command on Solaris.
+ At this point, support is exactly the same as ``blastwave`` support and
+ has not actually been tested."
+ if pkgget = binary("pkg-get")
+ PKGGET = pkgget
+ else
+ PKGGET = nil
+ end
+
+ confine :exists => PKGGET
+end
+
+# $Id$
diff --git a/lib/puppet/provider/package/yum.rb b/lib/puppet/provider/package/yum.rb
new file mode 100755
index 000000000..587b1911e
--- /dev/null
+++ b/lib/puppet/provider/package/yum.rb
@@ -0,0 +1,53 @@
+Puppet::Type.type(:package).provide :yum, :parent => :rpm do
+ desc "Support via ``yum``."
+ commands :yum => "yum", :rpm => "rpm"
+
+ defaultfor :operatingsystem => :fedora
+
+ # Install a package using 'yum'.
+ def install
+ cmd = "#{command(:yum)} -y install %s" % @model[:name]
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::PackageError.new(detail)
+ end
+
+ unless self.query
+ raise Puppet::PackageError.new(
+ "Could not find package %s" % self.name
+ )
+ end
+ end
+
+ # What's the latest package version available?
+ def latest
+ cmd = "#{command(:yum)} list available %s" % @model[:name]
+
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure => detail
+ raise Puppet::PackageError.new(detail)
+ end
+
+ if output =~ /#{@model[:name]}\S+\s+(\S+)\s/
+ return $1
+ else
+ # Yum didn't find updates, pretend the current
+ # version is the latest
+ return @model[:version]
+ end
+ end
+
+ def update
+ # Install in yum can be used for update, too
+ self.install
+ end
+
+ def versionable?
+ false
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb
new file mode 100755
index 000000000..eedcac4ae
--- /dev/null
+++ b/lib/puppet/provider/service/base.rb
@@ -0,0 +1,135 @@
+Puppet::Type.type(:service).provide :base do
+ desc "The simplest form of service support. You have to specify
+ enough about your service for this to work; the minimum you can specify
+ is a binary for starting the process, and this same binary will be searched
+ for in the process table to stop the service.
+
+ It is preferable to specify start, stop, and status commands, akin
+ to how you would do so using ``init``."
+
+ # Execute a command. Basically just makes sure it exits with a 0
+ # code.
+ def execute(type, cmd)
+ self.debug "Executing %s" % cmd.inspect
+ output = %x(#{cmd} 2>&1)
+ unless $? == 0
+ @model.fail "Could not %s %s: %s" %
+ [type, self.name, output.chomp]
+ end
+ end
+
+ # Get the process ID for a running process. Requires the 'pattern'
+ # parameter.
+ def getpid
+ unless @model[:pattern]
+ @model.fail "Either a stop command or a pattern must be specified"
+ end
+ ps = Facter["ps"].value
+ warning ps.inspect
+ unless ps and ps != ""
+ @model.fail(
+ "You must upgrade Facter to a version that includes 'ps'"
+ )
+ end
+ regex = Regexp.new(@model[:pattern])
+ self.debug "Executing '#{ps}'"
+ IO.popen(ps) { |table|
+ table.each { |line|
+ if regex.match(line)
+ ary = line.sub(/^\s+/, '').split(/\s+/)
+ return ary[1]
+ end
+ }
+ }
+
+ return nil
+ end
+
+ # Basically just a synonym for restarting. Used to respond
+ # to events.
+ def refresh
+ self.restart
+ end
+
+ # How to restart the process.
+ def restart
+ if @model[:restart] or self.respond_to?(:restartcmd)
+ cmd = @model[:restart] || self.restartcmd
+ self.execute("restart", cmd)
+ else
+ self.stop
+ self.start
+ end
+ end
+
+ # Check if the process is running. Prefer the 'status' parameter,
+ # then 'statuscmd' method, then look in the process table. We give
+ # the object the option to not return a status command, which might
+ # happen if, for instance, it has an init script (and thus responds to
+ # 'statuscmd') but does not have 'hasstatus' enabled.
+ def status
+ if @model[:status] or (
+ self.respond_to?(:statuscmd) and self.statuscmd
+ )
+ cmd = @model[:status] || self.statuscmd
+ self.debug "Executing %s" % cmd.inspect
+ output = %x(#{cmd} 2>&1)
+ self.debug "%s status returned %s" %
+ [self.name, output.inspect]
+ if $? == 0
+ return :running
+ else
+ return :stopped
+ end
+ elsif pid = self.getpid
+ self.debug "PID is %s" % pid
+ return :running
+ else
+ return :stopped
+ end
+ end
+
+ # Run the 'start' parameter command, or the specified 'startcmd'.
+ def start
+ cmd = @model[:start] || self.startcmd
+ self.execute("start", cmd)
+ end
+
+ # The command used to start. Generated if the 'binary' argument
+ # is passed.
+ def startcmd
+ if @model[:binary]
+ return @model[:binary]
+ else
+ raise Puppet::Error,
+ "Services must specify a start command or a binary"
+ end
+ end
+
+ # Stop the service. If a 'stop' parameter is specified, it
+ # takes precedence; otherwise checks if the object responds to
+ # a 'stopcmd' method, and if so runs that; otherwise, looks
+ # for the process in the process table.
+ # This method will generally not be overridden by submodules.
+ def stop
+ if @model[:stop]
+ return @model[:stop]
+ elsif self.respond_to?(:stopcmd)
+ self.execute("stop", self.stopcmd)
+ else
+ pid = getpid
+ unless pid
+ self.info "%s is not running" % self.name
+ return false
+ end
+ output = %x(kill #{pid} 2>&1)
+ if $? != 0
+ @model.fail "Could not kill %s, PID %s: %s" %
+ [self.name, pid, output]
+ end
+ return true
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/type/service/debian.rb b/lib/puppet/provider/service/debian.rb
index 88fa598bf..b8acbf326 100755
--- a/lib/puppet/type/service/debian.rb
+++ b/lib/puppet/provider/service/debian.rb
@@ -1,11 +1,15 @@
-require 'puppet/type/service/init'
-
# Manage debian services. Start/stop is the same as InitSvc, but enable/disable
# is special.
-Puppet.type(:service).newsvctype(:debian, :init) do
+Puppet::Type.type(:service).provide :debian, :parent => :init do
+ desc "Debian's form of ``init``-style management. The only difference
+ is that this supports service enabling and disabling via ``update-rc.d``."
+
+ commands :update => "/usr/sbin/update-rc.d"
+ defaultfor :operatingsystem => :debian
+
# Remove the symlinks
def disable
- cmd = %{update-rc.d -f #{self[:name]} remove 2>&1}
+ cmd = %{#{command(:update)} -f #{@model[:name]} remove 2>&1}
self.debug "Executing '%s'" % cmd
output = %x{#{cmd}}
@@ -16,7 +20,7 @@ Puppet.type(:service).newsvctype(:debian, :init) do
end
def enabled?
- cmd = %{update-rc.d -n -f #{self[:name]} remove 2>&1}
+ cmd = %{#{command(:update)} -n -f #{@model[:name]} remove 2>&1}
self.debug "Executing 'enabled' test: '%s'" % cmd
output = %x{#{cmd}}
unless $? == 0
@@ -34,7 +38,7 @@ Puppet.type(:service).newsvctype(:debian, :init) do
end
def enable
- cmd = %{update-rc.d #{self[:name]} defaults 2>&1}
+ cmd = %{#{command(:update)} #{@model[:name]} defaults 2>&1}
self.debug "Executing '%s'" % cmd
output = %x{#{cmd}}
@@ -44,3 +48,5 @@ Puppet.type(:service).newsvctype(:debian, :init) do
end
end
end
+
+# $Id$
diff --git a/lib/puppet/type/service/init.rb b/lib/puppet/provider/service/init.rb
index 4a9f52a31..76cac2763 100755
--- a/lib/puppet/type/service/init.rb
+++ b/lib/puppet/provider/service/init.rb
@@ -1,23 +1,34 @@
# The standard init-based service type. Many other service types are
# customizations of this module.
-Puppet.type(:service).newsvctype(:init) do
- def self.defpath
- case Facter["operatingsystem"].value
- when "FreeBSD":
- "/etc/rc.d"
- else
- "/etc/init.d"
- end
+Puppet::Type.type(:service).provide :init, :parent => :base do
+ desc "Standard init service management. This provider assumes that the
+ init script has not ``status`` command, because so few scripts do,
+ so you need to either provide a status command or specify via ``hasstatus``
+ that one already exists in the init script."
+
+ class << self
+ attr_accessor :defpath
+ end
+
+ case Facter["operatingsystem"].value
+ when "FreeBSD":
+ @defpath = "/etc/rc.d"
+ else
+ @defpath = "/etc/init.d"
end
- Puppet.type(:service).newpath(:init, defpath())
+ #confine :exists => @defpath
+
+ if self.suitable?
+ # Add it to the search paths
+ Puppet.type(:service).newpath(:init, defpath())
- # Set the default init directory.
- Puppet.type(:service).attrclass(:path).defaultto defpath()
+ # Set the default init directory.
+ Puppet.type(:service).attrclass(:path).defaultto defpath()
+ end
- # List all services of this type. This has to be an instance method
- # so that it's inherited by submodules.
- def list(name)
+ # List all services of this type.
+ def self.list(name)
# We need to find all paths specified for our type or any parent types
paths = Puppet.type(:service).paths(name)
@@ -94,12 +105,12 @@ Puppet.type(:service).newsvctype(:init) do
if defined? @initscript
return @initscript
else
- @initscript = self.search(self[:name])
+ @initscript = self.search(@model[:name])
end
end
def search(name)
- self[:path].each { |path|
+ @model[:path].each { |path|
fqname = File.join(path,name)
begin
stat = File.stat(fqname)
@@ -124,7 +135,7 @@ Puppet.type(:service).newsvctype(:init) do
# we just return that; otherwise, we return false, which causes it to
# fallback to other mechanisms.
def statuscmd
- if self[:hasstatus]
+ if @model[:hasstatus]
return self.initscript + " status"
else
return false
diff --git a/lib/puppet/type/service/redhat.rb b/lib/puppet/provider/service/redhat.rb
index 24272cd0b..e599e63f1 100755
--- a/lib/puppet/type/service/redhat.rb
+++ b/lib/puppet/provider/service/redhat.rb
@@ -1,13 +1,18 @@
-require 'puppet/type/service/init'
-
# Manage debian services. Start/stop is the same as InitSvc, but enable/disable
# is special.
-Puppet.type(:service).newsvctype(:redhat, :init) do
+Puppet::Type.type(:service).provide :redhat, :parent => :init do
+ desc "Red Hat's (and probably many others) form of ``init``-style service
+ management; uses ``chkconfig`` for service enabling and disabling."
+
+ confine :exists => "/sbin/chkconfig"
+
+ defaultfor :operatingsystem => [:redhat, :fedora]
+
# Remove the symlinks
def disable
begin
- output = util_execute("/sbin/chkconfig #{self[:name]} off 2>&1")
- output += util_execute("/sbin/chkconfig --del #{self[:name]} 2>&1")
+ output = util_execute("/sbin/chkconfig #{@model[:name]} off 2>&1")
+ output += util_execute("/sbin/chkconfig --del #{@model[:name]} 2>&1")
rescue Puppet::ExecutionFailure
raise Puppet::Error, "Could not disable %s: %s" %
[self.name, output]
@@ -16,7 +21,7 @@ Puppet.type(:service).newsvctype(:redhat, :init) do
def enabled?
begin
- output = util_execute("/sbin/chkconfig #{self[:name]} 2>&1").chomp
+ output = util_execute("/sbin/chkconfig #{@model[:name]} 2>&1").chomp
rescue Puppet::ExecutionFailure
return :false
end
@@ -28,11 +33,13 @@ Puppet.type(:service).newsvctype(:redhat, :init) do
# in the init scripts.
def enable
begin
- output = util_execute("/sbin/chkconfig --add #{self[:name]} 2>&1")
- output += util_execute("/sbin/chkconfig #{self[:name]} on 2>&1")
+ output = util_execute("/sbin/chkconfig --add #{@model[:name]} 2>&1")
+ output += util_execute("/sbin/chkconfig #{@model[:name]} on 2>&1")
rescue Puppet::ExecutionFailure
raise Puppet::Error, "Could not enable %s: %s" %
[self.name, output]
end
end
end
+
+# $Id$
diff --git a/lib/puppet/type/service/smf.rb b/lib/puppet/provider/service/smf.rb
index 71dc22ff4..becd6fa3f 100755
--- a/lib/puppet/type/service/smf.rb
+++ b/lib/puppet/provider/service/smf.rb
@@ -1,5 +1,14 @@
# Solaris 10 SMF-style services.
-Puppet.type(:service).newsvctype(:smf) do
+Puppet::Type.type(:service).provide :smf, :parent => :base do
+ desc "Support for Sun's new Service Management Framework. Starting a service
+ is effectively equivalent to enabling it, so there is only support
+ for starting and stopping services, which also enables and disables them,
+ respectively."
+
+ defaultfor :operatingsystem => :solaris
+
+ commands :adm => "/usr/sbin/svcadm", :svcs => "/usr/bin/svcs"
+
def enable
self.start
end
@@ -18,19 +27,26 @@ Puppet.type(:service).newsvctype(:smf) do
end
def restartcmd
- "svcadm restart %s" % self[:name]
+ "#{command(:adm)} restart %s" % @model[:name]
end
def startcmd
- "svcadm enable %s" % self[:name]
+ "#{command(:adm)} enable %s" % @model[:name]
end
def status
- if self[:status]
+ if @model[:status]
super
return
end
- %x{/usr/bin/svcs -l #{self[:name]} 2>/dev/null}.split("\n").each { |line|
+ begin
+ output = Puppet::Util.execute("#{command(:svcs)} -l #{@model[:name]}")
+ rescue Puppet::ExecutionFailure
+ warning "Could not get status on service %s" % self.name
+ return :stopped
+ end
+
+ output.split("\n").each { |line|
var = nil
value = nil
if line =~ /^(\w+)\s+(.+)/
@@ -58,16 +74,10 @@ Puppet.type(:service).newsvctype(:smf) do
end
end
}
-
- if $? != 0
- #raise Puppet::Error,
- warning "Could not get status on service %s" % self.name
- return :stopped
- end
end
def stopcmd
- "svcadm disable %s" % self[:name]
+ "#{command(:adm)} disable %s" % @model[:name]
end
end
diff --git a/lib/puppet/provider/user/netinfo.rb b/lib/puppet/provider/user/netinfo.rb
new file mode 100644
index 000000000..e453c4582
--- /dev/null
+++ b/lib/puppet/provider/user/netinfo.rb
@@ -0,0 +1,91 @@
+# Manage NetInfo POSIX objects. Probably only used on OS X, but I suppose
+# it could be used elsewhere.
+require 'puppet/provider/nameservice/netinfo'
+
+Puppet::Type.type(:user).provide :netinfo, :parent => Puppet::Provider::NameService::NetInfo do
+ desc "User management in NetInfo."
+
+ NIREPORT = binary("nireport")
+ NIUTIL = binary("niutil")
+ confine :exists => NIREPORT
+ confine :exists => NIUTIL
+
+ options :comment, :key => "realname"
+
+ defaultfor :operatingsystem => :darwin
+
+ # The list of all groups the user is a member of. Different
+ # user mgmt systems will need to override this method.
+ def groups
+ groups = []
+
+ user = @model[:name]
+ # Retrieve them all from netinfo
+ open("| nireport / /groups name users") do |file|
+ file.each do |line|
+ name, members = line.split(/\s+/)
+ next unless members
+ next if members =~ /NoValue/
+ members = members.split(",")
+
+ if members.include? user
+ groups << name
+ end
+ end
+ end
+
+ groups.join(",")
+ end
+
+ # This is really lame. We have to iterate over each
+ # of the groups and add us to them.
+ def groups=(groups)
+ groups = groups.split(/\s*,\s*/)
+ # Get just the groups we need to modify
+ diff = groups - (@is || [])
+
+ data = {}
+ open("| nireport / /groups name users") do |file|
+ file.each do |line|
+ name, members = line.split(/\s+/)
+
+ if members.nil? or members =~ /NoValue/
+ data[name] = []
+ else
+ # Add each diff group's current members
+ data[name] = members.split(/,/)
+ end
+ end
+ end
+
+ user = @model[:name]
+ data.each do |name, members|
+ if members.include? user and groups.include? name
+ # I'm in the group and should be
+ next
+ elsif members.include? user
+ # I'm in the group and shouldn't be
+ setuserlist(name, members - [user])
+ elsif groups.include? name
+ # I'm not in the group and should be
+ setuserlist(name, members + [user])
+ else
+ # I'm not in the group and shouldn't be
+ next
+ end
+ end
+ end
+
+ def setuserlist(group, list)
+ cmd = "niutil -createprop / /groups/%s users %s" %
+ [group, list.join(",")]
+ begin
+ output = execute(cmd)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error,
+ "Failed to set groups: %s" % output
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/user/pw.rb b/lib/puppet/provider/user/pw.rb
new file mode 100644
index 000000000..7342c42fe
--- /dev/null
+++ b/lib/puppet/provider/user/pw.rb
@@ -0,0 +1,41 @@
+require 'puppet/provider/nameservice/pw'
+
+Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::PW do
+ desc "User management via ``pw`` on FreeBSD."
+
+ commands :pw => "pw"
+
+ defaultfor :operatingsystem => :freebsd
+
+ options :home, :flag => "-d", :method => :dir
+ options :comment, :method => :gecos
+ options :groups, :flag => "-G"
+
+ verify :gid, "GID must be an integer" do |value|
+ value.is_a? Integer
+ end
+
+ verify :groups, "Groups must be comma-separated" do |value|
+ value !~ /\s/
+ end
+
+ def addcmd
+ cmd = [command(:pw), "useradd", @model[:name]]
+ @model.class.validstates.each do |state|
+ next if name == :ensure
+ # the value needs to be quoted, mostly because -c might
+ # have spaces in it
+ if value = @model[state] and value != ""
+ cmd << flag(state) << "'%s'" % @model[state]
+ end
+ end
+
+ if @model[:allowdupe] == :true
+ cmd << "-o"
+ end
+
+ return cmd.join(" ")
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/provider/user/useradd.rb b/lib/puppet/provider/user/useradd.rb
new file mode 100644
index 000000000..edd5ec6e3
--- /dev/null
+++ b/lib/puppet/provider/user/useradd.rb
@@ -0,0 +1,46 @@
+require 'puppet/provider/nameservice/objectadd'
+
+Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameService::ObjectAdd do
+ desc "User management via ``useradd`` and its ilk."
+
+ commands :add => "useradd", :delete => "userdel", :modify => "usermod"
+
+ options :home, :flag => "-d", :method => :dir
+ options :comment, :method => :gecos
+ options :groups, :flag => "-G"
+
+ verify :gid, "GID must be an integer" do |value|
+ value.is_a? Integer
+ end
+
+ verify :groups, "Groups must be comma-separated" do |value|
+ value !~ /\s/
+ end
+
+ def addcmd
+ cmd = [command(:add)]
+ @model.class.validstates.each do |state|
+ next if name == :ensure
+ # the value needs to be quoted, mostly because -c might
+ # have spaces in it
+ if value = @model[state] and value != ""
+ cmd << flag(state) << "'%s'" % @model[state]
+ end
+ end
+ # stupid fedora
+ case Facter["operatingsystem"].value
+ when "Fedora", "RedHat":
+ cmd << "-M"
+ else
+ end
+ if @model[:allowdupe] == :true
+ cmd << "-o"
+ end
+
+ cmd << @model[:name]
+
+ cmd.join(" ")
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index 7a3b761b7..4207992a4 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -76,6 +76,9 @@ class Transaction
# event if they want
events = [change.forward].flatten.reject { |e| e.nil? }
rescue => detail
+ if Puppet[:debug]
+ puts detail.backtrace
+ end
change.state.err "change from %s to %s failed: %s" %
[change.state.is_to_s, change.state.should_to_s, detail]
@failures[child] += 1
@@ -299,7 +302,7 @@ class Transaction
# but a chmod failed? how would i handle that error? dern
end
- collecttargets(events)
+ collecttargets(events) if events
# Now check to see if there are any events for this child.
# Kind of hackish, since going backwards goes a change at a
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 91b75e9d4..824dd33f1 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -6,11 +6,14 @@ require 'puppet/metric'
require 'puppet/type/state'
require 'puppet/parameter'
require 'puppet/util'
+require 'puppet/autoload'
+
# see the bottom of the file for the rest of the inclusions
module Puppet
# The type is unknown
class UnknownTypeError < Puppet::Error; end
+class UnknownProviderError < Puppet::Error; end
class Type < Puppet::Element
# Types (which map to elements in the languages) are entirely composed of
@@ -22,6 +25,7 @@ class Type < Puppet::Element
# that it is clear whether it operates on all attributes (thus has 'attr' in
# the method name, or whether it operates on a specific type of attributes.
attr_accessor :children
+ attr_accessor :provider
attr_accessor :file, :line
attr_reader :tags, :parent
@@ -35,17 +39,6 @@ class Type < Puppet::Element
end
include Enumerable
-
- # a little fakery, since Puppet itself isn't a type
- # I don't think this is used any more, now that the language can't
- # call methods
- #@name = :puppet
-
- # set it to something to silence the tests, but otherwise not used
- #@namevar = :notused
-
- # again, silence the tests; the :notused has to be there because it's
- # the namevar
# class methods dealing with Type management
@@ -54,27 +47,10 @@ class Type < Puppet::Element
# the Type class attribute accessors
class << self
attr_reader :name, :states
+ attr_accessor :providerloader
+ attr_writer :defaultprovider
include Enumerable
-
- #def inspect
- # "Type(%s)" % self.name
- #end
-
- # This class is aggregatable, meaning that instances are defined on
- # one system but instantiated on another
- def isaggregatable
- @aggregatable = true
- end
-
- # Is this one aggregatable?
- def aggregatable?
- if defined? @aggregatable
- return @aggregatable
- else
- return false
- end
- end
end
# iterate across all of the subclasses of Type
@@ -91,9 +67,9 @@ class Type < Puppet::Element
# can easily call it and create their own 'ensure' values.
def self.ensurable(&block)
if block_given?
- self.newstate(:ensure, Puppet::State::Ensure, &block)
+ self.newstate(:ensure, :parent => Puppet::State::Ensure, &block)
else
- self.newstate(:ensure, Puppet::State::Ensure) do
+ self.newstate(:ensure, :parent => Puppet::State::Ensure) do
self.defaultvalues
end
end
@@ -119,11 +95,16 @@ class Type < Puppet::Element
@objects = Hash.new
@aliases = Hash.new
+ @providers = Hash.new
+ @defaults = {}
+
unless defined? @parameters
@parameters = []
end
@validstates = {}
+ @parameters = []
+ @paramhash = {}
@paramdoc = Hash.new { |hash,key|
if key.is_a?(String)
@@ -161,7 +142,7 @@ class Type < Puppet::Element
Puppet.info "loaded %s" % file
return true
rescue LoadError => detail
- Puppet.info "Could not load %s: %s" %
+ Puppet.info "Could not load plugin %s: %s" %
[file, detail]
return false
end
@@ -223,6 +204,14 @@ class Type < Puppet::Element
end
end
+ # Now set up autoload any providers that might exist for this type.
+ t.providerloader = Puppet::Autoload.new(t,
+ "puppet/provider/#{t.name.to_s}"
+ )
+
+ # We have to load everything so that we can figure out the default type.
+ t.providerloader.loadall()
+
t
end
@@ -406,12 +395,15 @@ class Type < Puppet::Element
def self.namevar
unless defined? @namevar
return nil unless defined? @parameters and ! @parameters.empty?
- @namevar = @parameters.find { |name, param|
+ namevarparam = @parameters.find { |param|
param.isnamevar?
- unless param
- raise Puppet::DevError, "huh? %s" % name
- end
- }[0].value
+ }
+
+ if namevarparam
+ @namevar = namevarparam.name
+ else
+ raise Puppet::DevError, "No namevar for %s" % self.name
+ end
end
@namevar
end
@@ -425,10 +417,7 @@ class Type < Puppet::Element
unless param
raise Puppet::DevError, "Class %s has no param %s" % [klass, name]
end
- @parameters ||= []
@parameters << param
-
- @paramhash ||= {}
@parameters.each { |p| @paramhash[name] = p }
if param.isnamevar?
@@ -462,6 +451,179 @@ class Type < Puppet::Element
@@metaparams.each { |p| yield p.name }
end
+ # Find the default provider.
+ def self.defaultprovider
+ unless defined? @defaultprovider and @defaultprovider
+ suitable = suitableprovider()
+
+ max = 0
+ suitable.each do |provider|
+ if provider.defaultnum > max
+ max = provider.defaultnum
+ end
+ end
+
+ defaults = suitable.find_all do |provider|
+ provider.defaultnum == max
+ end
+
+ retval = nil
+ if defaults.length > 1
+ Puppet.warning(
+ "Found multiple default providers for %s: %s; using %s" %
+ [self.name, defaults.collect { |i| i.name.to_s }.join(", "),
+ defaults[0].name]
+ )
+ retval = defaults.shift
+ elsif defaults.length == 1
+ retval = defaults.shift
+ else
+ raise Puppet::DevError, "Could not find a default provider for %s" %
+ self.name
+ end
+
+ @defaultprovider = retval
+ end
+
+ return @defaultprovider
+ end
+
+ # Retrieve a provider by name.
+ def self.provider(name)
+ name = Puppet::Util.symbolize(name)
+
+ # If we don't have it yet, try loading it.
+ unless @providers.has_key?(name)
+ @providerloader.load(name)
+ end
+ return @providers[name]
+ end
+
+ # Just list all of the providers.
+ def self.providers
+ @providers.keys
+ end
+
+ def self.validprovider?(name)
+ name = Puppet::Util.symbolize(name)
+
+ return (@providers.has_key?(name) && @providers[name].suitable?)
+ end
+
+ # Create a new provider of a type. This method must be called
+ # directly on the type that it's implementing.
+ def self.provide(name, options = {}, &block)
+ name = Puppet::Util.symbolize(name)
+ model = self
+
+ parent = if pname = options[:parent]
+ if pname.is_a? Class
+ pname
+ else
+ if provider = self.provider(pname)
+ provider
+ else
+ raise Puppet::DevError,
+ "Could not find parent provider %s of %s" %
+ [pname, name]
+ end
+ end
+ else
+ Puppet::Type::Provider
+ end
+
+ self.providify
+
+ provider = Class.new(parent)
+ provider.name = name
+ provider.model = model
+ provider.initvars
+
+ const_set "Provider%s" % name.to_s.capitalize, provider
+
+ provider.class_eval(&block)
+ @providers[name] = provider
+
+ return provider
+ end
+
+ # Make sure we have a :use parameter defined. Only gets called if there
+ # are providers.
+ def self.providify
+ return if @paramhash.has_key? :provider
+ newparam(:provider) do
+ desc "The specific backend for #{self.name.to_s} to use. You will
+ seldom need to specify this -- Puppet will usually discover the
+ appropriate provider for your platform."
+
+ # We need to add documentation for each provider.
+ def self.doc
+ @doc + "Available providers are:\n" + @model.providers.sort { |a,b|
+ a.to_s <=> b.to_s
+ }.each { |i|
+ "* **%s**: %s" % [i, self.provider(i).doc]
+ }
+ end
+
+ defaultto { @parent.class.defaultprovider.name }
+
+ validate do |value|
+ value = value[0] if value.is_a? Array
+ if provider = @parent.class.provider(value)
+ unless provider.suitable?
+ raise ArgumentError,
+ "Provider '%s' is not functional on this platform" %
+ [value]
+ end
+ else
+ raise ArgumentError, "Invalid %s provider '%s'" %
+ [@parent.class.name, value]
+ end
+ end
+
+ munge do |provider|
+ provider = provider[0] if provider.is_a? Array
+ if provider.is_a? String
+ provider = provider.intern
+ end
+ @parent.provider = provider
+ provider
+ end
+ end
+ end
+
+ def self.unprovide(name)
+ puts "wtf?"
+ if @providers.has_key? name
+ p "Removing provider %s" % name
+ if @defaultprovider and @defaultprovider.name == name
+ @defaultprovider = nil
+ end
+ @providers.delete(name)
+ else
+ p name
+ p @providers
+ end
+ end
+
+ # Return an array of all of the suitable providers.
+ def self.suitableprovider
+ @providers.find_all { |name, provider|
+ provider.suitable?
+ }.collect { |name, provider|
+ provider
+ }
+ end
+
+ def provider=(name)
+ if klass = self.class.provider(name)
+ @provider = klass.new(self)
+ else
+ raise UnknownProviderError, "Could not find %s provider of %s" %
+ [name, self.class.name]
+ end
+ end
+
# Create a new parameter. Requires a block and a name, stores it in the
# @parameters array, and does some basic checking on it.
def self.newparam(name, &block)
@@ -475,10 +637,7 @@ class Type < Puppet::Element
param.element = self
param.class_eval(&block)
const_set("Parameter" + name.to_s.capitalize,param)
- @parameters ||= []
@parameters << param
-
- @paramhash ||= {}
@parameters.each { |p| @paramhash[name] = p }
# These might be enabled later.
@@ -497,9 +656,22 @@ class Type < Puppet::Element
return param
end
- # Create a new state.
- def self.newstate(name, parent = nil, &block)
- parent ||= Puppet::State
+ # Create a new state. The first parameter must be the name of the state;
+ # this is how users will refer to the state when creating new instances.
+ # The second parameter is a hash of options; the options are:
+ # * <tt>:parent</tt>: The parent class for the state. Defaults to Puppet::State.
+ # * <tt>:retrieve</tt>: The method to call on the provider or @parent object (if
+ # the provider is not set) to retrieve the current value.
+ def self.newstate(name, options = {}, &block)
+ name = symbolize(name)
+
+ # This is here for types that might still have the old method of defining
+ # a parent class.
+ unless options.is_a? Hash
+ raise Puppet::DevError, "Options must be a hash, not %s" % options.inspect
+ end
+
+ parent = options[:parent] || Puppet::State
if @validstates.include?(name)
raise Puppet::DevError, "Class %s already has a state named %s" %
[self.name, name]
@@ -508,10 +680,22 @@ class Type < Puppet::Element
@name = name
end
+ # If they've passed a retrieve method, then override the retrieve method
+ # on the class.
+ if options[:retrieve]
+ s.send(:define_method, :retrieve) do
+ instance_variable_set(
+ "@is", provider.send(options[:retrieve])
+ )
+ end
+ end
+
s.initvars
const_set("State" + name.to_s.capitalize,s)
- s.class_eval(&block)
+ if block_given?
+ s.class_eval(&block)
+ end
@states ||= []
# If it's the 'ensure' state, always put it first.
@@ -598,6 +782,7 @@ class Type < Puppet::Element
# does the name reflect a valid state?
def self.validstate?(name)
+ name = name.intern if name.is_a? String
unless @validstates.length == @states.length
self.buildstatehash
end
@@ -1710,21 +1895,6 @@ class Type < Puppet::Element
end
}
- # if they're not using :name for the namevar but we got :name (probably
- # from the parser)
-# if namevar != :name and hash.include?(:name) and ! hash[:name].nil?
-# #self[namevar] = hash[:name]
-# hash[namevar] = hash[:name]
-# hash.delete(:name)
-# # else if we got the namevar
-# elsif hash.has_key?(namevar) and ! hash[namevar].nil?
-# #self[namevar] = hash[namevar]
-# #hash.delete(namevar)
-# # else something's screwy
-# else
-# # they didn't specify anything related to names
-# end
-
return hash
end
@@ -2423,6 +2593,7 @@ end # Puppet::Type
end
require 'puppet/statechange'
+require 'puppet/provider'
require 'puppet/type/component'
require 'puppet/type/cron'
require 'puppet/type/exec'
diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb
index 10c362848..03ac24d29 100755
--- a/lib/puppet/type/cron.rb
+++ b/lib/puppet/type/cron.rb
@@ -78,10 +78,6 @@ module Puppet
def should_to_s
if @should
- unless @should.is_a?(Array)
- fail "wtf?"
- end
-
if self.name == :command or @should[0].is_a? Symbol
@should[0]
else
@@ -158,9 +154,9 @@ module Puppet
# Override 'newstate' so that all states default to having the
# correct parent type
- def self.newstate(name, parent = nil, &block)
- parent ||= Puppet::State::CronParam
- super(name, parent, &block)
+ def self.newstate(name, options = {}, &block)
+ options[:parent] ||= Puppet::State::CronParam
+ super(name, options, &block)
end
# Somewhat uniquely, this state does not actually change anything -- it
@@ -170,7 +166,7 @@ module Puppet
#
# Note that this means that managing many cron jobs for a given user
# could currently result in multiple write sessions for that user.
- newstate(:command, CronParam) do
+ newstate(:command, :parent => CronParam) do
desc "The command to execute in the cron job. The environment
provided to the command varies by local system rules, and it is
best to always provide a fully qualified command. The user's
@@ -193,7 +189,7 @@ module Puppet
end
end
- newstate(:special, Puppet::State::ParsedParam) do
+ newstate(:special, :parent => Puppet::State::ParsedParam) do
desc "Special schedules only supported on FreeBSD."
def specials
@@ -208,19 +204,19 @@ module Puppet
end
end
- newstate(:minute, CronParam) do
+ newstate(:minute) do
self.boundaries = [0, 59]
desc "The minute at which to run the cron job.
Optional; if specified, must be between 0 and 59, inclusive."
end
- newstate(:hour, CronParam) do
+ newstate(:hour) do
self.boundaries = [0, 23]
desc "The hour at which to run the cron job. Optional;
if specified, must be between 0 and 23, inclusive."
end
- newstate(:weekday, CronParam) do
+ newstate(:weekday) do
def alpha
%w{sunday monday tuesday wednesday thursday friday saturday}
end
@@ -230,7 +226,7 @@ module Puppet
0 being Sunday, or must be the name of the day (e.g., Tuesday)."
end
- newstate(:month, CronParam) do
+ newstate(:month) do
def alpha
%w{january february march april may june july
august september october november december}
@@ -240,13 +236,13 @@ module Puppet
must be between 1 and 12 or the month name (e.g., December)."
end
- newstate(:monthday, CronParam) do
+ newstate(:monthday) do
self.boundaries = [1, 31]
desc "The day of the month on which to run the
command. Optional; if specified, must be between 1 and 31."
end
- newstate(:environment, Puppet::State::ParsedParam) do
+ newstate(:environment, :parent => Puppet::State::ParsedParam) do
desc "Any environment settings associated with this cron job. They
will be stored between the header and the job in the crontab. There
can be no guarantees that other, earlier settings will not also
@@ -462,8 +458,8 @@ module Puppet
def self.list
# Look for cron jobs for each user
- Puppet::Type.type(:user).list.each { |user|
- self.retrieve(user.name)
+ Puppet::Type.type(:user).list_by_name.each { |user|
+ self.retrieve(user, false)
}
self.collect { |c| c }
@@ -594,12 +590,14 @@ module Puppet
# Retrieve a given user's cron job, using the @filetype's +retrieve+
# method. Returns nil if there was no cron job; else, returns the
# number of cron instances found.
- def self.retrieve(user)
- # First make sure the user exists
- begin
- Puppet::Util.uid(user)
- rescue ArgumentError
- raise Puppet::Error, "User %s not found" % user
+ def self.retrieve(user, checkuser = true)
+ # First make sure the user exists, unless told not to
+ if checkuser
+ begin
+ Puppet::Util.uid(user)
+ rescue ArgumentError
+ raise Puppet::Error, "User %s not found" % user
+ end
end
@tabs[user] ||= @filetype.new(user)
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb
index b2c5161cf..b03f57907 100755
--- a/lib/puppet/type/group.rb
+++ b/lib/puppet/type/group.rb
@@ -12,7 +12,7 @@ require 'puppet/type/state'
require 'puppet/type/nameservice'
module Puppet
- newtype(:group, Puppet::Type::NSSType) do
+ newtype(:group) do
@doc = "Manage groups. This type can only create groups. Group
membership must be managed on individual users. This element type
uses the prescribed native tools for creating groups and generally
@@ -23,32 +23,23 @@ module Puppet
for Mac OS X, NetInfo is used. This is currently unconfigurable,
but if you desperately need it to be so, please contact us."
- case Facter["operatingsystem"].value
- when "Darwin":
- @parentstate = Puppet::NameService::NetInfo::NetInfoState
- @parentmodule = Puppet::NameService::NetInfo
- when "FreeBSD":
- @parentstate = Puppet::NameService::PW::PWGroup
- @parentmodule = Puppet::NameService::PW
- else
- @parentstate = Puppet::NameService::ObjectAdd::ObjectAddGroup
- @parentmodule = Puppet::NameService::ObjectAdd
- end
+ newstate(:ensure) do
+ desc "The basic state that the object should be in."
- newstate(:ensure, @parentstate) do
newvalue(:present) do
- self.syncname(:present)
+ provider.create
+
+ :group_created
end
newvalue(:absent) do
- self.syncname(:absent)
- end
+ provider.delete
- desc "The basic state that the object should be in."
+ :group_removed
+ end
# If they're talking about the thing at all, they generally want to
# say it should exist.
- #defaultto :present
defaultto do
if @parent.managed?
:present
@@ -77,7 +68,7 @@ module Puppet
end
def retrieve
- if @parent.exists?
+ if provider.exists?
@is = :present
else
@is = :absent
@@ -104,7 +95,7 @@ module Puppet
end
- newstate(:gid, @parentstate) do
+ newstate(:gid) do
desc "The group ID. Must be specified numerically. If not
specified, a number will be picked, which can result in ID
differences across systems and thus is not recommended. The
@@ -131,6 +122,19 @@ module Puppet
return @@prevauto
end
+ def retrieve
+ @is = provider.gid
+ end
+
+ def sync
+ if self.should == :absent
+ raise Puppet::DevError, "GID cannot be deleted"
+ else
+ provider.gid = self.should
+ :group_modified
+ end
+ end
+
munge do |gid|
case gid
when String
@@ -140,28 +144,15 @@ module Puppet
self.fail "Invalid GID %s" % gid
end
when Symbol
- unless gid == :auto or gid == :absent
+ unless gid == :absent
self.devfail "Invalid GID %s" % gid
end
- if gid == :auto
- # FIXME this should be done at sync time, not
- # here.
- unless self.class.autogen?
- gid = autogen()
- end
- end
end
return gid
end
end
- class << self
- attr_accessor :netinfodir
- end
-
- @netinfodir = "groups"
-
newparam(:name) do
desc "The group name. While naming limitations vary by
system, it is advisable to keep the name to the degenerate
@@ -172,7 +163,8 @@ module Puppet
end
newparam(:allowdupe) do
- desc "Whether to allow duplicate GIDs."
+ desc "Whether to allow duplicate GIDs. This option does not work on
+ FreeBSD (contract to the ``pw`` man page)."
newvalues(:true, :false)
@@ -190,35 +182,14 @@ module Puppet
return groups
end
- def exists?
- self.class.parentmodule.exists?(self)
- end
-
- def getinfo(refresh = false)
- if @groupinfo.nil? or refresh == true
- begin
- @groupinfo = Etc.getgrnam(self[:name])
- rescue ArgumentError => detail
- @groupinfo = nil
- end
- end
-
- @groupinfo
- end
-
- def initialize(hash)
- @groupinfo = nil
- super
- end
-
def retrieve
- if self.exists?
+ if @provider.exists?
super
else
# the group does not exist
- unless @states.include?(:gid)
- self[:gid] = :auto
- end
+ #unless @states.include?(:gid)
+ # self[:gid] = :auto
+ #end
@states.each { |name, state|
state.is = :absent
diff --git a/lib/puppet/type/nameservice.rb b/lib/puppet/type/nameservice.rb
index 34a5a08e8..6db2ff083 100755
--- a/lib/puppet/type/nameservice.rb
+++ b/lib/puppet/type/nameservice.rb
@@ -128,33 +128,6 @@ class State
end
end
- # The list of all groups the user is a member of. Different
- # user mgmt systems will need to override this method.
- def grouplist
- groups = []
-
- # Reset our group list
- Etc.setgrent
-
- user = @parent[:name]
-
- # Now iterate across all of the groups, adding each one our
- # user is a member of
- while group = Etc.getgrent
- members = group.mem
-
- if members.include? user
- groups << group.name
- end
- end
-
- # We have to close the file, so each listing is a separate
- # reading of the file.
- Etc.endgrent
-
- groups
- end
-
# Sync the information.
def sync
event = nil
@@ -195,57 +168,6 @@ class State
return "#{@parent.class.name}_modified".intern
end
end
-
- # This is only used when creating or destroying the object.
- def syncname(value)
- cmd = nil
- event = nil
- case value
- when :absent
- # we need to remove the object...
- unless @parent.exists?
- self.info "already absent"
- # the object already doesn't exist
- return nil
- end
-
- # again, needs to be set by the ind. state or its
- # parent
- cmd = self.deletecmd
- type = "delete"
- when :present
- if @parent.exists?
- self.info "already exists"
- # The object already exists
- return nil
- end
-
- # blah blah, define elsewhere, blah blah
- cmd = self.addcmd
- type = "create"
- end
- self.debug "Executing %s" % cmd.inspect
-
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::Error, "Could not %s %s %s: %s" %
- [type, @parent.class.name, @parent.name, output]
- end
-
- # we want object creation to show up as one event,
- # not many
- unless self.class.allatonce?
- Puppet.debug "%s is not allatonce" % @parent.class.name
- if type == "create"
- @parent.eachstate { |state|
- next if state.name == :ensure
- state.sync
- state.retrieve
- }
- end
- end
- end
end
end
end
diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb
index e75f6c288..f7ae0f0d4 100644
--- a/lib/puppet/type/package.rb
+++ b/lib/puppet/type/package.rb
@@ -20,97 +20,6 @@ module Puppet
using the ``type`` parameter; obviously, if you specify that you
want to use ``rpm`` then the ``rpm`` tools must be available."
- # Create a new packaging type
- def self.newpkgtype(name, parent = nil, &block)
- @pkgtypes ||= {}
-
- if @pkgtypes.include?(name)
- raise Puppet::DevError, "Package type %s already defined" % name
- end
-
- mod = Module.new
- const_set("PkgType" + name.to_s.capitalize,mod)
-
- # Add our parent, if it exists
- if parent
- unless parenttype = pkgtype(parent)
- raise Puppet::DevError,
- "No parent type %s for package type %s" %
- [parent, name]
- end
- mod.send(:include, parenttype)
- end
-
- # And now define the support methods
- code = %{
- def self.name
- "#{name}"
- end
-
- def self.to_s
- "PkgType(#{name})"
- end
-
- def pkgtype
- "#{name}"
- end
- }
-
- mod.module_eval(code)
-
- mod.module_eval(&block)
-
- class << mod
- include Puppet::Util
- end
-
- # It's at least conceivable that a module would not define this method
- # "module_function" makes the :list method private, so if the parent
- # method also called module_function, then it's already private
- if mod.public_method_defined? :list or mod.private_method_defined? :list
- mod.send(:module_function, :list)
- end
-
- # Add it to our list
- @pkgtypes[name] = mod
-
- # And mark it as a valid type
- unless defined? @typeparam
- @typeparam = @parameters.find { |p| p.name == :type }
-
- unless @typeparam
- Puppet.warning @parameters.inspect
- raise Puppet::DevError, "Could not package type parameter"
- end
- end
- @typeparam.newvalues(name)
- end
-
- # Autoload the package types, if they're not already defined.
- def self.pkgtype(name)
- #name = name[0] if name.is_a? Array
- name = name.intern if name.is_a? String
- @pkgtypes ||= {}
- unless @pkgtypes.include? name
- begin
- require "puppet/type/package/#{name}"
-
- unless @pkgtypes.include? name
- Puppet.warning @pkgtypes.keys
- raise Puppet::DevError,
- "Loaded %s but pkgtype was not created" % name.inspect
- end
- rescue LoadError
- raise Puppet::Error, "Could not load package type %s" % name
- end
- end
- @pkgtypes[name]
- end
-
- def self.pkgtypes
- @pkgtypes.keys
- end
-
ensurable do
desc "What state the package should be in.
*latest* only makes sense for those packaging formats that can
@@ -119,22 +28,22 @@ module Puppet
attr_accessor :latest
- newvalue(:present) do
- @parent.install
+ newvalue(:present, :event => :package_installed) do
+ provider.install
end
- newvalue(:absent) do
- @parent.uninstall
+ newvalue(:absent, :event => :package_removed) do
+ provider.uninstall
end
# Alias the 'present' value.
aliasvalue(:installed, :present)
newvalue(:latest) do
- unless @parent.respond_to?(:latest)
+ unless provider.respond_to?(:latest)
self.fail(
- "Package type %s does not support specifying 'latest'" %
- @parent[:type]
+ "Package provider %s does not support specifying 'latest'" %
+ @parent[:provider]
)
end
@@ -143,13 +52,13 @@ module Puppet
# to compare against later.
current = self.is
begin
- @parent.update
+ provider.update
rescue => detail
self.fail "Could not update: %s" % detail
end
if current == :absent
- return :package_created
+ return :package_installed
else
return :package_changed
end
@@ -178,10 +87,10 @@ module Puppet
return false
end
- unless @parent.respond_to?(:latest)
+ unless provider.respond_to?(:latest)
self.fail(
"Package type %s does not support specifying 'latest'" %
- @parent[:type]
+ @parent[:provider]
)
end
@@ -190,7 +99,7 @@ module Puppet
#self.debug "Skipping latest check"
else
begin
- @latest = @parent.latest
+ @latest = provider.latest
@lateststamp = Time.now.to_i
rescue => detail
self.fail "Could not get latest version: %s" % detail
@@ -224,7 +133,7 @@ module Puppet
# This retrieves the current state
def retrieve
- @parent.retrieve
+ @is = @parent.retrieve
end
def sync
@@ -238,15 +147,12 @@ module Puppet
super
else
#self.info "updating from %s" % value
- @parent.update
+ provider.update
+ :package_updated
end
end
end
- # Packages are complicated because each package format has completely
- # different commands.
- attr_reader :pkgtype
-
newparam(:name) do
desc "The package name. This is the name that the packaging
system uses internally, which is sometimes (especially on Solaris)
@@ -286,32 +192,6 @@ module Puppet
isnamevar
end
- newparam(:type) do
- desc "The package format. You will seldom need to specify this --
- Puppet will discover the appropriate format for your platform."
-
- defaultto { @parent.class.default }
-
-
- validate do |value|
- value = value[0] if value.is_a? Array
- unless @parent.class.pkgtype(value)
- raise ArgumentError, "Invalid package type '%s'" % value
- end
- end
-
-
- munge do |type|
- type = type[0] if type.is_a? Array
- if type.is_a? String
- type = type.intern
- end
- @parent.type2module(type)
- type
- end
-
- end
-
newparam(:source) do
desc "From where to retrieve the package."
@@ -330,6 +210,17 @@ module Puppet
desc "A read-only parameter set by the package."
end
+ newparam(:type) do
+ desc "Deprecated form of ``use``."
+
+ munge do |value|
+ warning "'type' is deprecated; use 'use' instead"
+ @parent[:provider] = value
+
+ @parent[: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
@@ -406,9 +297,6 @@ module Puppet
@allowedmethods = [:types]
- @default = nil
- @platform = nil
-
class << self
attr_reader :listed
end
@@ -418,53 +306,10 @@ module Puppet
super
end
- # Cache and return the default package type for our current
- # platform.
- def self.default
- if @default.nil?
- self.init
- end
-
- return @default
- end
-
- # Figure out what the default package type is for the platform
- # on which we're running.
- def self.init
- unless @platform = Facter["operatingsystem"].value.downcase
- raise Puppet::DevError.new(
- "Must know platform for package management"
- )
- end
- case @platform
- when "solaris": @default = :sun
- when "gentoo":
- Puppet.notice "No support for gentoo yet"
- @default = nil
- when "debian": @default = :apt
- when "centos": @default = :rpm
- when "fedora": @default = :yum
- when "redhat": @default = :rpm
- when "freebsd": @default = :ports
- when "openbsd": @default = :openbsd
- when "darwin": @default = :apple
- else
- if Facter["kernel"] == "Linux"
- Puppet.warning "Defaulting to RPM for %s" %
- Facter["operatingsystem"].value
- @default = :rpm
- else
- Puppet.warning "No default package system for %s" %
- Facter["operatingsystem"].value
- @default = nil
- end
- end
- end
-
# Create a new package object from listed information
def self.installedpkg(hash)
- unless hash.include? :type
- raise Puppet::DevError, "Got installed package with no type"
+ unless hash.include? :provider
+ raise Puppet::DevError, "Got installed package with no provider"
end
# this is from code, so we don't have to do as much checking
name = hash[:name]
@@ -478,7 +323,15 @@ module Puppet
# List all package instances
def self.list
- pkgtype(default).list()
+ # XXX For now, just list the default provider, but we should list
+ # all suitables or something, but we need to not list a parent
+ # type if a child type gets listed.
+ #suitableprovider.each do |provider|
+ # p provider.name
+ # provider.list
+ #end
+
+ defaultprovider.list
self.collect do |pkg|
pkg
@@ -490,7 +343,7 @@ module Puppet
def self.markabsent(pkgtype, packages)
# Mark any packages we didn't find as absent
self.each do |pkg|
- next unless packages[:type] == pkgtype
+ next unless packages[:provider] == pkgtype
unless packages.include? pkg
pkg.is = [:ensure, :absent]
end
@@ -505,7 +358,7 @@ module Puppet
# The 'query' method returns a hash of info if the package
# exists and returns nil if it does not.
def exists?
- self.query
+ @provider.query
end
# okay, there are two ways that a package could be created...
@@ -515,16 +368,16 @@ module Puppet
def initialize(hash)
self.initvars
type = nil
- [:type, "type"].each { |label|
+ [:provider, "use"].each { |label|
if hash.include?(label)
type = hash[label]
hash.delete(label)
end
}
if type
- self[:type] = type
+ self[:provider] = type
else
- self.setdefaults(:type)
+ self.setdefaults(:provider)
end
super
@@ -534,8 +387,8 @@ module Puppet
# self[:ensure] = true
#end
- unless @parameters.include?(:type)
- self[:type] = self.class.default
+ unless @parameters.include?(:provider)
+ raise Puppet::DevError, "No package type set"
end
end
@@ -543,7 +396,7 @@ module Puppet
# If the package is installed, then retrieve all of the information
# about it and set it appropriately.
#@states[:ensure].retrieve
- if hash = self.query
+ if hash = @provider.query
if hash == :listed # Mmmm, hackalicious
return
end
@@ -574,17 +427,6 @@ module Puppet
end
}
end
-
- # Extend the package with the appropriate package type.
- def type2module(typename)
- if type = self.class.pkgtype(typename)
- self.extend(type)
-
- return type
- else
- self.fail "Invalid package type %s" % typename
- end
- end
end # Puppet.type(:package)
end
diff --git a/lib/puppet/type/package/apple.rb b/lib/puppet/type/package/apple.rb
deleted file mode 100755
index 563449c2e..000000000
--- a/lib/puppet/type/package/apple.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-module Puppet
- # OS X Packaging sucks. We can install packages, but that's about it.
- Puppet.type(:package).newpkgtype(:apple) do
- def query
- if FileTest.exists?("/Library/Receipts/#{self[:name]}.pkg")
- return {:name => self[:name], :ensure => :present}
- else
- return nil
- end
- end
-
- def install
- source = nil
- unless source = self[:source]
- self.fail "Mac OS X packages must specify a package source"
- end
-
- output = %x{/usr/sbin/installer -pkg #{source} -target / 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- def list
- packages = []
-
- Dir.entries("/Library/Receipts").find { |f|
- f =~ /\.pkg$/
- }.collect { |f|
- Puppet.type(:package).installedpkg(
- :name => f.sub(/\.pkg/, ''),
- :type => :apple,
- :ensure => :installed
- )
- }
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/apt.rb b/lib/puppet/type/package/apt.rb
deleted file mode 100755
index cc1e26a06..000000000
--- a/lib/puppet/type/package/apt.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:apt, :dpkg) do
- ENV['DEBIAN_FRONTEND'] = "noninteractive"
-
-
-
- # A derivative of DPKG; this is how most people actually manage
- # Debian boxes, and the only thing that differs is that it can
- # install packages from remote sites.
-
- def checkforcdrom
- unless defined? @@checkedforcdrom
- if FileTest.exists? "/etc/apt/sources.list"
- if File.read("/etc/apt/sources.list") =~ /^[^#]*cdrom:/
- @@checkedforcdrom = true
- else
- @@checkedforcdrom = false
- end
- else
- # This is basically a pathalogical case, but we'll just
- # ignore it
- @@checkedforcdrom = false
- end
- end
-
- if @@checkedforcdrom and self[:allowcdrom] != :true
- raise Puppet::Error,
- "/etc/apt/sources.list contains a cdrom source; not installing. Use 'allowcdrom' to override this failure."
- end
- end
-
- # Install a package using 'apt-get'. This function needs to support
- # installing a specific version.
- def install
- should = self.should(:ensure)
-
- checkforcdrom()
-
- str = self[:name]
- case should
- when true, false, Symbol
- # pass
- else
- # Add the package version
- str += "=%s" % should
- end
- cmd = "/usr/bin/apt-get -q -y install %s" % str
-
- self.info "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- # What's the latest package version available?
- def latest
- cmd = "/usr/bin/apt-cache showpkg %s" % self[:name]
- self.info "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
-
- if output =~ /Versions:\s*\n((\n|.)+)^$/
- versions = $1
- version = versions.split(/\n/).collect { |version|
- if version =~ /^([^\(]+)\(/
- $1
- else
- self.warning "Could not match version '%s'" % version
- nil
- end
- }.reject { |vers| vers.nil? }.sort[-1]
-
- unless version
- self.debug "No latest version"
- if Puppet[:debug]
- print output
- end
- end
-
- return version
- else
- self.err "Could not match string"
- end
- end
-
- def update
- self.install
- end
-
- def uninstall
- cmd = "/usr/bin/apt-get -y -q remove %s" % self[:name]
- output = %x{#{cmd} 2>&1}
- if $? != 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- def versionable?
- true
- end
- end
-end
diff --git a/lib/puppet/type/package/blastwave.rb b/lib/puppet/type/package/blastwave.rb
deleted file mode 100755
index 7ef91eca4..000000000
--- a/lib/puppet/type/package/blastwave.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:blastwave, :sun) do
- if pkgget = %x{which pkg-get 2>/dev/null}.chomp and pkgget != ""
- @@pkgget = pkgget
- else
- @@pkgget = nil
- end
-
- # This is so stupid
- ENV["PAGER"] = "/usr/bin/cat"
-
- def self.extended(mod)
- unless @@pkgget
- raise Puppet::Error,
- "The pkg-get command is missing; blastwave packaging unavailable"
- end
-
- unless FileTest.exists?("/var/pkg-get/admin")
- Puppet.notice "It is highly recommended you create '/var/pkg-get/admin'."
- Puppet.notice "See /var/pkg-get/admin-fullauto"
- end
- end
-
- # Turn our blastwave listing into a bunch of hashes.
- def blastlist(hash)
- command = "#{@@pkgget} -c"
-
- if hash[:justme]
- command += " " + self[:name]
- end
-
- begin
- output = execute(command)
- rescue ExecutionFailure => detail
- raise Puppet::Error, "Could not get package listing: %s" %
- detail
- end
-
- list = output.split("\n").collect do |line|
- next if line =~ /^#/
- next if line =~ /^WARNING/
- next if line =~ /localrev\s+remoterev/
-
- blastsplit(line)
- end.reject { |h| h.nil? }
-
- if hash[:justme]
- return list[0]
- else
- list.reject! { |h|
- h[:ensure] == :absent
- }
- return list
- end
-
- end
-
- # Split the different lines into hashes.
- def blastsplit(line)
- if line =~ /\s*(\S+)\s+((\[Not installed\])|(\S+))\s+(\S+)/
- hash = {}
- hash[:name] = $1
- hash[:ensure] = if $2 == "[Not installed]"
- :absent
- else
- $2
- end
- hash[:avail] = $5
-
- if hash[:avail] == "SAME"
- hash[:avail] = hash[:ensure]
- end
- hash[:type] = :blastwave
-
- return hash
- else
- Puppet.warning "Cannot match %s" % line
- return nil
- end
- end
-
- module_function :blastlist, :blastsplit
-
- def install
- begin
- execute("#{@@pkgget} -f install #{self[:name]}")
- rescue ExecutionFailure => detail
- raise Puppet::Error,
- "Could not install %s: %s" %
- [self[:name], detail]
- end
- end
-
- # Retrieve the version from the current package file.
- def latest
- hash = blastlist(:justme => true)
- hash[:avail]
- end
-
- def list(hash = {})
- blastlist(hash).each do |bhash|
- bhash.delete(:avail)
- Puppet::Type.type(:package).installedpkg(bhash)
- end
- end
-
- def query
- hash = blastlist(:justme => true)
-
- {:ensure => hash[:ensure]}
- end
-
- # Remove the old package, and install the new one
- def update
- begin
- execute("#{@@pkgget} -f upgrade #{self[:name]}")
- rescue ExecutionFailure => detail
- raise Puppet::Error,
- "Could not upgrade %s: %s" %
- [self[:name], detail]
- end
- end
-
- def uninstall
- begin
- execute("#{@@pkgget} -f remove #{self[:name]}")
- rescue ExecutionFailure => detail
- raise Puppet::Error,
- "Could not remove %s: %s" %
- [self[:name], detail]
- end
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/darwinport.rb b/lib/puppet/type/package/darwinport.rb
deleted file mode 100755
index 0d8fc288d..000000000
--- a/lib/puppet/type/package/darwinport.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:darwinport) do
- def port
- "/opt/local/bin/port"
- end
-
- def eachpkgashash
- # list out all of the packages
- open("| #{port} list installed") { |process|
- regex = %r{(\S+)\s+@(\S+)\s+(\S+)}
- fields = [:name, :version, :location]
- hash = {}
-
- # now turn each returned line into a package object
- process.each { |line|
- hash.clear
-
- if match = regex.match(line)
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
-
- hash.delete :location
- hash[:ensure] = hash[:version]
- yield hash.dup
- else
- raise Puppet::DevError,
- "Failed to match dpkg line %s" % line
- end
- }
- }
- end
-
- def install
- should = self.should(:ensure)
-
- # Seems like you can always say 'upgrade'
- cmd = "#{port()} upgrade #{self[:name]}"
-
- self.info "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- def list
- packages = []
-
- eachpkgashash do |hash|
- pkg = Puppet.type(:package).installedpkg(hash)
- packages << pkg
- end
-
- return packages
- end
-
- def query
- version = nil
- eachpkgashash do |hash|
- if hash[:name] == self[:name]
- return hash
- end
- end
-
- return nil
- end
-
- def latest
- info = %x{#{port()} search '^#{self[:name]}$' 2>/dev/null}
-
- if $? != 0 or info =~ /^Error/
- return nil
- end
-
- ary = info.split(/\s+/)
- version = ary[2].sub(/^@/, '')
-
- return version
- end
-
- def uninstall
- cmd = "#{port()} uninstall #{self[:name]}"
- output = %x{#{cmd} 2>&1}
- if $? != 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- def update
- return install()
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/dpkg.rb b/lib/puppet/type/package/dpkg.rb
deleted file mode 100755
index c66897b8c..000000000
--- a/lib/puppet/type/package/dpkg.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:dpkg) do
- def query
- packages = []
-
- # dpkg only prints as many columns as you have available
- # which means we don't get all of the info
- # stupid stupid
- oldcol = ENV["COLUMNS"]
- ENV["COLUMNS"] = "500"
- fields = [:desired, :status, :error, :name, :version, :description]
-
- hash = {}
- # list out our specific package
- open("| /usr/bin/dpkg -l %s 2>/dev/null" % self[:name]) { |process|
- # our regex for matching dpkg output
- regex = %r{^(.)(.)(.)\s(\S+)\s+(\S+)\s+(.+)$}
-
- # we only want the last line
- lines = process.readlines
- # we've got four header lines, so we should expect all of those
- # plus our output
- if lines.length < 5
- return nil
- end
-
- line = lines[-1]
-
- if match = regex.match(line)
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
- #packages.push Puppet.type(:package).installedpkg(hash)
- else
- raise Puppet::DevError,
- "failed to match dpkg line %s" % line
- end
- }
- ENV["COLUMNS"] = oldcol
-
- if hash[:error] != " "
- raise Puppet::PackageError.new(
- "Package %s, version %s is in error state: %s" %
- [hash[:name], hash[:version], hash[:error]]
- )
- end
-
- if hash[:status] == "i"
- hash[:ensure] = :present
- else
- hash[:ensure] = :absent
- end
-
- return hash
- end
-
- def list
- packages = []
-
- # dpkg only prints as many columns as you have available
- # which means we don't get all of the info
- # stupid stupid
- oldcol = ENV["COLUMNS"]
- ENV["COLUMNS"] = "500"
-
- # list out all of the packages
- open("| /usr/bin/dpkg -l") { |process|
- # our regex for matching dpkg output
- regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}
- fields = [:status, :name, :version, :description]
- hash = {}
-
- 5.times { process.gets } # throw away the header
-
- # now turn each returned line into a package object
- process.each { |line|
- if match = regex.match(line)
- hash.clear
-
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
-
- if self.is_a? Puppet::Type and type = self[:type]
- hash[:type] = type
- elsif self.is_a? Module and self.respond_to? :name
- hash[:type] = self.name
- else
- raise Puppet::DevError, "Cannot determine package type"
- end
- packages.push Puppet.type(:package).installedpkg(hash)
- else
- raise Puppet::DevError,
- "Failed to match dpkg line %s" % line
- end
- }
- }
- ENV["COLUMNS"] = oldcol
-
- return packages
- end
-
- def uninstall
- cmd = "/usr/bin/dpkg -r %s" % self[:name]
- output = %x{#{cmd} 2>&1}
- if $? != 0
- raise Puppet::PackageError.new(output)
- end
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/freebsd.rb b/lib/puppet/type/package/freebsd.rb
deleted file mode 100755
index bb36f7650..000000000
--- a/lib/puppet/type/package/freebsd.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:freebsd, :openbsd) do
- def listcmd
- "pkg_info"
- end
-
- def query
- list
-
- if self[:version]
- return :listed
- else
- return nil
- end
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/gem.rb b/lib/puppet/type/package/gem.rb
deleted file mode 100755
index 31d6ad226..000000000
--- a/lib/puppet/type/package/gem.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:gem) do
- if gem = %x{which gem 2>/dev/null}.chomp and gem != "" and gem !~ /^no /
- @@gem = gem
- else
- @@gem = nil
- end
- def self.extended(mod)
- unless @@gem
- raise Puppet::Error,
- "The gem command is missing; gems unavailable"
- end
- end
-
- def gemlist(hash)
- command = "#{@@gem} list "
-
- if hash[:local]
- command += "--local "
- else
- command += "--remote "
- end
-
- if hash[:justme]
- command += self[:name]
- end
- begin
- list = execute(command).split("\n\n").collect do |set|
- if gemhash = gemsplit(set)
- gemhash[:type] = :gem
- gemhash[:ensure] = gemhash[:version][0]
- gemhash
- else
- nil
- end
- end.reject { |p| p.nil? }
- rescue ExecutionFailure => detail
- raise Puppet::Error, "Could not list gems: %s" % detail
- end
-
- if hash[:justme]
- return list.shift
- else
- return list
- end
- end
-
- module_function :gemlist
-
- def gemsplit(desc)
- case desc
- when /^\*\*\*/: return nil
- when /^(\S+)\s+\((.+)\)\n/
- name = $1
- version = $2.split(/,\s*/)
- return {
- :name => name,
- :version => version
- }
- else
- Puppet.warning "Could not match %s" % desc
- nil
- end
- end
-
- module_function :gemsplit
-
- def install(useversion = true)
- command = "#{@@gem} install "
- if self[:version] and useversion
- command += "-v %s " % self[:version]
- end
- if source = self[:source]
- command += source
- else
- command += self[:name]
- end
- begin
- execute(command)
- rescue ExecutionFailure => detail
- raise Puppet::Error, "Could not install %s: %s" %
- [self[:name], detail]
- end
- end
-
- def latest
- # This always gets the latest version available.
- hash = gemlist(:justme => true)
-
- return hash[:version][0]
- end
-
- def list(justme = false)
- gemlist(:local => true).each do |hash|
- Puppet::Type.type(:package).installedpkg(hash)
- end
- end
-
- def query
- gemlist(:justme => true, :local => true)
- end
-
- def uninstall
- begin
- # Remove everything, including the binaries.
- execute("#{@@gem} uninstall -x -a #{self[:name]}")
- rescue ExecutionFailure => detail
- raise Puppet::Error, "Could not uninstall %s: %s" %
- [self[:name], detail]
- end
- end
-
- def update
- self.install(false)
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/openbsd.rb b/lib/puppet/type/package/openbsd.rb
deleted file mode 100755
index 441ff83d2..000000000
--- a/lib/puppet/type/package/openbsd.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:openbsd) do
- def listcmd
- "pkg_info -a"
- end
-
- module_function :listcmd
-
- def install
- should = self.should(:ensure)
-
- unless self[:source]
- raise Puppet::Error,
- "You must specify a package source for BSD packages"
- end
-
- cmd = "pkg_add #{self[:source]}"
-
- self.info "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- def query
- hash = {}
- # list out our specific package
- info = %x{pkg_info #{self[:name]} 2>/dev/null}
-
- # Search for the version info
- if info =~ /Information for #{self[:name]}-(\S+)/
- hash[:version] = $1
- hash[:ensure] = :present
- else
- return nil
- end
-
- # And the description
- if info =~ /Comment:\s*\n(.+)/
- hash[:description] = $1
- end
-
- return hash
- end
-
- def list
- packages = []
-
- if self.is_a? Puppet::Type
- debug "Executing %s" % listcmd().inspect
- else
- Puppet.debug "Executing %s" % listcmd().inspect
- end
- # list out all of the packages
- open("| #{listcmd()}") { |process|
- # our regex for matching pkg_info output
- regex = %r{^(\S+)-([^-\s]+)\s+(.+)}
- fields = [:name, :version, :description]
- hash = {}
-
- # now turn each returned line into a package object
- process.each { |line|
- hash.clear
- if match = regex.match(line)
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
- yup = nil
- name = hash[:name]
- hash[:ensure] = :present
-
- if self.is_a? Puppet::Type and type = self[:type]
- hash[:type] = type
- elsif self.is_a? Module and self.respond_to? :name
- hash[:type] = self.name
- else
- raise Puppet::DevError, "Cannot determine package type"
- end
-
- pkg = Puppet.type(:package).installedpkg(hash)
- packages << pkg
- 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
- }
- }
-
- # Mark as absent any packages we didn't find
- Puppet.type(:package).each do |pkg|
- unless packages.include? pkg
- pkg.is = [:ensure, :absent]
- end
- end
-
- return packages
- end
-
- def uninstall
- cmd = "pkg_delete %s" % self[:name]
- output = %x{#{cmd} 2>&1}
- if $? != 0
- raise Puppet::PackageError.new(output)
- end
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/ports.rb b/lib/puppet/type/package/ports.rb
deleted file mode 100755
index 972aa90be..000000000
--- a/lib/puppet/type/package/ports.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:ports, :openbsd) do
- # I hate ports
- %w{INTERACTIVE UNAME}.each do |var|
- if ENV.include?(var)
- ENV.delete(var)
- end
- end
- def install
- # -p: create a package
- # -N: install if the package is missing, otherwise upgrade
- # -P: prefer binary packages
- cmd = "/usr/local/sbin/portupgrade -p -N -P #{self[:name]}"
-
- self.debug "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1 1>/dev/null}
-
- if output =~ /\*\* No such /
- raise Puppet::PackageError, "Could not find package %s" % self[:name]
- end
- #output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- # If there are multiple packages, we only use the last one
- def latest
- cmd = "/usr/local/sbin/portversion -v #{self[:name]}"
-
- self.debug "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>/dev/null}.chomp
- line = output.split("\n").pop
-
- unless line =~ /^(\S+)\s+(\S)\s+(.+)$/
- # There's no "latest" version, so just return a placeholder
- return :latest
- end
-
- pkgstuff = $1
- match = $2
- info = $3
-
- unless pkgstuff =~ /^(\w+)-([0-9].+)$/
- raise Puppet::PackageError,
- "Could not match package info '%s'" % pkgstuff
- end
-
- name, version = $1, $2
-
- if match == "=" or match == ">"
- # we're up to date or more recent
- return version
- end
-
- # Else, we need to be updated; we need to pull out the new version
-
- unless info =~ /\((\w+) has (.+)\)/
- raise Puppet::PackageError,
- "Could not match version info '%s'" % info
- end
-
- source, newversion = $1, $2
-
- debug "Newer version in %s" % source
- return newversion
- end
-
- def listcmd
- "pkg_info"
- end
-
- module_function :listcmd
-
- def query
- list
-
- if self[:version] and @states[:ensure].is != :absent
- return :listed
- else
- return nil
- end
- end
-
- def uninstall
- cmd = "/usr/local/sbin/pkg_deinstall #{self[:name]}"
- self.debug "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
-
- end
-
- def update
- install()
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/rpm.rb b/lib/puppet/type/package/rpm.rb
deleted file mode 100755
index 153f9167b..000000000
--- a/lib/puppet/type/package/rpm.rb
+++ /dev/null
@@ -1,121 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:rpm) do
- VERSIONSTRING = "%{VERSION}-%{RELEASE}"
- def query
- fields = {
- :name => "NAME",
- :version => "VERSION",
- :description => "DESCRIPTION"
- }
-
- cmd = "rpm -q #{self[:name]} --qf '%s\n'" %
- "%{NAME} #{VERSIONSTRING}"
-
- self.debug "Executing %s" % cmd.inspect
- # list out all of the packages
- output = %x{#{cmd} 2>/dev/null}.chomp
-
- if $? != 0
- #if Puppet[:debug]
- # puts output
- #end
- return nil
- end
-
- regex = %r{^(\S+)\s+(\S+)}
- #fields = [:name, :ensure, :description]
- fields = [:name, :version]
- hash = {}
- if match = regex.match(output)
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
- else
- raise Puppet::DevError,
- "Failed to match rpm output '%s'" %
- output
- end
-
- hash[:ensure] = :present
-
- return hash
- end
-
- # Here we just retrieve the version from the file specified in the source.
- def latest
- unless source = self[:source]
- self.fail "RPMs must specify a package source"
- end
-
- cmd = "rpm -q --qf '#{VERSIONSTRING}' -p #{self[:source]}"
- self.debug "Executing %s" % cmd.inspect
- version = %x{#{cmd}}
-
- return version
- end
-
- def list
- packages = []
-
- # list out all of the packages
- open("| rpm -q -a --qf '%{NAME} #{VERSIONSTRING}\n'") { |process|
- # our regex for matching dpkg output
- regex = %r{^(\S+)\s+(\S+)}
- fields = [:name, :ensure]
- hash = {}
-
- # now turn each returned line into a package object
- process.each { |line|
- if match = regex.match(line)
- hash.clear
-
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
- if self.is_a? Puppet::Type and type = self[:type]
- hash[:type] = type
- elsif self.is_a? Module and self.respond_to? :name
- hash[:type] = self.name
- else
- raise Puppet::DevError, "Cannot determine package type"
- end
- packages.push Puppet.type(:package).installedpkg(hash)
- else
- raise "failed to match rpm line %s" % line
- end
- }
- }
-
- return packages
- end
-
- def install
- source = nil
- unless source = self[:source]
- self.fail "RPMs must specify a package source"
- end
-
- flag = "-i"
- if @states[:ensure].is != :absent
- flag = "-U"
- end
- output = %x{rpm #{flag} #{source} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- def uninstall
- cmd = "rpm -e %s" % self[:name]
- output = %x{#{cmd}}
- if $? != 0
- raise output
- end
- end
-
- def update
- self.install
- end
- end
-end
diff --git a/lib/puppet/type/package/sun.rb b/lib/puppet/type/package/sun.rb
deleted file mode 100755
index 1b419f4b9..000000000
--- a/lib/puppet/type/package/sun.rb
+++ /dev/null
@@ -1,174 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:sun) do
- # Get info on a package, optionally specifying a device.
- def info2hash(device = nil)
- names = {
- "PKGINST" => :name,
- "NAME" => nil,
- "CATEGORY" => :category,
- "ARCH" => :platform,
- "VERSION" => :ensure,
- "BASEDIR" => :root,
- "HOTLINE" => nil,
- "EMAIL" => nil,
- "VSTOCK" => nil,
- "VENDOR" => :vendor,
- "DESC" => :description,
- "PSTAMP" => nil,
- "INSTDATE" => nil,
- "STATUS" => nil,
- "FILES" => nil
- }
-
- hash = {}
- cmd = "pkginfo -l"
- if device
- cmd += " -d #{device}"
- end
- cmd += " #{self[:name]} 2>/dev/null"
-
- # list out all of the packages
- open("| #{cmd}") { |process|
- # we're using the long listing, so each line is a separate
- # piece of information
- process.each { |line|
- case line
- when /^$/: # ignore
- when /\s*([A-Z]+):\s+(.+)/:
- name = $1
- value = $2
- if names.include?(name)
- unless names[name].nil?
- hash[names[name]] = value
- end
- else
- self.notice "Ignoring unknown name %s" % name
- end
- when /\s+\d+.+/:
- # nothing; we're ignoring the FILES info
- end
- }
- }
-
- if hash.empty?
- return nil
- else
- return hash
- end
- end
-
- def install
- unless self[:source]
- raise Puppet::Error, "Sun packages must specify a package source"
- end
- cmd = ["pkgadd"]
-
- if self[:adminfile]
- cmd << " -a " + self[:adminfile]
- end
-
- if self[:responsefile]
- cmd << " -r " + self[:responsefile]
- end
-
- cmd += ["-d", self[:source]]
- cmd += ["-n", self[:name]]
- cmd << "2>&1"
- cmd = cmd.join(" ")
-
- self.debug "Executing %s" % cmd.inspect
- output = %x{#{cmd} 2>&1}
-
- unless $? == 0
- raise Puppet::PackageError.new(output)
- end
- end
-
- # Retrieve the version from the current package file.
- def latest
- hash = info2hash(self[:source])
- hash[:ensure]
- end
-
- def list
- packages = []
- hash = {}
- names = {
- "PKGINST" => :name,
- "NAME" => nil,
- "CATEGORY" => :category,
- "ARCH" => :platform,
- "VERSION" => :ensure,
- "BASEDIR" => :root,
- "HOTLINE" => nil,
- "EMAIL" => nil,
- "VENDOR" => :vendor,
- "DESC" => :description,
- "PSTAMP" => nil,
- "INSTDATE" => nil,
- "STATUS" => nil,
- "FILES" => nil
- }
-
- # list out all of the packages
- open("| pkginfo -l 2>&1") { |process|
- # we're using the long listing, so each line is a separate
- # piece of information
- process.each { |line|
- case line
- when /^$/:
- hash[:type] = :sun
-
- packages.push Puppet.type(:package).installedpkg(hash)
- hash.clear
- when /\s*(\w+):\s+(.+)/:
- name = $1
- value = $2
- if names.include?(name)
- unless names[name].nil?
- hash[names[name]] = value
- end
- else
- raise "Could not find %s" % name
- end
- when /\s+\d+.+/:
- # nothing; we're ignoring the FILES info
- end
- }
- }
- return packages
- end
-
- def query
- info2hash()
- end
-
- def uninstall
- command = "/usr/sbin/pkgrm -n "
-
- if self[:adminfile]
- command += " -a " + self[:adminfile]
- end
-
- command += " " + self[:name]
- begin
- execute(command)
- rescue ExecutionFailure => detail
- raise Puppet::Error,
- "Could not uninstall %s: %s" %
- [self[:name], detail]
- end
- end
-
- # Remove the old package, and install the new one. This will probably
- # often fail.
- def update
- if @states[:ensure].is != :absent
- self.uninstall
- end
- self.install
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/sunfreeware.rb b/lib/puppet/type/package/sunfreeware.rb
deleted file mode 100755
index 8778d37bb..000000000
--- a/lib/puppet/type/package/sunfreeware.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Puppet
- # At this point, it's an exact copy of the Blastwave stuff.
- Puppet.type(:package).newpkgtype(:sunfreeware, :blastwave) do
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/package/yum.rb b/lib/puppet/type/package/yum.rb
deleted file mode 100755
index fa214c7d6..000000000
--- a/lib/puppet/type/package/yum.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module Puppet
- Puppet.type(:package).newpkgtype(:yum, :rpm) do
- include Puppet::Util
- # Install a package using 'yum'.
- def install
- cmd = "yum -y install %s" % self[:name]
-
- begin
- output = execute(cmd)
- rescue Puppet::ExecutionFailure => detail
- raise Puppet::PackageError.new(detail)
- end
-
- @states[:ensure].retrieve
- if @states[:ensure].is == :absent
- raise Puppet::PackageError.new(
- "Could not find package %s" % self.name
- )
- end
- end
-
- # What's the latest package version available?
- def latest
- cmd = "yum list available %s" % self[:name]
-
- begin
- output = execute(cmd)
- rescue Puppet::ExecutionFailure => detail
- raise Puppet::PackageError.new(detail)
- end
-
- if output =~ /#{self[:name]}\S+\s+(\S+)\s/
- return $1
- else
- # Yum didn't find updates, pretend the current
- # version is the latest
- return self[:version]
- end
- end
-
- def update
- # Install in yum can be used for update, too
- self.install
- end
-
- def versionable?
- false
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/type/parsedtype.rb b/lib/puppet/type/parsedtype.rb
index 842346e1c..0bb4353ef 100755
--- a/lib/puppet/type/parsedtype.rb
+++ b/lib/puppet/type/parsedtype.rb
@@ -112,9 +112,9 @@ module Puppet
# Override 'newstate' so that all states default to having the
# correct parent type
- def self.newstate(name, parent = nil, &block)
- parent ||= Puppet::State::ParsedParam
- super(name, parent, &block)
+ def self.newstate(name, options = {}, &block)
+ options[:parent] ||= Puppet::State::ParsedParam
+ super(name, options, &block)
end
# Add another type var.
diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb
index b65a8cd54..427460c35 100644
--- a/lib/puppet/type/service.rb
+++ b/lib/puppet/type/service.rb
@@ -26,28 +26,28 @@ module Puppet
wherever possible, it relies on local tools to enable or disable
a given service. *true*/*false*/*runlevels*"
- newvalue(:true) do
- unless @parent.respond_to?(:enable)
+ newvalue(:true, :event => :service_enabled) do
+ unless provider.respond_to?(:enable)
raise Puppet::Error, "Service %s does not support enabling" %
@parent.name
end
- @parent.enable
+ provider.enable
end
- newvalue(:false) do
- unless @parent.respond_to?(:disable)
+ newvalue(:false, :event => :service_disabled) do
+ unless provider.respond_to?(:disable)
raise Puppet::Error, "Service %s does not support enabling" %
@parent.name
end
- @parent.disable
+ provider.disable
end
def retrieve
- unless @parent.respond_to?(:enabled?)
+ unless provider.respond_to?(:enabled?)
raise Puppet::Error, "Service %s does not support enabling" %
@parent.name
end
- @is = @parent.enabled?
+ @is = provider.enabled?
end
validate do |value|
@@ -80,13 +80,13 @@ module Puppet
case self.should
when :true
if @runlevel
- @parent.enable(@runlevel)
+ provider.enable(@runlevel)
else
- @parent.enable()
+ provider.enable()
end
return :service_enabled
when :false
- @parent.disable
+ provider.disable
return :service_disabled
end
end
@@ -96,34 +96,34 @@ module Puppet
newstate(:ensure) do
desc "Whether a service should be running. **true**/*false*"
- newvalue(:stopped) do
- @parent.stop
+ newvalue(:stopped, :event => :service_stopped) do
+ provider.stop
end
- newvalue(:running) do
- @parent.start
+ newvalue(:running, :event => :service_started) do
+ provider.start
end
aliasvalue(:false, :stopped)
aliasvalue(:true, :running)
def retrieve
- self.is = @parent.status
+ self.is = provider.status
end
def sync
- event = nil
- case self.should
- when :running
- @parent.start
- event = :service_started
- when :stopped
- @parent.stop
- event = :service_stopped
- else
- self.debug "Not running '%s' and shouldn't be running" %
- self
- end
+ event = super()
+# case self.should
+# when :running
+# provider.start
+# event = :service_started
+# when :stopped
+# provider.stop
+# event = :service_stopped
+# else
+# self.debug "Not running '%s' and shouldn't be running" %
+# self
+# end
if state = @parent.state(:enable)
state.retrieve
@@ -149,41 +149,13 @@ module Puppet
end
end
- newparam(:type) do
- desc "The service type. For most platforms, it does not make
- sense to set this parameter, as the default is based on
- the builtin service facilities. The service types available are:
-
- * **base**: You must specify everything.
- * **init**: Assumes ``start`` and ``stop`` commands exist, but you
- must specify everything else.
- * **debian**: Debian's own specific version of ``init``.
- * **smf**: Solaris 10's new Service Management Facility.
- "
-
- defaultto { @parent.class.defaulttype }
-
- # Make sure we've got the actual module, not just a string
- # representing the module.
- munge do |type|
- if type.is_a?(String)
- type = type.intern
- end
- if type.is_a?(Symbol)
- typeklass = @parent.class.svctype(type)
- end
- @parent.extend(typeklass)
-
- # Return the name, not the object
- return type
- end
- end
newparam(:binary) do
desc "The path to the daemon. This is only used for
systems that do not support init scripts. This binary will be
used to start the service if no ``start`` parameter is
provided."
end
+
newparam(:hasstatus) do
desc "Declare the the service's init script has a
functional status command. Based on testing, it was found
@@ -202,6 +174,16 @@ module Puppet
is in."
isnamevar
end
+
+ newparam(:type) do
+ desc "Deprecated form of ``provder``."
+
+ munge do |value|
+ warning "'type' is deprecated; use 'provider' instead"
+ @parent[:provider] = value
+ end
+ end
+
newparam(:path) do
desc "The search path for finding init scripts."
@@ -270,7 +252,7 @@ module Puppet
end
# Retrieve the default type for the current platform.
- def self.defaulttype
+ def self.disableddefaulttype
unless defined? @defsvctype
@defsvctype = nil
os = Facter["operatingsystem"].value
@@ -342,139 +324,6 @@ module Puppet
@paths[type].dup
end
- # Create new subtypes of service management.
- def self.newsvctype(name, parent = nil, &block)
- if parent
- parent = self.svctype(parent)
- end
- svcname = name
- mod = Module.new
- const_set("SvcType" + name.to_s.capitalize,mod)
-
- # Add our parent, if it exists
- if parent
- mod.send(:include, parent)
- end
-
- # And now define the support methods
- code = %{
- def self.name
- "#{svcname}"
- end
-
- def self.inspect
- "SvcType(#{svcname})"
- end
-
- def self.to_s
- "SvcType(#{svcname})"
- end
-
- def svctype
- "#{svcname}"
- end
- }
-
- mod.module_eval(code)
-
- mod.module_eval(&block)
-
- # Extend the service type with the util stuff
- mod.send(:include, Puppet::Util)
-
- #unless mod.respond_to? :list
- # Puppet.debug "Service type %s has no list method" % name
- #end
-
- # "module_function" makes the :list method private, so if the parent
- # method also called module_function, then it's already private
- if mod.public_method_defined? :list or mod.private_method_defined? :list
- mod.send(:module_function, :list)
- end
-
- # mark it as a valid type
- unless defined? @typeparam
- @typeparam = @parameters.find { |p| p.name == :type }
-
- unless @typeparam
- raise Puppet::DevError, "Could not package type parameter"
- end
- end
- @typeparam.newvalues(name)
-
- # And add it to our list of types
- @modules ||= Hash.new do |hash, key|
- if key.is_a?(String)
- key = key.intern
- end
-
- if hash.include?(key)
- hash[key]
- else
- nil
- end
- end
- @modules[name] = mod
- end
-
- # Retrieve a service type.
- def self.svctype(name)
- name = name.intern if name.is_a? String
-
- # Try autoloading lacking service types.
- unless @modules.include? name
- begin
- require "puppet/type/service/#{name}"
- unless @modules.include? name
- Puppet.warning(
- "Loaded puppet/type/service/#{name} but " +
- "service type was not created"
- )
- end
- rescue LoadError
- # nothing
- end
- end
- @modules[name]
- end
-
- # Execute a command. Basically just makes sure it exits with a 0
- # code.
- def execute(type, cmd)
- self.debug "Executing %s" % cmd.inspect
- output = %x(#{cmd} 2>&1)
- unless $? == 0
- self.fail "Could not %s %s: %s" %
- [type, self.name, output.chomp]
- end
- end
-
- # Get the process ID for a running process. Requires the 'pattern'
- # parameter.
- def getpid
- unless self[:pattern]
- self.fail "Either a stop command or a pattern must be specified"
- end
- ps = Facter["ps"].value
- unless ps and ps != ""
- self.fail(
- "You must upgrade Facter to a version that includes 'ps'"
- )
- end
- regex = Regexp.new(self[:pattern])
- self.debug "Executing '#{ps}'"
- IO.popen(ps) { |table|
- table.each { |line|
- if regex.match(line)
- ary = line.sub(/^\s+/, '').split(/\s+/)
- return ary[1]
- end
- }
- }
-
- return nil
- end
-
# Initialize the service. This is basically responsible for merging
# in the right module.
def initialize(hash)
@@ -485,95 +334,7 @@ module Puppet
self.configchk
end
end
-
- # Retrieve the service type.
- def type2module(type)
- self.class.svctype(type)
- end
-
- # Basically just a synonym for restarting. Used to respond
- # to events.
- def refresh
- self.restart
- end
-
- # How to restart the process.
- def restart
- if self[:restart] or self.respond_to?(:restartcmd)
- cmd = self[:restart] || self.restartcmd
- self.execute("restart", cmd)
- else
- self.stop
- self.start
- end
- end
-
- # Check if the process is running. Prefer the 'status' parameter,
- # then 'statuscmd' method, then look in the process table. We give
- # the object the option to not return a status command, which might
- # happen if, for instance, it has an init script (and thus responds to
- # 'statuscmd') but does not have 'hasstatus' enabled.
- def status
- if self[:status] or (
- self.respond_to?(:statuscmd) and self.statuscmd
- )
- cmd = self[:status] || self.statuscmd
- self.debug "Executing %s" % cmd.inspect
- output = %x(#{cmd} 2>&1)
- self.debug "%s status returned %s" %
- [self.name, output.inspect]
- if $? == 0
- return :running
- else
- return :stopped
- end
- elsif pid = self.getpid
- self.debug "PID is %s" % pid
- return :running
- else
- return :stopped
- end
- end
-
- # Run the 'start' parameter command, or the specified 'startcmd'.
- def start
- cmd = self[:start] || self.startcmd
- self.execute("start", cmd)
- end
-
- # Stop the service. If a 'stop' parameter is specified, it
- # takes precedence; otherwise checks if the object responds to
- # a 'stopcmd' method, and if so runs that; otherwise, looks
- # for the process in the process table.
- # This method will generally not be overridden by submodules.
- def stop
- if self[:stop]
- return self[:stop]
- elsif self.respond_to?(:stopcmd)
- self.execute("stop", self.stopcmd)
- else
- pid = getpid
- unless pid
- self.info "%s is not running" % self.name
- return false
- end
- output = %x(kill #{pid} 2>&1)
- if $? != 0
- self.fail "Could not kill %s, PID %s: %s" %
- [self.name, pid, output]
- end
- return true
- end
- end
end
end
-# Load all of the different service types. We could probably get away with
-# loading less here, but it's not a big deal to do so.
-require 'puppet/type/service/base'
-require 'puppet/type/service/init'
-require 'puppet/type/service/debian'
-require 'puppet/type/service/redhat'
-require 'puppet/type/service/smf'
-
# $Id$
diff --git a/lib/puppet/type/service/base.rb b/lib/puppet/type/service/base.rb
deleted file mode 100755
index f84fa85dc..000000000
--- a/lib/puppet/type/service/base.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-Puppet.type(:service).newsvctype(:base) do
- # The command used to start. Generated if the 'binary' argument
- # is passed.
- def startcmd
- if self[:binary]
- return self[:binary]
- else
- raise Puppet::Error,
- "Services must specify a start command or a binary"
- end
- end
-end
diff --git a/lib/puppet/type/state.rb b/lib/puppet/type/state.rb
index faa5353d0..c0330c13e 100644
--- a/lib/puppet/type/state.rb
+++ b/lib/puppet/type/state.rb
@@ -36,18 +36,40 @@ class State < Puppet::Parameter
end
end
+ # Only retrieve the event, don't autogenerate one.
+ def self.event(value)
+ if hash = @parameteroptions[value]
+ hash[:event]
+ else
+ nil
+ end
+ end
+
# Create the value management variables.
def self.initvars
@parametervalues = {}
@aliasvalues = {}
@parameterregexes = {}
+ @parameteroptions = {}
end
- # Parameters just use 'newvalues', since there's no work associated with them,
- # but states have blocks associated with their allowed values.
- def self.newvalue(name, &block)
+ # Define a new valid value for a state. You must provide the value itself,
+ # usually as a symbol, or a regex to match the value.
+ #
+ # The first argument to the method is either the value itself or a regex.
+ # The second argument is an option hash; valid options are:
+ # * <tt>:event</tt>: The event that should be returned when this value is set.
+ def self.newvalue(name, options = {}, &block)
name = name.intern if name.is_a? String
+ @parameteroptions[name] = {}
+ paramopts = @parameteroptions[name]
+
+ # Symbolize everything
+ options.each do |opt, val|
+ paramopts[symbolize(opt)] = symbolize(val)
+ end
+
case name
when Symbol
if @parametervalues.include?(name)
@@ -55,8 +77,11 @@ class State < Puppet::Parameter
end
@parametervalues[name] = block
- define_method("set_" + name.to_s, &block)
+ method = "set_" + name.to_s
+ settor = paramopts[:settor] || (self.name.to_s + "=")
+ define_method(method, &block)
when Regexp
+ # The regexes are handled in parameter.rb
@parameterregexes[name] = block
else
raise ArgumentError, "Invalid value %s of type %s" %
@@ -64,58 +89,24 @@ class State < Puppet::Parameter
end
end
- # Call the method associated with a given value.
- def set
- if self.insync?
- self.log "already in sync"
- return nil
- end
-
- value = self.should
- method = "set_" + value.to_s
- event = nil
- if self.respond_to?(method)
- self.debug "setting %s (currently %s)" % [value, self.is]
-
- begin
- event = self.send(method)
- rescue Puppet::Error
- raise
- rescue => detail
- if Puppet[:debug]
- puts detail.backtrace
- end
- self.fail "Could not set %s on %s: %s" %
- [value, self.class.name, detail]
- end
- elsif ary = self.class.match?(value)
- # FIXME It'd be better here to define a method, so that
- # the blocks could return values.
- event = self.instance_eval(&ary[1])
- else
- self.fail "%s is not a valid value for %s" %
- [value, self.class.name]
- end
-
- if event and event.is_a?(Symbol)
- if event == :nochange
- return nil
- else
- return event
- end
- else
- # Return the appropriate event.
- event = case self.should
- when :present: (@parent.class.name.to_s + "_created").intern
- when :absent: (@parent.class.name.to_s + "_removed").intern
+ # How should a state change be printed as a string?
+ def change_to_s
+ begin
+ if @is == :absent
+ return "defined '%s' as '%s'" %
+ [self.name, self.should_to_s]
+ elsif self.should == :absent or self.should == [:absent]
+ return "undefined %s from '%s'" %
+ [self.name, self.is_to_s]
else
- (@parent.class.name.to_s + "_changed").intern
+ return "%s changed '%s' to '%s'" %
+ [self.name, self.is_to_s, self.should_to_s]
end
-
- #self.log "made event %s because 'should' is %s, 'is' is %s" %
- # [event, self.should.inspect, self.is.inspect]
-
- return event
+ rescue Puppet::Error, Puppet::DevError
+ raise
+ rescue => detail
+ raise Puppet::DevError, "Could not convert change %s to string: %s" %
+ [self.name, detail]
end
end
@@ -186,9 +177,17 @@ class State < Puppet::Parameter
return false
end
+ # because the @should and @is vars might be in weird formats,
+ # we need to set up a mechanism for pretty printing of the values
+ # default to just the values, but this way individual states can
+ # override these methods
+ def is_to_s
+ @is
+ end
+
+ # Send a log message.
def log(msg)
unless @parent[:loglevel]
- p @parent
self.devfail "Parent %s has no loglevel" %
@parent.name
end
@@ -227,6 +226,82 @@ class State < Puppet::Parameter
end
end
+ # Retrieve the parent's provider. Some types don't have providers, in which
+ # case we return the parent object itself.
+ def provider
+ @parent.provider || @parent
+ end
+
+ # By default, call the method associated with the state name on our
+ # provider. In other words, if the state name is 'gid', we'll call
+ # 'provider.gid' to retrieve the current value.
+ def retrieve
+ @is = provider.send(self.class.name)
+ end
+
+ # Call the method associated with a given value.
+ def set
+ if self.insync?
+ self.log "already in sync"
+ return nil
+ end
+
+ value = self.should
+ method = "set_" + value.to_s
+ event = nil
+ if self.respond_to?(method)
+ self.debug "setting %s (currently %s)" % [value, self.is]
+
+ begin
+ event = self.send(method)
+ rescue Puppet::Error
+ raise
+ rescue => detail
+ if Puppet[:debug]
+ puts detail.backtrace
+ end
+ self.fail "Could not set %s on %s: %s" %
+ [value, self.class.name, detail]
+ end
+ elsif ary = self.class.match?(value)
+ # FIXME It'd be better here to define a method, so that
+ # the blocks could return values.
+ event = self.instance_eval(&ary[1])
+ else
+ if provider.respond_to?(self.class.name.to_s + "=")
+ provider.send(self.class.name.to_s + "=", self.should)
+ else
+ self.fail "%s is not a valid value for %s" %
+ [value, self.class.name]
+ end
+ end
+
+ if setevent = self.class.event(value)
+ return setevent
+ else
+ if event and event.is_a?(Symbol)
+ if event == :nochange
+ return nil
+ else
+ return event
+ end
+ else
+ # Return the appropriate event.
+ event = case self.should
+ when :present: (@parent.class.name.to_s + "_created").intern
+ when :absent: (@parent.class.name.to_s + "_removed").intern
+ else
+ (@parent.class.name.to_s + "_changed").intern
+ end
+
+ #self.log "made event %s because 'should' is %s, 'is' is %s" %
+ # [event, self.should.inspect, self.is.inspect]
+
+ return event
+ end
+ end
+ end
+
# Only return the first value
def should
if defined? @should
@@ -262,6 +337,14 @@ class State < Puppet::Parameter
end
end
+ def should_to_s
+ if defined? @should
+ @should.join(" ")
+ else
+ return nil
+ end
+ end
+
# The default 'sync' method only selects among a list of registered
# values.
def sync
@@ -280,43 +363,6 @@ class State < Puppet::Parameter
self.set
end
- # How should a state change be printed as a string?
- def change_to_s
- begin
- if @is == :absent
- return "defined '%s' as '%s'" %
- [self.name, self.should_to_s]
- elsif self.should == :absent or self.should == [:absent]
- return "undefined %s from '%s'" %
- [self.name, self.is_to_s]
- else
- return "%s changed '%s' to '%s'" %
- [self.name, self.is_to_s, self.should_to_s]
- end
- rescue Puppet::Error, Puppet::DevError
- raise
- rescue => detail
- raise Puppet::DevError, "Could not convert change %s to string: %s" %
- [self.name, detail]
- end
- end
-
- # because the @should and @is vars might be in weird formats,
- # we need to set up a mechanism for pretty printing of the values
- # default to just the values, but this way individual states can
- # override these methods
- def is_to_s
- @is
- end
-
- def should_to_s
- if defined? @should
- @should.join(" ")
- else
- return nil
- end
- end
-
def to_s
return "%s(%s)" % [@parent.name,self.name]
end
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index 4aec9efc3..3a78d9669 100755
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -4,47 +4,35 @@ require 'puppet/type/state'
require 'puppet/type/nameservice'
module Puppet
- newtype(:user, Puppet::Type::NSSType) do
- case Facter["operatingsystem"].value
- when "Darwin":
- @parentstate = Puppet::NameService::NetInfo::NetInfoState
- @parentmodule = Puppet::NameService::NetInfo
- when "FreeBSD":
- @parentstate = Puppet::NameService::PW::PWUser
- @parentmodule = Puppet::NameService::PW
- else
- @parentstate = Puppet::NameService::ObjectAdd::ObjectAddUser
- @parentmodule = Puppet::NameService::ObjectAdd
- end
-
- newstate(:ensure, @parentstate) do
- newvalue(:present) do
+ newtype(:user) do
+ newstate(:ensure) do
+ newvalue(:present, :event => :user_created) do
# Verify that they have provided everything necessary, if we
# are trying to manage the user
- if @parent.managed?
- @parent.class.states.each { |state|
- next if stateobj = @parent.state(state.name)
- next if state.name == :ensure
-
- unless state.autogen? or state.isoptional?
- if state.method_defined?(:autogen)
- @parent[state.name] = :auto
- else
- @parent.fail "Users require a value for %s" %
- state.name
- end
- end
- }
-
- #if @states.empty?
- # @parent[:comment] = @parent[:name]
- #end
- end
- self.syncname(:present)
+# if @parent.managed?
+# @parent.class.states.each { |state|
+# next if stateobj = @parent.state(state.name)
+# next if state.name == :ensure
+#
+# unless state.autogen? or state.isoptional?
+# if state.method_defined?(:autogen)
+# @parent[state.name] = :auto
+# else
+# @parent.fail "Users require a value for %s" %
+# state.name
+# end
+# end
+# }
+#
+# #if @states.empty?
+# # @parent[:comment] = @parent[:name]
+# #end
+# end
+ provider.create
end
- newvalue(:absent) do
- self.syncname(:absent)
+ newvalue(:absent, :event => :user_removed) do
+ provider.delete
end
desc "The basic state that the object should be in."
@@ -80,12 +68,13 @@ module Puppet
end
def retrieve
- if @parent.exists?
+ if provider.exists?
@is = :present
else
@is = :absent
end
end
+
# The default 'sync' method only selects among a list of registered
# values.
def sync
@@ -106,28 +95,13 @@ module Puppet
end
- newstate(:uid, @parentstate) do
+ newstate(:uid) do
desc "The user ID. Must be specified numerically. For new users
being created, if no user ID is specified then one will be
chosen automatically, which will likely result in the same user
having different IDs on different systems, which is not
recommended."
- isautogen
-
- def autogen
- highest = 0
- Etc.passwd { |user|
- if user.uid > highest
- unless user.uid > 65000
- highest = user.uid
- end
- end
- }
-
- return highest + 1
- end
-
munge do |value|
case value
when String
@@ -148,12 +122,10 @@ module Puppet
end
end
- newstate(:gid, @parentstate) do
+ newstate(:gid) do
desc "The user's primary group. Can be specified numerically or
by name."
- isautogen
-
munge do |gid|
method = :getgrgid
case gid
@@ -197,37 +169,27 @@ module Puppet
end
- newstate(:comment, @parentstate) do
+ newstate(:comment) do
desc "A description of the user. Generally is a user's full name."
-
- isoptional
-
- @posixmethod = :gecos
end
- newstate(:home, @parentstate) do
+ newstate(:home) do
desc "The home directory of the user. The directory must be created
separately and is not currently checked for existence."
-
- isautogen
- @posixmethod = :dir
end
- newstate(:shell, @parentstate) do
+ newstate(:shell) do
desc "The user's login shell. The shell must exist and be
executable."
- isautogen
end
- newstate(:groups, @parentstate) do
+ newstate(:groups) do
desc "The groups of which the user is a member. The primary
group should not be listed. Multiple groups should be
specified as an array."
- isoptional
-
def should_to_s
- self.should
+ self.should.join(",")
end
def is_to_s
@@ -244,18 +206,22 @@ module Puppet
@should ||= []
if @parent[:membership] == :inclusive
- @should.sort.join(",")
+ @should.sort
else
members = @should
if @is.is_a?(Array)
members += @is
end
- members.uniq.sort.join(",")
+ members.uniq.sort
end
end
def retrieve
- @is = grouplist()
+ if tmp = provider.groups
+ @is = tmp.split(",")
+ else
+ @is = :absent
+ end
end
def insync?
@@ -278,14 +244,8 @@ module Puppet
end
def sync
- if respond_to? :setgrouplist
- # Pass them the group list, so that the :membership logic
- # is all in this class, not in parent classes.
- setgrouplist(self.should)
- return :user_modified
- else
- super
- end
+ provider.groups = self.should.join(",")
+ :user_changed
end
end
@@ -293,25 +253,23 @@ module Puppet
# so i'm disabling them for now
# FIXME Puppet::State::UserLocked is currently non-functional
- #newstate(:locked, @parentstate) do
+ #newstate(:locked) do
# desc "The expected return code. An error will be returned if the
# executed command returns something else."
#end
# FIXME Puppet::State::UserExpire is currently non-functional
- #newstate(:expire, @parentstate) do
+ #newstate(:expire) do
# desc "The expected return code. An error will be returned if the
# executed command returns something else."
# @objectaddflag = "-e"
- # isautogen
#end
# FIXME Puppet::State::UserInactive is currently non-functional
- #newstate(:inactive, @parentstate) do
+ #newstate(:inactive) do
# desc "The expected return code. An error will be returned if the
# executed command returns something else."
# @objectaddflag = "-f"
- # isautogen
#end
newparam(:name) do
@@ -353,8 +311,6 @@ module Puppet
for Mac OS X, NetInfo is used. This is currently unconfigurable,
but if you desperately need it to be so, please contact us."
- @netinfodir = "users"
-
# Autorequire the group, if it's around
autorequire(:group) do
#return nil unless @states.include?(:gid)
@@ -384,7 +340,7 @@ module Puppet
}
end
- if @states.include?(:groups) and groups = @states[:groups].should.split(",")
+ if @states.include?(:groups) and groups = @states[:groups].should
autos += groups
end
@@ -400,55 +356,45 @@ module Puppet
end
end
- # List all found users
- def self.listbyname
+ def self.list_by_name
users = []
- Etc.setpwent
- while ent = Etc.getpwent
- users << ent.name
+ defaultprovider.listbyname do |user|
+ users << user
end
- Etc.endpwent
-
return users
end
- def exists?
- self.class.parentmodule.exists?(self)
- end
-
- def getinfo(refresh = false)
- if @userinfo.nil? or refresh == true
- begin
- @userinfo = Etc.getpwnam(self[:name])
- rescue ArgumentError => detail
- @userinfo = nil
- end
- end
-
- @userinfo
- end
-
- def initialize(hash)
- @userinfo = nil
- super
+ def self.list
+ defaultprovider.list
- unless defined? @states
- raise "wtf?"
+ self.collect do |user|
+ user
end
end
def retrieve
- info = self.getinfo(true)
-
- if info.nil?
- # the user does not exist
- @states.each { |name, state|
+ absent = false
+ states().each { |state|
+ if absent
state.is = :absent
- }
- return
- else
- super
- end
+ else
+ state.retrieve
+ end
+
+ if state.name == :ensure and state.is == :absent
+ absent = true
+ next
+ end
+ }
+ #if provider.exists?
+ # super
+ #else
+ # # the user does not exist
+ # @states.each { |name, state|
+ # state.is = :absent
+ # }
+ # return
+ #end
end
end
end
diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb
index bb07b93b3..92f83b384 100644
--- a/lib/puppet/type/yumrepo.rb
+++ b/lib/puppet/type/yumrepo.rb
@@ -209,7 +209,7 @@ module Puppet
isnamevar
end
- newstate(:descr, Puppet::IniState) do
+ newstate(:descr, :parent => Puppet::IniState) do
desc "A human readable description of the repository.
#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
@@ -217,7 +217,7 @@ module Puppet
inikey "name"
end
- newstate(:mirrorlist, Puppet::IniState) do
+ newstate(:mirrorlist, :parent => Puppet::IniState) do
desc "The URL that holds the list of mirrors for this repository.
#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
@@ -225,21 +225,21 @@ module Puppet
newvalue(/.*/) { }
end
- newstate(:baseurl, Puppet::IniState) do
+ newstate(:baseurl, :parent => Puppet::IniState) do
desc "The URL for this repository.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
# Should really check that it's a valid URL
newvalue(/.*/) { }
end
- newstate(:enabled, Puppet::IniState) do
+ newstate(:enabled, :parent => Puppet::IniState) do
desc "Whether this repository is enabled or disabled. Possible
values are '0', and '1'.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(%r{(0|1)}) { }
end
- newstate(:gpgcheck, Puppet::IniState) do
+ newstate(:gpgcheck, :parent => Puppet::IniState) do
desc "Whether to check the GPG signature on packages installed
from this repository. Possible values are '0', and '1'.
\n#{ABSENT_DOC}"
@@ -247,7 +247,7 @@ module Puppet
newvalue(%r{(0|1)}) { }
end
- newstate(:gpgkey, Puppet::IniState) do
+ newstate(:gpgkey, :parent => Puppet::IniState) do
desc "The URL for the GPG key with which packages from this
repository are signed.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
@@ -255,14 +255,14 @@ module Puppet
newvalue(/.*/) { }
end
- newstate(:include, Puppet::IniState) do
+ newstate(:include, :parent => Puppet::IniState) do
desc "A URL from which to include the config.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
# Should really check that it's a valid URL
newvalue(/.*/) { }
end
- newstate(:exclude, Puppet::IniState) do
+ newstate(:exclude, :parent => Puppet::IniState) do
desc "List of shell globs. Matching packages will never be
considered in updates or installs for this repo.
#{ABSENT_DOC}"
@@ -270,7 +270,7 @@ module Puppet
newvalue(/.*/) { }
end
- newstate(:includepkgs, Puppet::IniState) do
+ newstate(:includepkgs, :parent => Puppet::IniState) do
desc "List of shell globs. If this is set, only packages
matching one of the globs will be considered for
update or install.\n#{ABSENT_DOC}"
@@ -278,7 +278,7 @@ module Puppet
newvalue(/.*/) { }
end
- newstate(:enablegroups, Puppet::IniState) do
+ newstate(:enablegroups, :parent => Puppet::IniState) do
desc "Determines whether yum will allow the use of
package groups for this repository. Possible
values are '0', and '1'.\n#{ABSENT_DOC}"
@@ -286,27 +286,27 @@ module Puppet
newvalue(%r{(0|1)}) { }
end
- newstate(:failovermethod, Puppet::IniState) do
+ newstate(:failovermethod, :parent => Puppet::IniState) do
desc "Either 'roundrobin' or 'priority'.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(%r(roundrobin|priority)) { }
end
- newstate(:keepalive, Puppet::IniState) do
+ newstate(:keepalive, :parent => Puppet::IniState) do
desc "Either '1' or '0'. This tells yum whether or not HTTP/1.1
keepalive should be used with this repository.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(%r{(0|1)}) { }
end
- newstate(:timeout, Puppet::IniState) do
+ newstate(:timeout, :parent => Puppet::IniState) do
desc "Number of seconds to wait for a connection before timing
out.\n#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(%r{[0-9]+}) { }
end
- newstate(:metadata_expire, Puppet::IniState) do
+ newstate(:metadata_expire, :parent => Puppet::IniState) do
desc "Number of seconds after which the metadata will expire.
#{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
diff --git a/lib/puppet/type/zone.rb b/lib/puppet/type/zone.rb
index 4fb6fe174..82656e068 100644
--- a/lib/puppet/type/zone.rb
+++ b/lib/puppet/type/zone.rb
@@ -178,7 +178,7 @@ Puppet::Type.newtype(:zone) do
and cannot be changed."
end
- newstate(:ip, ZoneMultiConfigState) do
+ newstate(:ip, :parent => ZoneMultiConfigState) do
require 'ipaddr'
desc "The IP address of the zone. IP addresses must be specified
@@ -225,7 +225,7 @@ end
end
end
- newstate(:autoboot, ZoneConfigState) do
+ newstate(:autoboot, :parent => ZoneConfigState) do
desc "Whether the zone should automatically boot."
defaultto true
@@ -238,7 +238,7 @@ end
end
end
- newstate(:pool, ZoneConfigState) do
+ newstate(:pool, :parent => ZoneConfigState) do
desc "The resource pool for this zone."
def configtext
@@ -246,7 +246,7 @@ end
end
end
- newstate(:inherit, ZoneMultiConfigState) do
+ newstate(:inherit, :parent => ZoneMultiConfigState) do
desc "The list of directories that the zone inherits from the global
zone. All directories must be fully qualified."
diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb
index 03ef93d21..5086e6cfc 100644
--- a/lib/puppet/util.rb
+++ b/lib/puppet/util.rb
@@ -31,8 +31,8 @@ module Util
# If they're running as a normal user, then just execute as that same
# user.
unless Process.uid == 0
- yield
- return
+ retval = yield
+ return retval
end
begin
@@ -82,7 +82,6 @@ module Util
Puppet.warning "Could not retrieve UID for %s" % user
end
end
-
retval = yield
ensure
if olduid
@@ -278,6 +277,29 @@ module Util
}
end
+ # Proxy a bunch of methods to another object.
+ def self.classproxy(klass, objmethod, *methods)
+ classobj = class << klass; self; end
+ methods.each do |method|
+ classobj.send(:define_method, method) do |*args|
+ obj = self.send(objmethod)
+
+ obj.send(method, *args)
+ end
+ end
+ end
+
+ # Proxy a bunch of methods to another object.
+ def self.proxy(klass, objmethod, *methods)
+ methods.each do |method|
+ klass.send(:define_method, method) do |*args|
+ obj = self.send(objmethod)
+
+ obj.send(method, *args)
+ end
+ end
+ end
+
# XXX this should all be done using puppet objects, not using
# normal mkdir
def self.recmkdir(dir,mode = 0755)
@@ -301,15 +323,6 @@ module Util
end
end
- def self.symbolize(value)
- case value
- when String: value = value.intern
- when Symbol: value
- else
- raise ArgumentError, "'%s' must be a string or symbol" % value
- end
- end
-
# Execute a given chunk of code with a new umask.
def self.withumask(mask)
cur = File.umask(mask)
@@ -354,6 +367,54 @@ module Util
end
end
+ def binary(bin)
+ if bin =~ /^\//
+ if FileTest.exists? bin
+ return true
+ else
+ return nil
+ end
+ else
+ ENV['PATH'].split(":").each do |dir|
+ if FileTest.exists? File.join(dir, bin)
+ return File.join(dir, bin)
+ end
+ end
+ return nil
+ end
+ end
+ module_function :binary
+
+ # Execute the provided command in a pipe, yielding the pipe object.
+ def execpipe(command, failonfail = true)
+ if respond_to? :debug
+ debug "Executing '%s'" % command
+ else
+ Puppet.debug "Executing '%s'" % command
+ end
+
+ output = open("| #{command} 2>&1") do |pipe|
+ yield pipe
+ end
+
+ if failonfail
+ unless $? == 0
+ raise ExecutionFailure, output
+ end
+ end
+
+ return output
+ end
+
+ def execfail(command, exception)
+ begin
+ output = execute(command)
+ return output
+ rescue ExecutionFailure
+ raise exception, output
+ end
+ end
+
# Execute the desired command, and return the status and output.
def execute(command, failonfail = true)
if respond_to? :debug
@@ -372,6 +433,8 @@ module Util
return output
end
+ module_function :execute
+
# Create an exclusive lock.
def threadlock(resource, type = Sync::EX)
Puppet::Util.sync(resource).synchronize(type) do
@@ -400,6 +463,16 @@ module Util
end
end
+ def symbolize(value)
+ case value
+ when String: value = value.intern
+ when Symbol: value
+ else
+ raise ArgumentError, "'%s' must be a string or symbol" % value
+ end
+ end
+ module_function :symbolize
+
# Just benchmark, with no logging.
def thinmark
seconds = Benchmark.realtime {
diff --git a/test/other/autoload.rb b/test/other/autoload.rb
new file mode 100644
index 000000000..95fafbda2
--- /dev/null
+++ b/test/other/autoload.rb
@@ -0,0 +1,111 @@
+#!/usr/bin/env ruby
+
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/autoload'
+require 'puppettest'
+require 'test/unit'
+
+class TestAutoload < Test::Unit::TestCase
+ include TestPuppet
+ @things = []
+ def self.newthing(name)
+ @things << name
+ end
+
+ def self.thing?(name)
+ @things.include? name
+ end
+
+ def self.clear
+ @things.clear
+ end
+
+ def mkfile(name, path)
+ # Now create a file to load
+ File.open(path, "w") do |f|
+ f.puts %{
+TestAutoload.newthing(:#{name.to_s})
+ }
+ end
+ end
+
+ def teardown
+ super
+ self.class.clear
+ end
+
+ def test_load
+ dir = tempfile()
+ $: << dir
+ cleanup do
+ $:.delete(dir)
+ end
+
+ Dir.mkdir(dir)
+
+ rbdir = File.join(dir, "yayness")
+
+ Dir.mkdir(rbdir)
+
+ # An object for specifying autoload
+ klass = self.class
+
+ loader = nil
+ assert_nothing_raised {
+ loader = Puppet::Autoload.new(klass, :yayness)
+ }
+
+ assert_equal(loader.object_id, Puppet::Autoload[klass].object_id,
+ "Did not retrieve loader object by class")
+
+ # Make sure we don't fail on missing files
+ assert_nothing_raised {
+ assert_equal(false, loader.load(:mything),
+ "got incorrect return on failed load")
+ }
+
+ # Now create a couple of files for testing
+ path = File.join(rbdir, "mything.rb")
+ mkfile(:mything, path)
+ opath = File.join(rbdir, "othing.rb")
+ mkfile(:othing, opath)
+
+ # Now try to actually load it.
+ assert_nothing_raised {
+ assert_equal(true, loader.load(:mything),
+ "got incorrect return on failed load")
+ }
+
+ assert(loader.loaded?(:mything), "Not considered loaded")
+
+ assert(klass.thing?(:mything),
+ "Did not get loaded thing")
+
+ # Now clear everything, and test loadall
+ assert_nothing_raised {
+ loader.clear
+ }
+
+ self.class.clear
+
+ assert_nothing_raised {
+ loader.loadall
+ }
+
+ [:mything, :othing].each do |thing|
+ assert(loader.loaded?(thing), "#{thing.to_s} not considered loaded")
+
+ assert(klass.thing?(thing),
+ "Did not get loaded #{thing.to_s}")
+ end
+ end
+
+ def test_loadall
+ end
+end
diff --git a/test/other/provider.rb b/test/other/provider.rb
new file mode 100644
index 000000000..fc04b9926
--- /dev/null
+++ b/test/other/provider.rb
@@ -0,0 +1,165 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../.."
+end
+
+require 'puppet'
+require 'puppet/provider'
+require 'puppettest'
+require 'test/unit'
+
+class TestImpl < Test::Unit::TestCase
+ include TestPuppet
+
+ def setup
+ super
+ @type = newtype(@method_name.to_s + "type")
+
+ # But create a new provider for every method.
+ @provider = newprovider(@method_name.to_s + "provider")
+ end
+
+ def newtype(name)
+ # First create a fake type
+ return Puppet::Type.newtype(name) {
+ newparam(:name) { isnamevar }
+ }
+ end
+
+ def newprovider(name, type = nil)
+ type ||= @type
+ provider = nil
+ assert_nothing_raised("Could not create provider") do
+ provider = type.provide(name) {}
+ end
+ return provider
+ end
+
+ # Just a quick run-through to see if the basics work
+ def test_newprovider
+ assert_nothing_raised do
+ @provider.confine :operatingsystem => Facter["operatingsystem"].value
+ @provider.defaultfor :operatingsystem => Facter["operatingsystem"].value
+ end
+
+ assert(@provider.suitable?, "Implementation was not considered suitable")
+ assert(@provider.default?, "Implementation was not considered a default")
+
+ assert_equal(@provider, @type.defaultprovider,
+ "Did not correctly find default provider")
+
+ end
+
+ def test_provider_false_confine
+ assert_nothing_raised do
+ @provider.confine :false => false
+ end
+
+ assert(@provider.suitable?, "False did not check correctly")
+ end
+
+ def test_provider_true_confine
+ assert_nothing_raised do
+ @provider.confine :true => true
+ end
+
+ assert(@provider.suitable?, "True did not check correctly")
+
+ # Now check whether we multiple true things work
+ assert_nothing_raised do
+ @provider.confine :true => false
+ @provider.confine :true => true
+ end
+ assert(! @provider.suitable?, "One truth overrode another")
+ end
+
+ def test_provider_exists_confine
+ file = tempfile()
+
+ assert_nothing_raised do
+ @provider.confine :exists => file
+ end
+
+ assert(! @provider.suitable?, "Exists did not check correctly")
+ File.open(file, "w") { |f| f.puts "" }
+ assert(@provider.suitable?, "Exists did not find file correctly")
+ end
+
+ def test_provider_facts_confine
+ # Now check for multiple platforms
+ assert_nothing_raised do
+ @provider.confine :operatingsystem => [Facter["operatingsystem"].value, :yayos]
+ @provider.confine :operatingsystem => [:fakeos, :otheros]
+ end
+
+ assert(@provider.suitable?, "Implementation not considered suitable")
+ end
+
+ def test_provider_default
+ nondef = nil
+ assert_nothing_raised {
+ nondef = newprovider(:nondefault)
+ }
+
+ assert_nothing_raised do
+ @provider.defaultfor :operatingsystem => Facter["operatingsystem"].value
+ end
+
+ assert_equal(@provider.name, @type.defaultprovider.name, "Did not get right provider")
+
+ @type.suitableprovider
+ end
+
+ def test_subclassconfines
+ parent = newprovider("parentprovider")
+
+ # Now make a bad confine on the parent
+ parent.confine :exists => "/this/file/definitely/does/not/exist"
+
+ child = nil
+ assert_nothing_raised {
+ child = @type.provide("child", :parent => parent.name) {}
+ }
+
+ assert(child.suitable?, "Parent ruled out child")
+ end
+
+ def test_commands
+ parent = newprovider("parentprovider")
+
+ child = nil
+ assert_nothing_raised {
+ child = @type.provide("child", :parent => parent.name) {}
+ }
+
+ assert_nothing_raised {
+ child.commands :which => "which"
+ }
+
+ assert(child.command(:which), "Did not find 'which' command")
+
+ assert(child.command(:which) =~ /^\//,
+ "Command did not become fully qualified")
+ assert(FileTest.exists?(child.command(:which)),
+ "Did not find actual 'which' binary")
+
+ assert_raise(Puppet::DevError) do
+ child.command(:nosuchcommand)
+ end
+
+ # Now create a parent command
+ assert_nothing_raised {
+ parent.commands :sh => Puppet::Util.binary('sh')
+ }
+
+ assert(parent.command(:sh), "Did not find 'sh' command")
+
+ assert(child.command(:sh), "Did not find parent's 'sh' command")
+
+ assert(FileTest.exists?(child.command(:sh)),
+ "Somehow broke path to sh")
+ end
+end
+
+# $Id$
diff --git a/test/providers/group.rb b/test/providers/group.rb
new file mode 100755
index 000000000..364d1750d
--- /dev/null
+++ b/test/providers/group.rb
@@ -0,0 +1,246 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../.."
+end
+
+require 'etc'
+require 'puppet/type'
+require 'puppettest'
+require 'test/unit'
+
+class TestGroupProvider < Test::Unit::TestCase
+ include TestPuppet
+ def setup
+ super
+ @@tmpgroups = []
+ @provider = nil
+ assert_nothing_raised {
+ @provider = Puppet::Type.type(:group).defaultprovider
+ }
+
+ assert(@provider, "Could not find default group provider")
+ assert(@provider.name != :fake, "Got a fake provider")
+ end
+
+ def teardown
+ super
+ Puppet.type(:group).clear
+ @@tmpgroups.each { |group|
+ unless missing?(group)
+ remove(group)
+ end
+ }
+ end
+
+ def mkgroup(name, hash = {})
+ fakemodel = fakemodel(:group, name)
+ group = nil
+ assert_nothing_raised {
+ group = @provider.new(fakemodel)
+ }
+ hash.each do |name, val|
+ fakemodel[name] = val
+ end
+ assert(group, "Could not create provider group")
+
+ return group
+ end
+
+ case Facter["operatingsystem"].value
+ when "Darwin":
+ def missing?(group)
+ output = %x{nidump -r /groups/#{group} / 2>/dev/null}.chomp
+
+ if output == ""
+ return true
+ else
+ return false
+ end
+
+ assert_equal("", output, "Group %s is present:\n%s" % [group, output])
+ end
+
+ def gid(name)
+ %x{nireport / /groups name gid}.split("\n").each { |line|
+ group, id = line.chomp.split(/\s+/)
+ assert(id =~ /^-?\d+$/, "Group id %s for %s is not a number" %
+ [id, group])
+ if group == name
+ return Integer(id)
+ end
+ }
+
+ return nil
+ end
+
+ def remove(group)
+ system("niutil -destroy / /groups/%s" % group)
+ end
+ else
+ def missing?(group)
+ begin
+ obj = Etc.getgrnam(group)
+ return false
+ rescue ArgumentError
+ return true
+ end
+ end
+
+ def gid(name)
+ assert_nothing_raised {
+ obj = Etc.getgrnam(name)
+ return obj.gid
+ }
+
+ return nil
+ end
+
+ def remove(group)
+ system("groupdel %s" % group)
+ end
+ end
+
+ def groupnames
+ %x{groups}.chomp.split(/ /)
+ end
+
+ def groupids
+ Process.groups
+ end
+
+ def attrtest_ensure(group)
+ old = group.ensure
+ assert_nothing_raised {
+ group.ensure = :absent
+ }
+
+ assert(!group.exists?, "Group was not deleted")
+
+ assert_nothing_raised {
+ group.ensure = :present
+ }
+ assert(group.exists?, "Group was not created")
+
+ unless old == :present
+ assert_nothing_raised {
+ group.ensure = old
+ }
+ end
+ end
+
+ def attrtest_gid(group)
+ old = gid(group.name)
+
+ newgid = old
+ while true
+ newgid += 1
+
+ if newgid - old > 1000
+ $stderr.puts "Could not find extra test UID"
+ return
+ end
+ begin
+ Etc.getgrgid(newgid)
+ rescue ArgumentError => detail
+ break
+ end
+ end
+
+ assert_nothing_raised("Failed to change group id") {
+ group.gid = newgid
+ }
+
+ curgid = nil
+ assert_nothing_raised {
+ curgid = gid(group.name)
+ }
+
+ assert_equal(newgid, curgid, "GID was not changed")
+ # Refresh
+ group.getinfo(true)
+ assert_equal(newgid, group.gid, "Object got wrong gid")
+
+ assert_nothing_raised("Failed to change group id") {
+ group.gid = old
+ }
+ end
+
+ # Iterate over each of our groups and try to grab the gid.
+ def test_ownprovidergroups
+ groupnames().each { |group|
+ gobj = nil
+ comp = nil
+ fakemodel = fakemodel(:group, group)
+ assert_nothing_raised {
+ gobj = @provider.new(fakemodel)
+ }
+
+ assert(gobj.gid, "Failed to retrieve gid")
+ }
+ end
+
+ if Process.uid == 0
+ def test_mkgroup
+ gobj = nil
+ comp = nil
+ name = "pptestgr"
+ assert(missing?(name), "Group %s is still present" % name)
+ group = mkgroup(name)
+
+ @@tmpgroups << name
+
+ assert(group.respond_to?(:addcmd), "no respondo?")
+ assert_nothing_raised {
+ group.create
+ }
+ assert(!missing?(name), "Group %s is missing" % name)
+
+ tests = Puppet.type(:group).validstates
+
+ tests.each { |test|
+ if self.respond_to?("attrtest_%s" % test)
+ self.send("attrtest_%s" % test, group)
+ else
+ $stderr.puts "Not testing attr %s of group" % test
+ end
+ }
+
+ assert_nothing_raised {
+ group.delete
+ }
+ end
+
+ # groupadd -o is broken in FreeBSD.
+ unless Facter["operatingsystem"].value == "FreeBSD"
+ def test_duplicateIDs
+ group1 = mkgroup("group1", :gid => 125)
+ group2 = mkgroup("group2", :gid => 125)
+
+ @@tmpgroups << "group1"
+ @@tmpgroups << "group2"
+ # Create the first group
+ assert_nothing_raised {
+ group1.create
+ }
+
+ # Not all OSes fail here, so we can't test that it doesn't work with
+ # it off, only that it does work with it on.
+ assert_nothing_raised {
+ group2.model[:allowdupe] = :true
+ }
+
+ # Now create the second group
+ assert_nothing_raised {
+ group2.create
+ }
+ assert_equal(:present, group2.ensure,
+ "Group did not get created")
+ end
+ end
+ else
+ $stderr.puts "Not running as root; skipping group creation tests."
+ end
+end
+
+# $Id$
diff --git a/test/providers/nameservice.rb b/test/providers/nameservice.rb
new file mode 100644
index 000000000..0e0f8050f
--- /dev/null
+++ b/test/providers/nameservice.rb
@@ -0,0 +1,36 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../.."
+end
+
+require 'puppettest'
+require 'puppet'
+require 'test/unit'
+require 'facter'
+
+class TestNameServiceProvider < Test::Unit::TestCase
+ include FileTesting
+
+ def test_option
+ klass = Class.new(Puppet::Type::Provider::NameService)
+ klass.model = Puppet::Type.type(:user)
+
+ val = nil
+ assert_nothing_raised {
+ val = klass.option(:home, :flag)
+ }
+
+ assert_nil(val, "Got an option")
+
+ assert_nothing_raised {
+ klass.options :home, :flag => "-d"
+ }
+ assert_nothing_raised {
+ val = klass.option(:home, :flag)
+ }
+ assert_equal("-d", val, "Got incorrect option")
+ end
+end
+
+# $Id$
diff --git a/test/providers/package.rb b/test/providers/package.rb
new file mode 100644
index 000000000..210fae5c1
--- /dev/null
+++ b/test/providers/package.rb
@@ -0,0 +1,86 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../.."
+end
+
+require 'etc'
+require 'puppet/type'
+require 'puppettest'
+require 'test/unit'
+
+class TestPackageProvider < Test::Unit::TestCase
+ include TestPuppet
+ def setup
+ super
+ @provider = nil
+ assert_nothing_raised {
+ @provider = Puppet::Type.type(:package).defaultprovider
+ }
+
+ assert(@provider, "Could not find default package provider")
+ assert(@provider.name != :fake, "Got a fake provider")
+ end
+
+ def test_nothing
+ end
+
+ if Facter["operatingsystem"].value == "Solaris" and Process.uid == 0
+ if Puppet.type(:package).provider(:blastwave).suitable?
+ # FIXME The packaging crap needs to be rewritten to support testing
+ # multiple package types on the same platform.
+ def test_list_blastwave
+ pkgs = nil
+ assert_nothing_raised {
+ pkgs = Puppet::Type.type(:package).provider(:blastwave).list
+ }
+
+ pkgs.each do |pkg|
+ if pkg[:name] =~ /^CSW/
+ assert_equal(:blastwave, pkg[:provider],
+ "Type was not set correctly")
+ end
+ end
+ end
+
+ def test_install_blastwave
+ pkg = nil
+ name = "cabextract"
+ model = fakemodel(:package, name)
+ assert_nothing_raised {
+ pkg = Puppet::Type.type(:package).provider(:blastwave).new(model)
+ }
+
+ if hash = pkg.query and hash[:ensure] != :absent
+ p hash
+ $stderr.puts "Cannot test pkg installation; %s is already installed" %
+ name
+ return
+ end
+
+ assert_nothing_raised {
+ pkg.install
+ }
+
+ hash = nil
+ assert(hash = pkg.query,
+ "package did not install")
+ assert(hash[:ensure] != :absent,
+ "package did not install")
+
+ latest = nil
+ assert_nothing_raised {
+ latest = pkg.latest
+ }
+ assert(latest, "Could not find latest package version")
+ assert_nothing_raised {
+ pkg.uninstall
+ }
+ end
+ else
+ $stderr.puts "No pkg-get scripting; skipping blastwave tests"
+ end
+ end
+end
+
+# $Id$
diff --git a/test/providers/user.rb b/test/providers/user.rb
new file mode 100644
index 000000000..15b453780
--- /dev/null
+++ b/test/providers/user.rb
@@ -0,0 +1,528 @@
+if __FILE__ == $0
+ $:.unshift '..'
+ $:.unshift '../../lib'
+ $puppetbase = "../.."
+end
+
+require 'puppettest'
+require 'puppet'
+require 'test/unit'
+require 'facter'
+
+class TestUserProvider < Test::Unit::TestCase
+ include FileTesting
+
+ def setup
+ super
+ setme()
+ @@tmpusers = []
+ @provider = nil
+ assert_nothing_raised {
+ @provider = Puppet::Type.type(:user).defaultprovider
+ }
+
+ assert(@provider, "Could not find default user provider")
+
+ end
+
+ def teardown
+ @@tmpusers.each { |user|
+ unless missing?(user)
+ remove(user)
+ end
+ }
+ super
+ #Puppet.type(:user).clear
+ end
+
+ case Facter["operatingsystem"].value
+ when "Darwin":
+ def missing?(user)
+ output = %x{nidump -r /users/#{user} / 2>/dev/null}.chomp
+
+ if output == ""
+ return true
+ else
+ return false
+ end
+
+ assert_equal("", output, "User %s is present:\n%s" % [user, output])
+ end
+
+ def current?(param, user)
+ state = Puppet.type(:user).states.find { |st|
+ st.name == param
+ }
+
+ output = %x{nireport / /users name #{state.netinfokey}}
+ output.split("\n").each { |line|
+ if line =~ /^(\w+)\s+(.+)$/
+ username = $1
+ id = $2.sub(/\s+$/, '')
+ if username == user.name
+ if id =~ /^[-0-9]+$/
+ return Integer(id)
+ else
+ return id
+ end
+ end
+ else
+ raise "Could not match %s" % line
+ end
+ }
+
+ return nil
+ end
+
+ def remove(user)
+ system("niutil -destroy / /users/%s" % user)
+ end
+ else
+ def missing?(user)
+ begin
+ obj = Etc.getpwnam(user)
+ return false
+ rescue ArgumentError
+ return true
+ end
+ end
+
+ def current?(param, user)
+ state = Puppet.type(:user).states.find { |st|
+ st.name == param
+ }
+
+ assert_nothing_raised {
+ obj = Etc.getpwnam(user.name)
+ return obj.send(user.posixmethod(param))
+ }
+
+ return nil
+ end
+
+ def remove(user)
+ system("userdel %s" % user)
+ end
+ end
+
+
+ def eachstate
+ Puppet::Type.type(:user).validstates.each do |state|
+ next if state == :ensure
+ yield state
+ end
+ end
+
+ def findshell(old = nil)
+ %w{/bin/sh /bin/bash /sbin/sh /bin/ksh /bin/zsh /bin/csh /bin/tcsh
+ /usr/bin/sh /usr/bin/bash /usr/bin/ksh /usr/bin/zsh /usr/bin/csh
+ /usr/bin/tcsh}.find { |shell|
+ if old
+ FileTest.exists?(shell) and shell != old
+ else
+ FileTest.exists?(shell)
+ end
+ }
+ end
+
+ def fakedata(name, param)
+ case param
+ when :name: name
+ when :comment: "Puppet Testing User %s" % name
+ when :gid: Process.gid()
+ when :shell: findshell()
+ when :home: "/home/%s" % name
+ else
+ return nil
+ end
+ end
+
+ def mkuser(name)
+ fakemodel = fakemodel(:user, name)
+ user = nil
+ assert_nothing_raised {
+ user = @provider.new(fakemodel)
+ }
+ assert(user, "Could not create provider user")
+
+ return user
+ end
+
+ def test_list
+ names = nil
+ assert_nothing_raised {
+ names = @provider.listbyname
+ }
+
+ assert(names.length > 0, "Listed no users")
+
+ # Now try it by object
+ assert_nothing_raised {
+ names = @provider.list
+ }
+ assert(names.length > 0, "Listed no users as objects")
+
+ names.each do |obj|
+ assert_instance_of(Puppet::Type.type(:user), obj)
+ assert(obj[:provider], "Provider was not set")
+ end
+ end
+
+ def test_infocollection
+ fakemodel = fakemodel(:user, @me)
+
+ user = nil
+ assert_nothing_raised {
+ user = @provider.new(fakemodel)
+ }
+ assert(user, "Could not create user provider")
+
+ Puppet::Type.type(:user).validstates.each do |state|
+ next if state == :ensure
+ val = nil
+ assert_nothing_raised {
+ val = user.send(state)
+ }
+
+ assert(val != :absent,
+ "State %s is missing" % state)
+
+ assert(val, "Did not get value for %s" % state)
+ end
+ end
+
+ def test_exists
+ user = mkuser("nosuchuserok")
+
+ assert(! user.exists?,
+ "Fake user exists?")
+
+ user = mkuser(@me)
+ assert(user.exists?,
+ "I don't exist?")
+ end
+
+ def attrtest_ensure(user)
+ old = user.ensure
+ assert_nothing_raised {
+ user.ensure = :absent
+ }
+
+ assert(missing?(user.name), "User is still present")
+ assert_nothing_raised {
+ user.ensure = :present
+ }
+ assert(!missing?(user.name), "User is absent")
+ assert_nothing_raised {
+ user.ensure = :absent
+ }
+
+ unless old == :absent
+ user.ensure = old
+ end
+ end
+
+ def attrtest_comment(user)
+ old = user.comment
+
+ assert_nothing_raised {
+ user.comment = "A different comment"
+ }
+
+ assert_equal("A different comment", current?(:comment, user),
+ "Comment was not changed")
+
+ assert_nothing_raised {
+ user.comment = old
+ }
+
+ assert_equal(old, current?(:comment, user),
+ "Comment was not reverted")
+ end
+
+ def attrtest_home(user)
+ old = current?(:home, user)
+
+ assert_nothing_raised {
+ user.home = "/tmp"
+ }
+
+ assert_equal("/tmp", current?(:home, user), "Home was not changed")
+ assert_nothing_raised {
+ user.home = old
+ }
+
+ assert_equal(old, current?(:home, user), "Home was not reverted")
+ end
+
+ def attrtest_shell(user)
+ old = current?(:shell, user)
+
+ newshell = findshell(old)
+
+ unless newshell
+ $stderr.puts "Cannot find alternate shell; skipping shell test"
+ return
+ end
+
+ assert_nothing_raised {
+ user.shell = newshell
+ }
+
+ assert_equal(newshell, current?(:shell, user),
+ "Shell was not changed")
+
+ assert_nothing_raised {
+ user.shell = old
+ }
+
+ assert_equal(old, current?(:shell, user), "Shell was not reverted")
+ end
+
+ def attrtest_gid(user)
+ old = current?(:gid, user)
+
+ newgroup = %w{nogroup nobody staff users daemon}.find { |gid|
+ begin
+ group = Etc.getgrnam(gid)
+ rescue ArgumentError => detail
+ next
+ end
+ old != group.gid
+ }
+ group = Etc.getgrnam(newgroup)
+
+ unless newgroup
+ $stderr.puts "Cannot find alternate group; skipping gid test"
+ return
+ end
+
+ assert_raise(ArgumentError, "gid allowed a non-integer value") do
+ user.gid = group.name
+ end
+
+ assert_nothing_raised("Failed to specify group by id") {
+ user.gid = group.gid
+ }
+
+ assert_equal(group.gid, current?(:gid,user), "GID was not changed")
+
+ assert_nothing_raised("Failed to change back to old gid") {
+ user.gid = old
+ }
+ end
+
+ def attrtest_uid(user)
+ old = current?(:uid, user)
+
+ newuid = old
+ while true
+ newuid += 1
+
+ if newuid - old > 1000
+ $stderr.puts "Could not find extra test UID"
+ return
+ end
+ begin
+ newuser = Etc.getpwuid(newuid)
+ rescue ArgumentError => detail
+ break
+ end
+ end
+
+ assert_nothing_raised("Failed to change user id") {
+ user.uid = newuid
+ }
+
+ assert_equal(newuid, current?(:uid, user), "UID was not changed")
+
+ assert_nothing_raised("Failed to change user id") {
+ user.uid = old
+ }
+ assert_equal(old, current?(:uid, user), "UID was not changed back")
+ end
+
+ def attrtest_groups(user)
+ Etc.setgrent
+ max = 0
+ while group = Etc.getgrent
+ if group.gid > max and group.gid < 5000
+ max = group.gid
+ end
+ end
+
+ groups = []
+ main = []
+ extra = []
+ 5.times do |i|
+ i += 1
+ name = "pptstgr%s" % i
+ tmpgroup = Puppet.type(:group).create(
+ :name => name,
+ :gid => max + i
+ )
+
+ groups << tmpgroup
+
+ cleanup do
+ tmpgroup.provider.delete if tmpgroup.provider.exists?
+ end
+
+ if i < 3
+ main << name
+ else
+ extra << name
+ end
+ end
+
+ # Create our test groups
+ assert_apply(*groups)
+
+ # Now add some of them to our user
+ assert_nothing_raised {
+ user.model[:groups] = extra.join(",")
+ }
+
+ # Some tests to verify that groups work correctly startig from nothing
+ # Remove our user
+ user.ensure = :absent
+
+ # And add it again
+ user.ensure = :present
+
+ # Make sure that the group list is added at creation time.
+ # This is necessary because we don't have default fakedata for groups.
+ assert(user.groups, "Did not retrieve group list")
+
+ list = user.groups.split(",")
+ assert_equal(extra.sort, list.sort, "Group list was not set at creation time")
+
+ # Now set to our main list of groups
+ assert_nothing_raised {
+ user.groups = main.join(",")
+ }
+
+ list = user.groups.split(",")
+ assert_equal(main.sort, list.sort, "Group list is not equal")
+ end
+
+ if Process.uid == 0
+ def test_simpleuser
+ name = "pptest"
+
+ assert(missing?(name), "User %s is present" % name)
+
+ user = mkuser(name)
+
+ eachstate do |state|
+ if val = fakedata(user.name, state)
+ user.model[state] = val
+ end
+ end
+
+ @@tmpusers << name
+
+ assert_nothing_raised {
+ user.create
+ }
+
+ assert_equal("Puppet Testing User pptest",
+ user.comment,
+ "Comment was not set")
+
+ assert_nothing_raised {
+ user.delete
+ }
+
+ assert(missing?(user.name), "User was not deleted")
+ end
+
+ def test_alluserstates
+ user = nil
+ name = "pptest"
+
+ assert(missing?(name), "User %s is present" % name)
+
+ user = mkuser(name)
+
+ eachstate do |state|
+ if val = fakedata(user.name, state)
+ user.model[state] = val
+ end
+ end
+
+ @@tmpusers << name
+
+ assert_nothing_raised {
+ user.create
+ }
+ assert_equal("Puppet Testing User pptest", user.comment,
+ "Comment was not set")
+
+ tests = Puppet::Type.type(:user).validstates
+
+ just = nil
+ tests.each { |test|
+ next unless test == :groups
+ if self.respond_to?("attrtest_%s" % test)
+ self.send("attrtest_%s" % test, user)
+ else
+ Puppet.err "Not testing attr %s of user" % test
+ end
+ }
+
+ assert_nothing_raised {
+ user.delete
+ }
+ end
+
+ # This is a weird method that shows how annoying the interface between
+ # types and providers is. Grr.
+ def test_duplicateIDs
+ user1 = mkuser("user1")
+ user1.create
+ user1.uid = 125
+ user2 = mkuser("user2")
+ user2.model[:uid] = 125
+
+ cleanup do
+ user1.ensure = :absent
+ user2.ensure = :absent
+ end
+
+ # Not all OSes fail here, so we can't test that it doesn't work with
+ # it off, only that it does work with it on.
+ assert_nothing_raised {
+ user2.model[:allowdupe] = :true
+ }
+ assert_nothing_raised { user2.create }
+ assert_equal(:present, user2.ensure,
+ "User did not get created")
+ end
+ else
+ $stderr.puts "Not root; skipping user creation/modification tests"
+ end
+
+ # Here is where we test individual providers
+ def test_useradd_flags
+ useradd = nil
+ assert_nothing_raised {
+ useradd = Puppet::Type.type(:user).provider(:useradd)
+ }
+ assert(useradd, "Did not retrieve useradd provider")
+
+ user = nil
+ assert_nothing_raised {
+ fakemodel = fakemodel(:user, @me)
+ user = useradd.new(fakemodel)
+ }
+
+ assert_equal("-d", user.send(:flag, :home),
+ "Incorrect home flag")
+
+ assert_equal("-s", user.send(:flag, :shell),
+ "Incorrect shell flag")
+ end
+end
+
+# $Id$
diff --git a/test/puppet/utiltest.rb b/test/puppet/utiltest.rb
index ebf61e0f0..c2dcdada5 100755
--- a/test/puppet/utiltest.rb
+++ b/test/puppet/utiltest.rb
@@ -294,6 +294,48 @@ class TestPuppetUtil < Test::Unit::TestCase
assert(Process.euid == 0, "UID did not get reset")
end
end
+
+ def test_proxy
+ klass = Class.new do
+ attr_accessor :hash
+ class << self
+ attr_accessor :ohash
+ end
+ end
+ klass.send(:include, Puppet::Util)
+
+ klass.ohash = {}
+
+ inst = klass.new
+ inst.hash = {}
+ assert_nothing_raised do
+ Puppet::Util.proxy klass, :hash, "[]", "[]=", :clear, :delete
+ end
+
+ assert_nothing_raised do
+ Puppet::Util.classproxy klass, :ohash, "[]", "[]=", :clear, :delete
+ end
+
+ assert_nothing_raised do
+ inst[:yay] = "boo"
+ inst["cool"] = :yayness
+ end
+
+ [:yay, "cool"].each do |var|
+ assert_equal(inst.hash[var], inst[var],
+ "Var %s did not take" % var)
+ end
+
+ assert_nothing_raised do
+ klass[:Yay] = "boo"
+ klass["Cool"] = :yayness
+ end
+
+ [:Yay, "Cool"].each do |var|
+ assert_equal(inst.hash[var], inst[var],
+ "Var %s did not take" % var)
+ end
+ end
end
# $Id$
diff --git a/test/puppettest.rb b/test/puppettest.rb
index cc7ff2ccc..a49ceb175 100644
--- a/test/puppettest.rb
+++ b/test/puppettest.rb
@@ -9,6 +9,100 @@ require 'test/unit'
module TestPuppet
include ObjectSpace
+ # A baseclass for the faketypes.
+ class FakeModel < Hash
+ class << self
+ attr_accessor :name
+ @name = :fakemodel
+ end
+
+ def self.validstates
+ Puppet::Type.type(@name).validstates
+ end
+
+ def self.validstate?(name)
+ Puppet::Type.type(@name).validstate?(name)
+ end
+
+ def initialize(name)
+ self[:name] = name
+ end
+
+ def inspect
+ "%s(%s)" % [self.class.to_s.sub(/.+::/, ''), super()]
+ end
+
+ def name
+ self[:name]
+ end
+ end
+
+ class FakeProvider
+ attr_accessor :model
+ class << self
+ attr_accessor :name, :model, :methods
+ end
+
+ # A very low number, so these never show up as defaults via the standard
+ # algorithms.
+ def self.defaultnum
+ -50
+ end
+
+ # Set up methods to fake things
+ def self.apimethods(*ary)
+ @model.validstates.each do |state|
+ ary << state unless ary.include? state
+ end
+ attr_accessor *ary
+
+ @methods = ary
+ end
+
+ def self.initvars
+ @calls = Hash.new do |hash, key|
+ hash[key] = 0
+ end
+ end
+
+ def self.suitable?
+ true
+ end
+
+ def initialize(model)
+ @model = model
+ end
+ end
+
+ @@fakemodels = {}
+ @@fakeproviders = {}
+
+ def fakemodel(type, name, options = {})
+ type = type.intern if type.is_a? String
+ unless @@fakemodels.include? type
+ @@fakemodels[type] = Class.new(FakeModel)
+ @@fakemodels[type].name = type
+ end
+
+ obj = @@fakemodels[type].new(name)
+ obj[:name] = name
+ options.each do |name, val|
+ obj[name] = val
+ end
+ obj
+ end
+
+ def fakeprovider(type, model)
+ type = type.intern if type.is_a? String
+ unless @@fakeproviders.include? type
+ @@fakeproviders[type] = Class.new(FakeModel) do
+ @name = type
+ end
+ end
+
+ @@fakeproviders[type].new(model)
+ end
+
def gcdebug(type)
Puppet.warning "%s: %s" % [type, ObjectSpace.each_object(type) { |o| }]
end
@@ -119,6 +213,19 @@ module TestPuppet
@@cleaners << block
end
+ def setme
+ # retrieve the user name
+ id = %x{id}.chomp
+ if id =~ /uid=\d+\(([^\)]+)\)/
+ @me = $1
+ else
+ puts id
+ end
+ unless defined? @me
+ raise "Could not retrieve user name; 'id' did not work"
+ end
+ end
+
def teardown
stopservices
diff --git a/test/server/pelement.rb b/test/server/pelement.rb
index 7bff1cb26..5d5b5ceb4 100644
--- a/test/server/pelement.rb
+++ b/test/server/pelement.rb
@@ -27,11 +27,8 @@ class TestPElementServer < Test::Unit::TestCase
obj.retrieve
end
- assert(obj.insync?, "Described %s[%s] is not in sync" %
- [trans.type, name])
-
if trans.type == :package
- assert_equal(Puppet::Type.type(:package).default, obj[:type])
+ assert_equal(Puppet::Type.type(:package).defaultprovider.name, obj[:provider])
end
end
type.clear
diff --git a/test/types/basic.rb b/test/types/basic.rb
index 1c41d37bc..f734cb5a0 100644
--- a/test/types/basic.rb
+++ b/test/types/basic.rb
@@ -38,7 +38,7 @@ class TestBasic < Test::Unit::TestCase
assert_nothing_raised() {
@sleeper = Puppet.type(:service).create(
:name => "sleeper",
- :type => "init",
+ :provider => "init",
:path => File.join($puppetbase,"examples/root/etc/init.d"),
:hasstatus => true,
:ensure => :running
diff --git a/test/types/cron.rb b/test/types/cron.rb
index fee09b360..caada9f91 100755
--- a/test/types/cron.rb
+++ b/test/types/cron.rb
@@ -15,16 +15,8 @@ class TestCron < Test::Unit::TestCase
include TestPuppet
def setup
super
- # retrieve the user name
- id = %x{id}.chomp
- if id =~ /uid=\d+\(([^\)]+)\)/
- @me = $1
- else
- puts id
- end
- unless defined? @me
- raise "Could not retrieve user name; 'id' did not work"
- end
+
+ setme()
# god i'm lazy
@crontype = Puppet.type(:cron)
diff --git a/test/types/group.rb b/test/types/group.rb
index cb9b988ed..508b8436c 100755
--- a/test/types/group.rb
+++ b/test/types/group.rb
@@ -1,11 +1,9 @@
if __FILE__ == $0
$:.unshift '..'
$:.unshift '../../lib'
- $puppetbase = "../../../../language/trunk"
+ $puppetbase = "../.."
end
-# $Id$
-
require 'etc'
require 'puppet/type'
require 'puppettest'
@@ -13,18 +11,40 @@ require 'test/unit'
class TestGroup < Test::Unit::TestCase
include TestPuppet
+
+ p = Puppet::Type.type(:group).provide :fake, :parent => TestPuppet::FakeProvider do
+ @name = :fake
+ apimethods :ensure, :gid
+
+ def create
+ @ensure = :present
+ end
+
+ def delete
+ @ensure = :absent
+ end
+
+ def exists?
+ if defined? @ensure and @ensure == :present
+ true
+ else
+ false
+ end
+ end
+ end
+
+ FakeGroupProvider = p
+
+ @@fakeproviders[:group] = p
+
def setup
super
- @@tmpgroups = []
+ Puppet::Type.type(:group).defaultprovider = FakeGroupProvider
end
def teardown
Puppet.type(:group).clear
- @@tmpgroups.each { |group|
- unless missing?(group)
- remove(group)
- end
- }
+ Puppet::Type.type(:group).defaultprovider = nil
super
end
@@ -38,60 +58,6 @@ class TestGroup < Test::Unit::TestCase
return group
end
- case Facter["operatingsystem"].value
- when "Darwin":
- def missing?(group)
- output = %x{nidump -r /groups/#{group} / 2>/dev/null}.chomp
-
- if output == ""
- return true
- else
- return false
- end
-
- assert_equal("", output, "Group %s is present:\n%s" % [group, output])
- end
-
- def gid(name)
- %x{nireport / /groups name gid}.split("\n").each { |line|
- group, id = line.chomp.split(/\s+/)
- assert(id =~ /^-?\d+$/, "Group id %s for %s is not a number" %
- [id, group])
- if group == name
- return Integer(id)
- end
- }
-
- return nil
- end
-
- def remove(group)
- system("niutil -destroy / /groups/%s" % group)
- end
- else
- def missing?(group)
- begin
- obj = Etc.getgrnam(group)
- return false
- rescue ArgumentError
- return true
- end
- end
-
- def gid(name)
- assert_nothing_raised {
- obj = Etc.getgrnam(name)
- return obj.gid
- }
-
- return nil
- end
-
- def remove(group)
- system("groupdel %s" % group)
- end
- end
-
def groupnames
%x{groups}.chomp.split(/ /)
end
@@ -101,99 +67,52 @@ class TestGroup < Test::Unit::TestCase
end
def attrtest_ensure(group)
- old = group.is(:ensure)
group[:ensure] = :absent
comp = newcomp("ensuretest", group)
- assert_apply(group)
- assert(missing?(group.name), "User is still present")
+ assert_apply(comp)
+ assert_equal(:absent, group.provider.ensure, "Group is still present")
group[:ensure] = :present
assert_events([:group_created], comp)
- assert(!missing?(group.name), "User is absent")
+ assert_equal(:present, group.provider.ensure, "Group is absent")
group[:ensure] = :absent
trans = assert_events([:group_removed], comp)
+ assert_equal(:absent, group.provider.ensure, "Group is present")
assert_rollback_events(trans, [:group_created], "group")
-
- group[:ensure] = old
- assert_apply(group)
+ assert_equal(:present, group.provider.ensure, "Group is absent")
end
+ # This is a bit odd, since we're not actually doing anything on the machine.
+ # Just make sure we can set the gid and that it will work correctly.
def attrtest_gid(group)
- obj = nil
- #assert_nothing_raised {
- # obj = Etc.getgrnam(group[:name])
- #}
- group.retrieve
- old = gid(group[:name])
- comp = newcomp("gidtest", group)
-
- group[:gid] = old
-
- trans = assert_events([], comp, "group")
-
- newgid = old
- while true
- newgid += 1
-
- if newgid - old > 1000
- $stderr.puts "Could not find extra test UID"
- return
- end
- begin
- Etc.getgrgid(newgid)
- rescue ArgumentError => detail
- break
- end
- end
-
- assert_nothing_raised("Failed to change group id") {
- group[:gid] = newgid
- }
-
- trans = assert_events([:group_modified], comp, "group")
- curgid = nil
+ # Check the validation.
assert_nothing_raised {
- curgid = gid(group[:name])
+ group[:gid] = "15"
}
- assert_equal(newgid, curgid, "GID was not changed")
+ assert_equal(15, group.should(:gid),
+ "Did not convert gid to number")
- assert_rollback_events(trans, [:group_modified], "group")
+ comp = newcomp(group)
+ trans = assert_events([:group_modified], comp, "group")
+ assert_equal(15, group.provider.gid, "GID was not changed")
assert_nothing_raised {
- curgid = gid(group[:name])
+ group[:gid] = 16
}
- assert_equal(old, curgid, "UID was not reverted")
- end
-
- # Disabled, because it was testing implementation, not function
- def disabled_test_eachmethod
- obj = Etc.getgrnam(groupnames()[0])
-
- assert(obj, "Could not retrieve test group object")
+ assert_equal(16, group.should(:gid),
+ "Did not keep gid as number")
- Puppet.type(:group).validstates.each { |name, state|
- assert_nothing_raised {
- method = state.infomethod
- assert(method, "State %s has no infomethod" % name)
- assert(obj.respond_to?(method),
- "State %s has an invalid method %s" %
- [name, method]
- )
- }
+ # Now switch to 16
+ trans = assert_events([:group_modified], comp, "group")
+ assert_equal(16, group.provider.gid, "GID was not changed")
- assert_nothing_raised {
- method = state.infomethod
- assert(method, "State %s has no infomethod" % name)
- assert(obj.respond_to?(method),
- "State %s has an invalid method %s" %
- [name, method]
- )
- }
- }
+ # And then rollback
+ assert_rollback_events(trans, [:group_modified], "group")
+ assert_equal(15, group.provider.gid, "GID was not changed")
end
def test_owngroups
@@ -205,119 +124,52 @@ class TestGroup < Test::Unit::TestCase
:name => group,
:check => [:gid]
)
-
- comp = newcomp("grouptest %s" % group, gobj)
}
- #trans = nil
+ # Set a fake gid
+ gobj.provider.gid = rand(100)
+
assert_nothing_raised {
gobj.retrieve
- #trans = comp.evaluate
}
assert(gobj.is(:gid), "Failed to retrieve gid")
}
end
- # Test that we can query things
- # It'd be nice if we could automate this...
- def test_checking
- require 'etc'
+ def test_mkgroup
+ gobj = nil
+ comp = nil
+ name = "pptestgr"
- name = nil
- assert_nothing_raised {
- name = Etc.getgrgid(Process.gid).name
- }
- user = nil
assert_nothing_raised {
- checks = Puppet.type(:group).validstates
- user = Puppet.type(:group).create(
+ gobj = Puppet.type(:group).create(
:name => name,
- :check => checks
+ :ensure => :present
)
- }
- assert_nothing_raised {
- user.retrieve
+ comp = newcomp("groupmaker %s" % name, gobj)
}
- assert_equal(Process.gid, user.is(:gid), "Retrieved UID does not match")
- end
+ trans = assert_events([:group_created], comp, "group")
- if Process.uid == 0
- def test_mkgroup
- gobj = nil
- comp = nil
- name = "pptestgr"
-
- #os = Facter["operatingsystem"].value
-
- #if os == "Darwin"
- # obj = nil
- # assert_nothing_raised {
- # obj = Etc.getgrnam(name)
- # }
- # assert_equal(-2, obj.gid, "Darwin GID is not -2")
- #else
- #assert_raise(ArgumentError) {
- # obj = Etc.getgrnam(name)
- #}
- #end
- assert(missing?(name), "Group %s is still present" % name)
- assert_nothing_raised {
- gobj = Puppet.type(:group).create(
- :name => name,
- :ensure => :present
- )
-
- comp = newcomp("groupmaker %s" % name, gobj)
- }
-
- @@tmpgroups << name
- trans = assert_events([:group_created], comp, "group")
-
- obj = nil
- assert_nothing_raised {
- obj = Etc.getgrnam(name)
- }
- assert(!missing?(name), "Group %s is missing" % name)
+ assert(gobj.provider.exists?,
+ "Did not create group")
- tests = Puppet.type(:group).validstates
+ tests = Puppet.type(:group).validstates
- gobj.retrieve
- tests.each { |test|
- if self.respond_to?("attrtest_%s" % test)
- self.send("attrtest_%s" % test, gobj)
- else
- #$stderr.puts "Not testing attr %s of group" % test
- end
- }
-
- assert_rollback_events(trans, [:group_removed], "group")
-
- assert(missing?(name), "Group %s is still present" % name)
- end
-
- # groupadd -o is broken in FreeBSD.
- unless Facter["operatingsystem"].value == "FreeBSD"
- def test_duplicateIDs
- group1 = mkgroup("group1", :gid => 125)
- group2 = mkgroup("group2", :gid => 125)
+ gobj.retrieve
+ tests.each { |test|
+ if self.respond_to?("attrtest_%s" % test)
+ self.send("attrtest_%s" % test, gobj)
+ else
+ #$stderr.puts "Not testing attr %s of group" % test
+ end
+ }
- assert_apply(group1)
+ assert_rollback_events(trans, [:group_removed], "group")
- # Not all OSes fail here, so we can't test that it doesn't work with
- # it off, only that it does work with it on.
- assert_nothing_raised {
- group2[:allowdupe] = true
- }
- assert_apply(group2)
- group2.retrieve
- assert_equal(:present, group2.state(:ensure).is,
- "Group did not get created")
- end
- end
- else
- $stderr.puts "Not running as root; skipping group creation tests."
+ assert(! gobj.provider.exists?,
+ "Did not delete group")
end
end
diff --git a/test/types/package.rb b/test/types/package.rb
index d4ddc14c9..779c693fe 100644
--- a/test/types/package.rb
+++ b/test/types/package.rb
@@ -11,7 +11,7 @@ require 'facter'
$platform = Facter["operatingsystem"].value
-unless Puppet.type(:package).default
+unless Puppet.type(:package).defaultprovider
puts "No default package type for %s; skipping package tests" % $platform
else
@@ -54,14 +54,15 @@ class TestPackages < Test::Unit::TestCase
list.each { |pkg, source|
hash = {:name => pkg}
if useensure
- hash[:ensure] = "latest"
+ hash[:ensure] = "installed"
end
if source
source = source[0] if source.is_a? Array
hash[:source] = source
end
+ # Override the default package type for our test packages.
if Facter["operatingsystem"].value == "Darwin"
- hash[:type] = "darwinport"
+ hash[:provider] = "darwinport"
end
obj = Puppet.type(:package).create(hash)
assert(pkg, "Could not create package")
@@ -103,6 +104,8 @@ class TestPackages < Test::Unit::TestCase
retval = {"aop" => nil}
when "FreeBSD":
retval = {"yahtzee" => nil}
+ when "RedHat":
+ retval = {"puppet" => "/home/luke/rpm/RPMS/i386/puppet-0.16.1-1.i386.rpm"}
else
Puppet.notice "No test packages for %s" % $platform
end
@@ -168,18 +171,6 @@ class TestPackages < Test::Unit::TestCase
}
end
- def test_specifypkgtype
- pkg = nil
- assert_nothing_raised {
- pkg = Puppet.type(:package).create(
- :name => "mypkg",
- :type => "yum"
- )
- }
- assert(pkg, "Did not create package")
- assert_equal(:yum, pkg[:type])
- end
-
def test_latestpkg
mkpkgs { |pkg|
next unless pkg.respond_to? :latest
@@ -194,11 +185,8 @@ class TestPackages < Test::Unit::TestCase
def test_listing
pkgtype = Puppet::Type.type(:package)
- # Heh
- defaulttype = pkgtype.pkgtype(pkgtype.default)
-
assert_nothing_raised("Could not list packages") do
- defaulttype.list
+ pkgtype.list
end
end
@@ -213,14 +201,14 @@ class TestPackages < Test::Unit::TestCase
pkg.retrieve
}
- if pkg.insync? or pkg.is(:ensure) != :absent
+ if pkg.provider.query
Puppet.notice "Test package %s is already installed; please choose a different package for testing" % pkg
next
end
comp = newcomp("package", pkg)
- assert_events([:package_created], comp, "package")
+ assert_events([:package_installed], comp, "package")
pkg.retrieve
@@ -243,7 +231,7 @@ class TestPackages < Test::Unit::TestCase
pkg[:ensure] = "latest"
}
- assert_events([:package_created], comp, "package")
+ assert_events([:package_installed], comp, "package")
pkg.retrieve
assert(pkg.insync?, "After install, package is not insync")
@@ -306,9 +294,9 @@ class TestPackages < Test::Unit::TestCase
modpkg(pkg)
- assert(pkg.latest, "Could not retrieve latest value")
+ assert(pkg.provider.latest, "Could not retrieve latest value")
- assert_events([:package_created], pkg)
+ assert_events([:package_installed], pkg)
assert_nothing_raised {
pkg.retrieve
@@ -345,7 +333,8 @@ class TestPackages < Test::Unit::TestCase
pkg = Puppet::Type.type(:package).create(
:name => "pkgtesting",
:source => "/Users/luke/Documents/Puppet/pkgtesting.pkg",
- :ensure => :present
+ :ensure => :present,
+ :provider => :apple
)
}
@@ -362,7 +351,7 @@ class TestPackages < Test::Unit::TestCase
@@tmpfiles << "/tmp/file"
@@tmpfiles << "/Library/Receipts/pkgtesting.pkg"
- assert_events([:package_created], pkg, "package")
+ assert_events([:package_installed], pkg, "package")
assert_nothing_raised {
pkg.retrieve
@@ -375,15 +364,15 @@ class TestPackages < Test::Unit::TestCase
end
# Yay, gems. They're special because any OS can test them.
- if %x{which gem 2>/dev/null}.chomp != ""
+ if Puppet::Type.type(:package).provider(:gem).suitable?
def test_list_gems
gems = nil
assert_nothing_raised {
- gems = Puppet::Type.type(:package).pkgtype(:gem).list
+ gems = Puppet::Type.type(:package).provider(:gem).list
}
gems.each do |gem|
- assert_equal(:gem, gem[:type],
+ assert_equal(:gem, gem[:provider],
"Type was not set correctly")
end
end
@@ -396,7 +385,7 @@ class TestPackages < Test::Unit::TestCase
:name => name,
:version => "0.0.2",
:ensure => "installed",
- :type => :gem
+ :provider => :gem
)
}
@@ -410,7 +399,7 @@ class TestPackages < Test::Unit::TestCase
return
end
- assert_events([:package_created], gem)
+ assert_events([:package_installed], gem)
assert_nothing_raised {
gem.retrieve
@@ -421,7 +410,7 @@ class TestPackages < Test::Unit::TestCase
latest = nil
assert_nothing_raised {
- latest = gem.latest
+ latest = gem.provider.latest
}
assert(latest != gem[:version], "Did not correctly find latest value")
@@ -440,12 +429,13 @@ class TestPackages < Test::Unit::TestCase
end
else
+ $stderr.puts "Install gems for gem tests"
def test_nogems_nofailures
obj = nil
assert_nothing_raised do
Puppet::Type.newpackage(
:name => "yayness",
- :type => "gem",
+ :provider => "gem",
:ensure => "installed"
)
end
@@ -455,7 +445,7 @@ class TestPackages < Test::Unit::TestCase
end
end
end
- if ["Fedora", "RedHat", "CentOS"].include?(Facter["operatingsystem"].value) and
+ if Puppet.type(:package).provider(:rpm).suitable? and
FileTest.exists?("/home/luke/rpm/RPMS/i386/puppet-server-0.16.1-1.i386.rpm")
# We have a special test here, because we don't actually want to install the
@@ -464,73 +454,36 @@ class TestPackages < Test::Unit::TestCase
pkg = nil
assert_nothing_raised {
pkg = Puppet::Type.type(:package).create(
- :type => :rpm,
+ :provider => :rpm,
:name => "puppet-server",
:source => "/home/luke/rpm/RPMS/i386/puppet-server-0.16.1-1.i386.rpm"
)
}
- assert_equal("0.16.1-1", pkg.latest, "RPM did not provide correct value for latest")
+ assert_equal("0.16.1-1", pkg.provider.latest, "RPM did not provide correct value for latest")
end
end
- if Facter["operatingsystem"].value == "Solaris"
- if pkgget = %x{which pkg-get 2>/dev/null}.chomp and pkgget != ""
- # FIXME The packaging crap needs to be rewritten to support testing
- # multiple package types on the same platform.
- def test_list_blastwave
- pkgs = nil
- assert_nothing_raised {
- pkgs = Puppet::Type.type(:package).pkgtype(:blastwave).list
- }
-
- pkgs.each do |pkg|
- if pkg[:name] =~ /^CSW/
- assert_equal(:blastwave, pkg[:type],
- "Type was not set correctly")
- end
+ def test_packagedefaults
+ should = case Facter["operatingsystem"].value
+ when "Debian": :apt
+ when "Darwin": :apple
+ when "RedHat": :rpm
+ when "Fedora": :yum
+ when "FreeBSD": :ports
+ when "OpenBSD": :openbsd
+ when "Solaris": :sun
end
- end
- def test_install_blastwave
- pkg = nil
- name = "cabextract"
- assert_nothing_raised {
- pkg = Puppet::Type.newpackage(
- :name => name,
- :ensure => "installed",
- :type => :blastwave
- )
- }
+ default = Puppet.type(:package).defaultprovider
+ assert(default, "No default package provider for %s" %
+ Facter["operatingsystem"].value)
- assert_nothing_raised {
- pkg.retrieve
- }
- if pkg.is(:ensure) != :absent
- p pkg.is(:ensure)
- $stderr.puts "Cannot test pkg installation; %s is already installed" %
- name
- return
+ if should
+ assert_equal(should, default.name,
+ "Incorrect default package format")
end
-
- assert_events([:package_created], pkg)
-
- assert_nothing_raised {
- pkg.retrieve
- }
-
- latest = nil
- assert_nothing_raised {
- latest = pkg.latest
- }
- pkg[:ensure] = :absent
-
- assert_events([:package_removed], pkg)
- end
- else
- $stderr.puts "No pkg-get scripting; skipping blastwave tests"
- end
end
end
end
diff --git a/test/types/query.rb b/test/types/query.rb
index 451291cac..121670012 100644
--- a/test/types/query.rb
+++ b/test/types/query.rb
@@ -34,7 +34,7 @@ class TestQuery < Test::Unit::TestCase
unless Puppet.type(:service).has_key?("sleeper")
Puppet.type(:service).create(
:name => "sleeper",
- :type => "init",
+ :provider => "init",
:path => File.join($puppetbase,"examples/root/etc/init.d"),
:hasstatus => true,
:check => [:ensure]
@@ -82,6 +82,7 @@ class TestQuery < Test::Unit::TestCase
def test_service
service = service()
+ assert(service, "Did not get service object")
service.eachstate { |state|
assert_nil(state.is)
}
diff --git a/test/types/service.rb b/test/types/service.rb
index 3a8ba089a..176f84818 100644
--- a/test/types/service.rb
+++ b/test/types/service.rb
@@ -118,7 +118,7 @@ class TestLocalService < Test::Unit::TestCase
end
def mktestsvcs
- tstsvcs.collect { |svc,svcargs|
+ list = tstsvcs.collect { |svc,svcargs|
args = svcargs.dup
args[:name] = svc
Puppet.type(:service).create(args)
@@ -167,11 +167,9 @@ class TestLocalService < Test::Unit::TestCase
# test refreshing it
assert_nothing_raised() {
- service.refresh
+ service.provider.refresh
}
- assert(service.respond_to?(:refresh))
-
# now stop it
assert_nothing_raised() {
service[:ensure] = :stopped
@@ -229,7 +227,7 @@ class TestLocalService < Test::Unit::TestCase
mktestsvcs.each { |svc|
val = nil
assert_nothing_raised("Could not get status") {
- val = svc.status
+ val = svc.provider.status
}
assert_instance_of(Symbol, val)
}
@@ -242,29 +240,28 @@ class TestLocalService < Test::Unit::TestCase
mktestsvcs.each { |svc|
startstate = nil
assert_nothing_raised("Could not get status") {
- startstate = svc.status
+ startstate = svc.provider.status
}
cycleservice(svc)
svc[:ensure] = startstate
assert_apply(svc)
- Puppet.type(:service).clear
Puppet.type(:component).clear
}
end
def test_serviceenabledisable
mktestsvcs.each { |svc|
+ assert(svc[:name], "Service has no name")
startstate = nil
svc[:check] = :enable
assert_nothing_raised("Could not get status") {
- startstate = svc.enabled?
+ startstate = svc.provider.enabled?
}
cycleenable(svc)
svc[:enable] = startstate
assert_apply(svc)
- Puppet.type(:service).clear
Puppet.type(:component).clear
}
end
@@ -299,7 +296,6 @@ class TestLocalService < Test::Unit::TestCase
svc[:enable] = startenable
svc[:ensure] = startensure
assert_apply(svc)
- Puppet.type(:service).clear
Puppet.type(:component).clear
end
end
diff --git a/test/types/state.rb b/test/types/state.rb
index 406b9af6b..638372afe 100644
--- a/test/types/state.rb
+++ b/test/types/state.rb
@@ -32,6 +32,7 @@ class TestState < Test::Unit::TestCase
def test_newvalue
state = newstate()
+ # These are bogus because they don't define events. :/
assert_nothing_raised {
state.newvalue(:one) do
@is = 1
@@ -51,6 +52,7 @@ class TestState < Test::Unit::TestCase
}
assert_equal(:one, inst.should)
+ ret = nil
assert_nothing_raised { inst.set_one }
assert_equal(1, inst.is)
@@ -87,6 +89,43 @@ class TestState < Test::Unit::TestCase
assert_equal("yayness".upcase, inst.is)
end
+
+ def test_newvalue_event_option
+ state = newstate()
+
+ assert_nothing_raised do
+ state.newvalue(:myvalue, :event => :fake_valued) do
+ @is = :valued
+ end
+ state.newvalue(:other, :event => "fake_other") do
+ @is = :valued
+ end
+ end
+ inst = newinst(state)
+
+ assert_nothing_raised {
+ inst.should = :myvalue
+ }
+
+ ret = nil
+ assert_nothing_raised {
+ ret = inst.sync
+ }
+
+ assert_equal(:fake_valued, ret,
+ "Event did not get returned correctly")
+
+ assert_nothing_raised {
+ inst.should = :other
+ }
+
+ assert_nothing_raised {
+ ret = inst.sync
+ }
+
+ assert_equal(:fake_other, ret,
+ "Event did not get returned correctly")
+ end
end
# $Id$
diff --git a/test/types/type.rb b/test/types/type.rb
index 333c5683f..48e0b4323 100644
--- a/test/types/type.rb
+++ b/test/types/type.rb
@@ -35,20 +35,22 @@ class TestType < Test::Unit::TestCase
# next
#end
- assert(
- type.namevar,
- "Failed to retrieve namevar for %s" % name
- )
+ 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.states,
+ "States for %s are nil" % name
+ )
- assert_not_nil(
- type.validstates,
- "Valid states for %s are nil" % name
- )
+ assert_not_nil(
+ type.validstates,
+ "Valid states for %s are nil" % name
+ )
+ }
}
end
@@ -464,6 +466,63 @@ end
assert_equal("yaytest", File.read(path),
"Exec did not correctly copy file.")
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
end
# $Id$
diff --git a/test/types/user.rb b/test/types/user.rb
index 87e90ff22..008f39272 100755
--- a/test/types/user.rb
+++ b/test/types/user.rb
@@ -1,11 +1,9 @@
if __FILE__ == $0
$:.unshift '..'
$:.unshift '../../lib'
- $puppetbase = "../../../../language/trunk"
+ $puppetbase = "../.."
end
-# $Id$
-
require 'etc'
require 'puppet/type'
require 'puppettest'
@@ -13,90 +11,37 @@ require 'test/unit'
class TestUser < Test::Unit::TestCase
include TestPuppet
- def setup
- super
- @@tmpusers = []
- end
- def teardown
- @@tmpusers.each { |user|
- unless missing?(user)
- remove(user)
+ p = Puppet::Type.type(:user).provide :fake, :parent => TestPuppet::FakeProvider do
+ @name = :fake
+ apimethods
+ def create
+ @ensure = :present
+ @model.eachstate do |state|
+ next if state.name == :ensure
+ state.sync
end
- }
- super
- #Puppet.type(:user).clear
- end
-
- case Facter["operatingsystem"].value
- when "Darwin":
- def missing?(user)
- output = %x{nidump -r /users/#{user} / 2>/dev/null}.chomp
-
- if output == ""
- return true
- else
- return false
- end
-
- assert_equal("", output, "User %s is present:\n%s" % [user, output])
end
- def current?(param, name)
- state = Puppet.type(:user).states.find { |st|
- st.name == param
- }
-
- output = %x{nireport / /users name #{state.netinfokey}}
- output.split("\n").each { |line|
- if line =~ /^(\w+)\s+(.+)$/
- user = $1
- id = $2.sub(/\s+$/, '')
- if user == name
- if id =~ /^[-0-9]+$/
- return Integer(id)
- else
- return id
- end
- end
- else
- raise "Could not match %s" % line
- end
- }
-
- return nil
+ def delete
+ @ensure = :absent
+ @model.eachstate do |state|
+ send(state.name.to_s + "=", :absent)
+ end
end
- def remove(user)
- system("niutil -destroy / /users/%s" % user)
- end
- else
- def missing?(user)
- begin
- obj = Etc.getpwnam(user)
- return false
- rescue ArgumentError
- return true
+ def exists?
+ if defined? @ensure and @ensure == :present
+ true
+ else
+ false
end
end
+ end
- def current?(param, name)
- state = Puppet.type(:user).states.find { |st|
- st.name == param
- }
-
- assert_nothing_raised {
- obj = Etc.getpwnam(name)
- return obj.send(state.posixmethod)
- }
-
- return nil
- end
+ FakeUserProvider = p
- def remove(user)
- system("userdel %s" % user)
- end
- end
+ @@fakeproviders[:group] = p
def findshell(old = nil)
%w{/bin/sh /bin/bash /sbin/sh /bin/ksh /bin/zsh /bin/csh /bin/tcsh
@@ -110,6 +55,16 @@ class TestUser < Test::Unit::TestCase
}
end
+ def setup
+ super
+ Puppet::Type.type(:user).defaultprovider = FakeUserProvider
+ end
+
+ def teardown
+ Puppet::Type.type(:user).defaultprovider = nil
+ super
+ end
+
def mkuser(name)
user = nil
assert_nothing_raised {
@@ -122,19 +77,21 @@ class TestUser < Test::Unit::TestCase
)
}
+ assert(user, "Did not create user")
+
return user
end
def attrtest_ensure(user)
- old = user.is(:ensure)
+ old = user.provider.ensure
user[:ensure] = :absent
comp = newcomp("ensuretest", user)
assert_apply(user)
- assert(missing?(user.name), "User is still present")
+ assert(!user.provider.exists?, "User is still present")
user[:ensure] = :present
assert_events([:user_created], comp)
- assert(!missing?(user.name), "User is absent")
+ assert(user.provider.exists?, "User is absent")
user[:ensure] = :absent
trans = assert_events([:user_removed], comp)
@@ -146,19 +103,19 @@ class TestUser < Test::Unit::TestCase
def attrtest_comment(user)
user.retrieve
- old = user.is(:comment)
+ old = user.provider.comment
user[:comment] = "A different comment"
comp = newcomp("commenttest", user)
- trans = assert_events([:user_modified], comp, "user")
+ trans = assert_events([:user_changed], comp, "user")
- assert_equal("A different comment", current?(:comment, user[:name]),
+ assert_equal("A different comment", user.provider.comment,
"Comment was not changed")
- assert_rollback_events(trans, [:user_modified], "user")
+ assert_rollback_events(trans, [:user_changed], "user")
- assert_equal(old, current?(:comment, user[:name]),
+ assert_equal(old, user.provider.comment,
"Comment was not reverted")
end
@@ -166,24 +123,24 @@ class TestUser < Test::Unit::TestCase
obj = nil
comp = newcomp("hometest", user)
- old = current?(:home, user[:name])
+ old = user.provider.home
user[:home] = old
trans = assert_events([], comp, "user")
user[:home] = "/tmp"
- trans = assert_events([:user_modified], comp, "user")
+ trans = assert_events([:user_changed], comp, "user")
- assert_equal("/tmp", current?(:home, user[:name]), "Home was not changed")
+ assert_equal("/tmp", user.provider.home, "Home was not changed")
- assert_rollback_events(trans, [:user_modified], "user")
+ assert_rollback_events(trans, [:user_changed], "user")
- assert_equal(old, current?(:home, user[:name]), "Home was not reverted")
+ assert_equal(old, user.provider.home, "Home was not reverted")
end
def attrtest_shell(user)
- old = current?(:shell, user[:name])
+ old = user.provider.shell
comp = newcomp("shelltest", user)
user[:shell] = old
@@ -199,19 +156,21 @@ class TestUser < Test::Unit::TestCase
user[:shell] = newshell
- trans = assert_events([:user_modified], comp, "user")
+ trans = assert_events([:user_changed], comp, "user")
- assert_equal(newshell, current?(:shell, user[:name]),
+ user.retrieve
+ assert_equal(newshell, user.provider.shell,
"Shell was not changed")
- assert_rollback_events(trans, [:user_modified], "user")
+ assert_rollback_events(trans, [:user_changed], "user")
+ user.retrieve
- assert_equal(old, current?(:shell, user[:name]), "Shell was not reverted")
+ assert_equal(old, user.provider.shell, "Shell was not reverted")
end
def attrtest_gid(user)
obj = nil
- old = current?(:gid,user.name)
+ old = user.provider.gid
comp = newcomp("gidtest", user)
user.retrieve
@@ -239,7 +198,7 @@ class TestUser < Test::Unit::TestCase
user[:gid] = newgid
}
- trans = assert_events([:user_modified], comp, "user")
+ trans = assert_events([:user_changed], comp, "user")
# then by id
newgid = Etc.getgrnam(newgid).gid
@@ -252,23 +211,21 @@ class TestUser < Test::Unit::TestCase
assert_events([], comp, "user")
- assert_equal(newgid, current?(:gid,user[:name]), "GID was not changed")
+ assert_equal(newgid, user.provider.gid, "GID was not changed")
- assert_rollback_events(trans, [:user_modified], "user")
+ assert_rollback_events(trans, [:user_changed], "user")
- assert_equal(old, current?(:gid,user[:name]), "GID was not reverted")
+ assert_equal(old, user.provider.gid, "GID was not reverted")
end
def attrtest_uid(user)
obj = nil
comp = newcomp("uidtest", user)
- old = current?(:uid, user[:name])
- user[:uid] = old
+ user.provider.uid = 1
- trans = assert_events([], comp, "user")
-
- newuid = old
+ old = 1
+ newuid = 1
while true
newuid += 1
@@ -287,13 +244,13 @@ class TestUser < Test::Unit::TestCase
user[:uid] = newuid
}
- trans = assert_events([:user_modified], comp, "user")
+ trans = assert_events([:user_changed], comp, "user")
- assert_equal(newuid, current?(:uid, user[:name]), "UID was not changed")
+ assert_equal(newuid, user.provider.uid, "UID was not changed")
- assert_rollback_events(trans, [:user_modified], "user")
+ assert_rollback_events(trans, [:user_changed], "user")
- assert_equal(old, current?(:uid, user[:name]), "UID was not reverted")
+ assert_equal(old, user.provider.uid, "UID was not reverted")
end
def attrtest_groups(user)
@@ -311,11 +268,7 @@ class TestUser < Test::Unit::TestCase
5.times do |i|
i += 1
name = "pptstgr%s" % i
- groups << Puppet.type(:group).create(
- :name => name,
- :gid => max + i
- )
-
+ groups << name
if i < 3
main << name
else
@@ -323,9 +276,6 @@ class TestUser < Test::Unit::TestCase
end
end
- # Create our test groups
- assert_apply(*groups)
-
assert(user[:membership] == :minimum, "Membership did not default correctly")
assert_nothing_raised {
@@ -353,6 +303,10 @@ class TestUser < Test::Unit::TestCase
user[:ensure] = :present
assert_apply(user)
+ # Make sure that the groups are a string, not an array
+ assert(user.provider.groups.is_a?(String),
+ "Incorrectly passed an array to groups")
+
user.retrieve
assert(user.state(:groups).is, "Did not retrieve group list")
@@ -365,7 +319,7 @@ class TestUser < Test::Unit::TestCase
user[:groups] = main
}
- assert_equal((main + extra).sort.join(","), user.state(:groups).should)
+ assert_equal((main + extra).sort, user.state(:groups).should.sort)
assert_nothing_raised {
user.retrieve
@@ -393,7 +347,7 @@ class TestUser < Test::Unit::TestCase
assert(!user.insync?, "User is incorrectly in sync")
- assert_events([:user_modified], user)
+ assert_events([:user_changed], user)
assert_nothing_raised {
user.retrieve
}
@@ -401,75 +355,35 @@ class TestUser < Test::Unit::TestCase
list = user.state(:groups).is
assert_equal(main.sort, list.sort, "Group list is not equal")
- # Now delete our groups
- groups.each do |group|
- group[:ensure] = :absent
- end
-
user.delete(:groups)
-
- assert_apply(*groups)
- end
-
- # Disabled, because this is testing too much internal implementation
- def disabled_test_eachmethod
- obj = Etc.getpwuid(Process.uid)
-
- assert(obj, "Could not retrieve test group object")
-
- Puppet.type(:user).validstates.each { |name|
- assert_nothing_raised {
- method = state.posixmethod
- assert(method, "State %s has no infomethod" % name)
- assert(obj.respond_to?(method),
- "State %s has an invalid method %s" %
- [name, method])
- }
- }
end
- def test_checking
- require 'etc'
-
- name = nil
- assert_nothing_raised {
- name = Etc.getpwuid(Process.uid).name
- }
+ def test_autorequire
+ file = tempfile()
+ comp = nil
user = nil
+ group =nil
+ home = nil
+ ogroup = nil
assert_nothing_raised {
- checks = Puppet.type(:user).validstates
user = Puppet.type(:user).create(
- :name => name,
- :check => checks
+ :name => "pptestu",
+ :home => file,
+ :gid => "pptestg",
+ :groups => "yayness"
)
+ home = Puppet.type(:file).create(
+ :path => file,
+ :ensure => "directory"
+ )
+ group = Puppet.type(:group).create(
+ :name => "pptestg"
+ )
+ ogroup = Puppet.type(:group).create(
+ :name => "yayness"
+ )
+ comp = newcomp(user, group, home, ogroup)
}
-
- assert_nothing_raised {
- user.retrieve
- }
-
- assert_equal(Process.uid, user.is(:uid), "Retrieved UID does not match")
- end
-
- def test_autorequire
- file = tempfile()
- user = Puppet.type(:user).create(
- :name => "pptestu",
- :home => file,
- :gid => "pptestg",
- :groups => "yayness"
- )
- home = Puppet.type(:file).create(
- :path => file,
- :ensure => "directory"
- )
- group = Puppet.type(:group).create(
- :name => "pptestg"
- )
- ogroup = Puppet.type(:group).create(
- :name => "yayness"
- )
- comp = newcomp(user, group, home, ogroup)
comp.finalize
comp.retrieve
@@ -478,86 +392,52 @@ class TestUser < Test::Unit::TestCase
assert(user.requires?(ogroup), "User did not require other groups")
end
- if Process.uid == 0
- def test_simpleuser
- name = "pptest"
-
- assert(missing?(name), "User %s is present" % name)
-
- user = mkuser(name)
-
- @@tmpusers << name
-
- comp = newcomp("usercomp", user)
+ def test_simpleuser
+ name = "pptest"
- trans = assert_events([:user_created], comp, "user")
+ user = mkuser(name)
- assert_equal("Puppet Testing User", current?(:comment, user[:name]),
- "Comment was not set")
+ comp = newcomp("usercomp", user)
- assert_rollback_events(trans, [:user_removed], "user")
+ trans = assert_events([:user_created], comp, "user")
- assert(missing?(user[:name]))
- end
-
- def test_allstates
- user = nil
- name = "pptest"
+ assert_equal(user.should(:comment), user.provider.comment,
+ "Comment was not set correctly")
- assert(missing?(name), "User %s is present" % name)
+ assert_rollback_events(trans, [:user_removed], "user")
- user = mkuser(name)
+ assert(! user.provider.exists?, "User did not get deleted")
+ end
- @@tmpusers << name
+ def test_allusermodelstates
+ user = nil
+ name = "pptest"
- comp = newcomp("usercomp", user)
+ user = mkuser(name)
- trans = assert_events([:user_created], comp, "user")
+ assert(! user.provider.exists?, "User %s is present" % name)
- user.retrieve
- assert_equal("Puppet Testing User", current?(:comment, user[:name]),
- "Comment was not set")
+ comp = newcomp("usercomp", user)
- tests = Puppet.type(:user).validstates
+ trans = assert_events([:user_created], comp, "user")
- tests.each { |test|
- next unless test.to_s =~ /groups/
- if self.respond_to?("attrtest_%s" % test)
- self.send("attrtest_%s" % test, user)
- else
- Puppet.err "Not testing attr %s of user" % test
- end
- }
-
- user[:ensure] = :absent
- assert_apply(user)
- end
+ user.retrieve
+ assert_equal("Puppet Testing User", user.provider.comment,
+ "Comment was not set")
- def test_duplicateIDs
- user1 = mkuser("user1")
- user1[:uid] = 125
- user2 = mkuser("user2")
- user2[:uid] = 125
+ tests = Puppet.type(:user).validstates
- cleanup do
- user1[:ensure] = :absent
- user2[:ensure] = :absent
- assert_apply(user1, user2)
+ tests.each { |test|
+ if self.respond_to?("attrtest_%s" % test)
+ self.send("attrtest_%s" % test, user)
+ else
+ Puppet.err "Not testing attr %s of user" % test
end
+ }
- assert_apply(user1)
-
- # Not all OSes fail here, so we can't test that it doesn't work with
- # it off, only that it does work with it on.
- assert_nothing_raised {
- user2[:allowdupe] = true
- }
- assert_apply(user2)
- user2.retrieve
- assert_equal(:present, user2.state(:ensure).is,
- "User did not get created")
- end
- else
- $stderr.puts "Not root; skipping user creation/modification tests"
+ user[:ensure] = :absent
+ assert_apply(user)
end
end
+
+# $Id$