summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/type/package.rb289
-rw-r--r--test/types/tc_package.rb180
2 files changed, 388 insertions, 81 deletions
diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb
index c4ff4e243..b551ad65f 100644
--- a/lib/puppet/type/package.rb
+++ b/lib/puppet/type/package.rb
@@ -6,26 +6,55 @@ require 'puppet/type/state'
require 'puppet/fact'
module Puppet
+ class PackageError < Puppet::Error; end
class State
class PackageInstalled < Puppet::State
@name = :install
def retrieve
- #self.is = Puppet::PackageTyping[@object.format][@object.name]
- unless @parent.class.listed
- @parent.class.getpkglist
+ unless defined? @is
+ @parent.retrieve
+ end
+
+ # if the only requirement is that it's installed, then settle
+ # for that
+ if @should == true
+ if @is
+ @is = true
+ end
+ end
+ end
+
+ def should=(value)
+ # possible values are: true, false, and a version number
+ if value == true or value == false or value =~ /^[0-9]/
+ @should = value
+ else
+ raise Puppet::Error.new(
+ "Invalid install value %s" % value
+ )
end
- Puppet.debug "package install state is %s" % self.is
end
def sync
- #begin
- raise "cannot sync package states yet"
- #rescue
- # raise "failed to sync #{@params[:file]}: #{$!}"
- #end
+ type = @parent.pkgtype
+ begin
+ if @should == false
+ type.remove(@parent)
+ else
+ type.install(@parent)
+ end
+ rescue => detail
+ raise Puppet::Error.new(
+ "Could not install package %s: %s" % [@parent.name, detail]
+ )
+ end
- #return :package_installed
+ if @should == false
+ return :package_removed
+ else
+ return :package_installed
+ end
end
end
end
@@ -35,13 +64,15 @@ module Puppet
# different commands. We need some way to convert specific packages
# into the general package object...
class Package < Type
- attr_reader :version, :format
+ attr_reader :version, :pkgtype
+
@states = [
Puppet::State::PackageInstalled
]
@parameters = [
- :format,
:name,
+ :type,
+ :instance,
:status,
:version,
:category,
@@ -58,16 +89,57 @@ module Puppet
@allowedmethods = [:types]
@@types = nil
- def Package.clear
+ @@default = nil
+ @@platform = nil
+
+ class << self
+ attr_reader :listed
+ end
+
+ def self.clear
@listed = false
super
end
- def Package.listed
- return @listed
+ def self.default
+ if @@default.nil?
+ self.init
+ end
+
+ return @@default
+ end
+
+ def self.defaulttype
+ return Puppet::PackagingType[self.default]
+ end
+
+ def self.init
+ unless @@platform = Facter["operatingsystem"].value.downcase
+ raise Puppet::DevError(
+ "Must know platform for package management"
+ )
+ end
+ case @@platform
+ when "sunos": @@default = :sunpkg
+ when "linux":
+ case Facter["distro"].value.downcase
+ when "gentoo": raise "No support for gentoo yet"
+ when "debian": @@default = :apt
+ when "redhat", "fedora": @@default = :rpm
+ else
+ #raise "No default type for " + Puppet::Fact["distro"]
+ Puppet.warning "Using rpm as default type for %s" %
+ Facter["distro"].value
+ @@default = :rpm
+ end
+ else
+ raise Puppet::Error.new(
+ "No default type for " + Puppet::Fact["operatingsystem"]
+ )
+ end
end
- def Package.types(array)
+ def self.types(array)
unless array.is_a?(Array)
array = [array]
end
@@ -75,25 +147,12 @@ module Puppet
Puppet.debug "Types are %s" % array.join(" ")
end
- def Package.getpkglist
+ def self.getpkglist
if @@types.nil?
- case Puppet::Fact["operatingsystem"]
- when "SunOS": @@types = ["sunpkg"]
- when "Linux":
- case Puppet::Fact["distro"]
- when "Gentoo": raise "No support for gentoo yet"
- when "Debian": @@types = ["dpkg"]
- when "RedHat": @@types = ["rpm"]
- when "Fedora": @@types = ["rpm"]
- else
- #raise "No default type for " + Puppet::Fact["distro"]
- Puppet.warning "Using rpm as default type for %s" %
- Puppet::Fact["distro"]
- @@types = ["rpm"]
- end
- else
- raise "No default type for " + Puppet::Fact["operatingsystem"]
+ if @@default.nil?
+ self.init
end
+ @@types = [@@default]
end
list = @@types.collect { |type|
@@ -156,6 +215,14 @@ module Puppet
end
end
+ def self.pkgtype(pkgtype)
+ if @typeary.length != @typehash.length
+ self.buildpkgtypehash
+ end
+
+ return @typehash[pkgtype]
+ end
+
def addis(state,value)
if stateklass = self.class.validstate?(state)
@states[state] = stateklass.new(:parent => self)
@@ -173,14 +240,88 @@ module Puppet
# be set in 'should', or through comparing against the system, in which
# case the hash's values should be set in 'is'
def initialize(hash)
+ unless hash.include?(:install) or hash.include?("install")
+ Puppet.debug "Defaulting to installing a package"
+ hash[:install] = true
+ end
+
super
+
+ unless @parameters.include?(:type)
+ if @@default.nil?
+ self.class.init
+ end
+ self[:type] = @@default
+ end
end
+ def retrieve
+ unless pkgtype = Puppet::PackagingType[@parameters[:type]]
+ raise Puppet::DevError.new(
+ "No support for type %s" % @parameters[:type]
+ )
+ end
+
+ begin
+ hash = pkgtype.query(self)
+ rescue => error
+ Puppet.err "Cannot install %s: %s" %
+ [self.name, error.to_s]
+
+ @states.delete(:install)
+ end
+
+ if hash.nil?
+ @states[:install].is = nil
+ end
+
+ hash.each { |name,value|
+ if self.class.validstate?(name)
+ if @states.include?(name)
+ @states[name].is = value
+ else
+ # silently ignore any returned states
+ # that we're not managing
+ # this is highly unlikely to happen
+ Puppet.info "%s missing state %s" %
+ [self.name, name]
+ end
+ elsif self.class.validparameter?(name)
+ self[name] = value
+ else
+ # silently ignore anything that's not a valid state
+ # or param
+ end
+ }
+
+ # now let them all handle things as necessary
+ @states.each { |name, state|
+ state.retrieve
+ }
+ end
+
+ def paramtype=(typename)
+ @parameters[:type] = typename
+ pkgtype = nil
+
+ unless pkgtype = Puppet::PackagingType[typename]
+ raise Puppet::Error.new(
+ "Could not find package type %s" % typename
+ )
+ end
+
+ @pkgtype = pkgtype
+ end
end # Puppet::Type::Package
end
class PackagingType
- attr_writer :list, :install, :remove, :check
+ @params = [:list, :query, :remove, :install]
+ attr_writer(*@params)
+
+ class << self
+ attr_reader :params
+ end
@@types = Hash.new(false)
@@ -201,12 +342,12 @@ module Puppet
return @packages[name]
end
- [:list, :install, :remove, :check].each { |method|
- self.send(:define_method, method) {
+ @params.each { |method|
+ self.send(:define_method, method) { |pkg|
# retrieve the variable
var = eval("@" + method.id2name)
if var.is_a?(Proc)
- var.call()
+ var.call(pkg)
else
raise "only blocks are supported right now"
end
@@ -229,7 +370,60 @@ module Puppet
end
end
- PackagingType.new("dpkg") { |type|
+ PackagingType.new(:apt) { |type|
+ type.query = proc { |pkg|
+ 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 all of the packages
+ open("| dpkg -l %s 2>/dev/null" % pkg) { |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 "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[:install], hash[:error]]
+ )
+ end
+
+ if hash[:status] == "i"
+ hash[:install] = hash[:version]
+ else
+ # this isn't really correct, but we'll settle for it for now
+ hash[:install] = nil
+ end
+
+ return hash
+ }
type.list = proc {
packages = []
@@ -269,20 +463,27 @@ module Puppet
# we need package retrieval mechanisms before we can have package
# installation mechanisms...
- #type.install = proc { |pkg|
- # raise "installation not implemented yet"
- #}
+ type.install = proc { |pkg|
+ cmd = "apt-get install %s" % pkg
+
+ Puppet.info "Executing %s" % cmd.inspect
+ output = %x{#{cmd} 2>&1}
+
+ unless $? == 0
+ raise Puppet::PackageError.new(output)
+ end
+ }
type.remove = proc { |pkg|
cmd = "dpkg -r %s" % pkg.name
output = %x{#{cmd}}
if $? != 0
- raise output
+ raise Puppet::PackageError.new(output)
end
}
}
- PackagingType.new("rpm") { |type|
+ PackagingType.new(:rpm) { |type|
type.list = proc {
packages = []
@@ -333,7 +534,7 @@ module Puppet
}
}
- PackagingType.new("sunpkg") { |type|
+ PackagingType.new(:sunpkg) { |type|
type.list = proc {
packages = []
hash = {}
diff --git a/test/types/tc_package.rb b/test/types/tc_package.rb
index a5a180738..772ce30b0 100644
--- a/test/types/tc_package.rb
+++ b/test/types/tc_package.rb
@@ -4,6 +4,7 @@ if __FILE__ == $0
$puppetbase = "../../../../language/trunk"
end
+require 'puppettest'
require 'puppet'
require 'test/unit'
require 'facter'
@@ -12,33 +13,28 @@ Puppet[:loglevel] = :debug if __FILE__ == $0
# $Id$
+$platform = Facter["operatingsystem"].value
+
class TestPackagingType < Test::Unit::TestCase
+ def teardown
+ Puppet::Type::Package.clear
+ end
+
def test_listing
- platform = Facter["operatingsystem"].value
- type = nil
- case platform
- when "SunOS"
- type = "sunpkg"
- when "Linux"
- case Facter["distro"].value
- when "Debian": type = "dpkg"
- when "RedHat": type = "rpm"
- else
- #raise "No default type for " + Facter["distro"].to_s
- Puppet.warning "Defaulting to 'rpm' for packaging"
- type = "rpm"
- end
- else
- type = :invalid
- end
+ type = Puppet::Type::Package.defaulttype
+ assert(type)
- assert_nothing_raised() {
- Puppet::PackagingType[type].list
- }
+ #assert_nothing_raised() {
+ # type.list(nil)
+ #}
end
end
class TestPackageSource < Test::Unit::TestCase
+ def teardown
+ Puppet::Type::Package.clear
+ end
+
def test_filesource
system("touch /tmp/fakepackage")
assert_equal(
@@ -50,34 +46,144 @@ class TestPackageSource < Test::Unit::TestCase
end
class TestPackages < Test::Unit::TestCase
+ include FileTesting
def setup
- @list = Puppet::Type::Package.getpkglist
+ #@list = Puppet::Type::Package.getpkglist
+ Puppet::Type::Package.clear
end
def teardown
Puppet::Type::Package.clear
end
- def test_checking
- pkg = nil
- assert_nothing_raised() {
- pkg = @list[rand(@list.length)]
+ def mkpkgcomp(pkg)
+ assert_nothing_raised {
+ pkg = Puppet::Type::Package.new(:name => pkg, :install => true)
}
- assert(pkg[:install])
- assert(! pkg.state(:install).should)
- assert_nothing_raised() {
- pkg.evaluate
+ assert_nothing_raised {
+ pkg.retrieve
}
- assert_nothing_raised() {
- pkg[:install] = pkg[:install]
+
+ comp = newcomp("package", pkg)
+
+ return comp
+ end
+
+ def test_checking
+# pkg = nil
+# assert_nothing_raised() {
+# pkg = @list[rand(@list.length)]
+# }
+# assert(pkg[:install])
+# assert(! pkg.state(:install).should)
+# assert_nothing_raised() {
+# pkg.evaluate
+# }
+# assert_nothing_raised() {
+# pkg[:install] = pkg[:install]
+# }
+# assert_nothing_raised() {
+# pkg.evaluate
+# }
+# assert(pkg.insync?)
+# assert_nothing_raised() {
+# pkg[:install] = "1.2.3.4"
+# }
+# assert(!pkg.insync?)
+ end
+
+ def test_retrievepkg
+ pkg = nil
+
+ case $platform
+ #when "SunOS"
+ # type = "SMCossh"
+ when "Linux"
+ case Facter["distro"].value
+ when "Debian": pkg = "ssh"
+ #when "RedHat": type = :rpm
+ else
+ Puppet.notice "No test package for %s" % $platform
+ return
+ end
+ else
+ Puppet.notice "No test package for %s" % $platform
+ return
+ end
+
+ obj = nil
+ assert_nothing_raised {
+ obj = Puppet::Type::Package.new(
+ :name => pkg
+ )
}
- assert_nothing_raised() {
- pkg.evaluate
+
+ assert_nothing_raised {
+ obj.retrieve
}
- assert(pkg.insync?)
- assert_nothing_raised() {
- pkg[:install] = "1.2.3.4"
+ end
+
+ def test_zinstallpkg
+ unless Process.uid == 0
+ Puppet.notice "Test as root for installation tests"
+ return
+ end
+ pkgs = nil
+ case $platform
+ #when "SunOS"
+ # type = "sunpkg"
+ when "Linux"
+ case Facter["distro"].value
+ when "Debian": type = :apt
+ pkgs = %w{zec}
+ #when "RedHat": type = :rpm
+ else
+ Puppet.notice "No test packags for %s" % $platform
+ return
+ end
+ else
+ Puppet.notice "No test packags for %s" % $platform
+ return
+ end
+
+ pkgs.each { |pkg|
+ assert_nothing_raised {
+ pkg = Puppet::Type::Package.new(:name => pkg, :install => true)
+ }
+ assert_nothing_raised {
+ pkg.retrieve
+ }
+
+ if pkg.insync?
+ Puppet.notice "Test package %s is already installed; please choose a different package for testing" % pkg
+ next
+ end
+
+ comp = newcomp("package", pkg)
+ trans = nil
+ assert_nothing_raised {
+ trans = comp.evaluate
+ }
+ events = nil
+ assert_nothing_raised {
+ events = trans.evaluate.collect { |event| event.event }
+ }
+ assert_equal([:package_installed],events)
+
+ assert_nothing_raised {
+ pkg[:install] = false
+ }
+
+ assert_nothing_raised {
+ comp.retrieve
+ }
+ assert_nothing_raised {
+ trans = comp.evaluate
+ }
+ assert_nothing_raised {
+ events = trans.evaluate.collect { |event| event.event }
+ }
+ assert_equal([:package_removed],events)
}
- assert(!pkg.insync?)
end
end