diff options
author | Luke Kanies <luke@madstop.com> | 2005-07-27 00:02:39 +0000 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2005-07-27 00:02:39 +0000 |
commit | 700b9650b8188bc9bf36b66e9ee1f78324fd0c16 (patch) | |
tree | f422a4ce4916225cb6ed5477a488baa6589280d7 | |
parent | e685ddb63aec1c6652e289125178ae43106f4041 (diff) | |
download | puppet-700b9650b8188bc9bf36b66e9ee1f78324fd0c16.tar.gz puppet-700b9650b8188bc9bf36b66e9ee1f78324fd0c16.tar.xz puppet-700b9650b8188bc9bf36b66e9ee1f78324fd0c16.zip |
dpkg/apt package installation and removal now works
git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@465 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r-- | lib/puppet/type/package.rb | 289 | ||||
-rw-r--r-- | test/types/tc_package.rb | 180 |
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 |