1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
|
require_relative '../../../puppet/util/package/version/range'
require_relative '../../../puppet/util/package/version/debian'
Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
# Provide sorting functionality
include Puppet::Util::Package
DebianVersion = Puppet::Util::Package::Version::Debian
VersionRange = Puppet::Util::Package::Version::Range
desc "Package management via `apt-get`.
This provider supports the `install_options` attribute, which allows command-line flags to be passed to apt-get.
These options should be specified as an array where each element is either a
string or a hash."
has_feature :versionable, :install_options, :virtual_packages
commands :aptget => "/usr/bin/apt-get"
commands :aptcache => "/usr/bin/apt-cache"
commands :aptmark => "/usr/bin/apt-mark"
commands :preseed => "/usr/bin/debconf-set-selections"
defaultfor :osfamily => :debian
ENV['DEBIAN_FRONTEND'] = "noninteractive"
# disable common apt helpers to allow non-interactive package installs
ENV['APT_LISTBUGS_FRONTEND'] = "none"
ENV['APT_LISTCHANGES_FRONTEND'] = "none"
def self.defaultto_allow_virtual
false
end
def self.instances
packages = super
manual_marks = aptmark('showmanual').split("\n")
packages.each do |package|
package.mark = :manual if manual_marks.include?(package.name)
end
packages
end
def query
hash = super
if !%i(absent purged).include?(hash[:ensure]) && aptmark('showmanual', @resource[:name]).strip == @resource[:name]
hash[:mark] = :manual
end
hash
end
def initialize(value={})
super(value)
@property_flush = {}
end
def mark
@property_flush[:mark]
end
def mark=(value)
@property_flush[:mark] = value
end
def flush
# unless we are removing the package mark it if it hasn't already been marked
if @property_flush
unless @property_flush[:mark] || [:purge, :absent].include?(resource[:ensure])
aptmark('manual', resource[:name])
end
end
end
# 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
have_cdrom = begin
!!(File.read("/etc/apt/sources.list") =~ /^[^#]*cdrom:/)
rescue
# This is basically pathological...
false
end
if have_cdrom and @resource[:allowcdrom] != :true
raise Puppet::Error,
_("/etc/apt/sources.list contains a cdrom source; not installing. Use 'allowcdrom' to override this failure.")
end
end
def best_version(should_range)
versions = []
output = aptcache :madison, @resource[:name]
output.each_line do |line|
is = line.split('|')[1].strip
begin
is_version = DebianVersion.parse(is)
versions << is_version if should_range.include?(is_version)
rescue DebianVersion::ValidationFailure
Puppet.debug("Cannot parse #{is} as a debian version")
end
end
return versions.sort.last if versions.any?
Puppet.debug("No available version for package #{@resource[:name]} is included in range #{should_range}")
should_range
end
# Install a package using 'apt-get'. This function needs to support
# installing a specific version.
def install
self.run_preseed if @resource[:responsefile]
should = @resource[:ensure]
if should.is_a?(String)
begin
should_range = VersionRange.parse(should, DebianVersion)
unless should_range.is_a?(VersionRange::Eq)
should = best_version(should_range)
end
rescue VersionRange::ValidationFailure, DebianVersion::ValidationFailure
Puppet.debug("Cannot parse #{should} as a debian version range, falling through")
end
end
checkforcdrom
cmd = %w{-q -y}
config = @resource[:configfiles]
if config
if config == :keep
cmd << "-o" << 'DPkg::Options::=--force-confold'
else
cmd << "-o" << 'DPkg::Options::=--force-confnew'
end
end
str = @resource[:name]
case should
when true, false, Symbol
# pass
else
# Add the package version and --force-yes option
str += "=#{should}"
cmd << "--force-yes"
end
cmd += install_options if @resource[:install_options]
cmd << :install
if source
cmd << source
else
cmd << str
end
self.unhold if self.properties[:mark] == :hold
begin
aptget(*cmd)
ensure
self.hold if @resource[:mark] == :hold
end
# If a source file was specified, we must make sure the expected version was installed from specified file
if source && !%i(present installed).include?(should)
is = self.query
raise Puppet::Error, _("Could not find package %{name}") % { name: self.name } unless is
version = is[:ensure]
raise Puppet::Error, _("Failed to update to version %{should}, got version %{version} instead") % { should: should, version: version } unless
insync?(version)
end
end
# What's the latest package version available?
def latest
output = aptcache :policy, @resource[:name]
if output =~ /Candidate:\s+(\S+)\s/
return $1
else
self.err _("Could not find latest version")
return nil
end
end
#
# preseeds answers to dpkg-set-selection from the "responsefile"
#
def run_preseed
response = @resource[:responsefile]
if response && Puppet::FileSystem.exist?(response)
self.info(_("Preseeding %{response} to debconf-set-selections") % { response: response })
preseed response
else
self.info _("No responsefile specified or non existent, not preseeding anything")
end
end
def uninstall
self.run_preseed if @resource[:responsefile]
args = ['-y', '-q']
args << '--allow-change-held-packages' if self.properties[:mark] == :hold
args << :remove << @resource[:name]
aptget(*args)
end
def purge
self.run_preseed if @resource[:responsefile]
args = ['-y', '-q']
args << '--allow-change-held-packages' if self.properties[:mark] == :hold
args << :remove << '--purge' << @resource[:name]
aptget(*args)
# workaround a "bug" in apt, that already removed packages are not purged
super
end
def install_options
join_options(@resource[:install_options])
end
def insync?(is)
# this is called after the generic version matching logic (insync? for the
# type), so we only get here if should != is
return false unless is && is != :absent
#if 'should' is a range and 'is' a debian version we should check if 'should' includes 'is'
should = @resource[:ensure]
return false unless is.is_a?(String) && should.is_a?(String)
begin
should_range = VersionRange.parse(should, DebianVersion)
rescue VersionRange::ValidationFailure, DebianVersion::ValidationFailure
Puppet.debug("Cannot parse #{should} as a debian version range")
return false
end
begin
is_version = DebianVersion.parse(is)
rescue DebianVersion::ValidationFailure
Puppet.debug("Cannot parse #{is} as a debian version")
return false
end
should_range.include?(is_version)
end
private
def source
@source ||= @resource[:source]
end
end
|