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 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
|
# coding: utf-8
# Define the different packaging systems. Each package system is implemented
# in a module, which then gets used to individually extend each package object.
# This allows packages to exist on the same machine using different packaging
# systems.
require_relative '../../puppet/parameter/package_options'
require_relative '../../puppet/parameter/boolean'
module Puppet
Type.newtype(:package) do
@doc = "Manage packages. There is a basic dichotomy in package
support right now: Some package types (such as yum and apt) can
retrieve their own package files, while others (such as rpm and sun)
cannot. For those package formats that cannot retrieve their own files,
you can use the `source` parameter to point to the correct file.
Puppet will automatically guess the packaging format that you are
using based on the platform you are on, but you can override it
using the `provider` parameter; each provider defines what it
requires in order to function, and you must meet those requirements
to use a given provider.
You can declare multiple package resources with the same `name` as long
as they have unique titles, and specify different providers and commands.
Note that you must use the _title_ to make a reference to a package
resource; `Package[<NAME>]` is not a synonym for `Package[<TITLE>]` like
it is for many other resource types.
**Autorequires:** If Puppet is managing the files specified as a
package's `adminfile`, `responsefile`, or `source`, the package
resource will autorequire those files."
feature :reinstallable, "The provider can reinstall packages.",
:methods => [:reinstall]
feature :installable, "The provider can install packages.",
:methods => [:install]
feature :uninstallable, "The provider can uninstall packages.",
:methods => [:uninstall]
feature :upgradeable, "The provider can upgrade to the latest version of a
package. This feature is used by specifying `latest` as the
desired value for the package.",
:methods => [:update, :latest]
feature :purgeable, "The provider can purge packages. This generally means
that all traces of the package are removed, including
existing configuration files. This feature is thus destructive
and should be used with the utmost care.",
:methods => [:purge]
feature :versionable, "The provider is capable of interrogating the
package database for installed version(s), and can select
which out of a set of available versions of a package to
install if asked."
feature :version_ranges, "The provider can ensure version ranges."
feature :holdable, "The provider is capable of placing packages on hold
such that they are not automatically upgraded as a result of
other package dependencies unless explicit action is taken by
a user or another package.",
:methods => [:hold, :unhold]
feature :install_only, "The provider accepts options to only install packages never update (kernels, etc.)"
feature :install_options, "The provider accepts options to be
passed to the installer command."
feature :uninstall_options, "The provider accepts options to be
passed to the uninstaller command."
feature :disableable, "The provider can disable packages. This feature is used by specifying `disabled` as the
desired value for the package.",
:methods => [:disable]
feature :supports_flavors, "The provider accepts flavors, which are specific variants of packages."
feature :package_settings, "The provider accepts package_settings to be
ensured for the given package. The meaning and format of these settings is
provider-specific.",
:methods => [:package_settings_insync?, :package_settings, :package_settings=]
feature :virtual_packages, "The provider accepts virtual package names for install and uninstall."
feature :targetable, "The provider accepts a targeted package management command."
ensurable do
desc <<-EOT
What state the package should be in. On packaging systems that can
retrieve new packages on their own, you can choose which package to
retrieve by specifying a version number or `latest` as the ensure
value. On packaging systems that manage configuration files separately
from "normal" system files, you can uninstall config files by
specifying `purged` as the ensure value. This defaults to `installed`.
Version numbers must match the full version to install, including
release if the provider uses a release moniker. For
example, to install the bash package from the rpm
`bash-4.1.2-29.el6.x86_64.rpm`, use the string `'4.1.2-29.el6'`.
On supported providers, version ranges can also be ensured. For example,
inequalities: `<2.0.0`, or intersections: `>1.0.0 <2.0.0`.
EOT
attr_accessor :latest
newvalue(:present, :event => :package_installed) do
provider.install
end
newvalue(:absent, :event => :package_removed) do
provider.uninstall
end
newvalue(:purged, :event => :package_purged, :required_features => :purgeable) do
provider.purge
end
newvalue(:disabled, :required_features => :disableable) do
provider.disable
end
# Alias the 'present' value.
aliasvalue(:installed, :present)
newvalue(:latest, :required_features => :upgradeable) do
# Because yum always exits with a 0 exit code, there's a retrieve
# in the "install" method. So, check the current state now,
# to compare against later.
current = self.retrieve
begin
provider.update
rescue => detail
self.fail Puppet::Error, _("Could not update: %{detail}") % { detail: detail }, detail
end
if current == :absent
:package_installed
else
:package_changed
end
end
newvalue(/./, :required_features => :versionable) do
begin
provider.install
rescue => detail
self.fail Puppet::Error, _("Could not update: %{detail}") % { detail: detail }, detail
end
if self.retrieve == :absent
:package_installed
else
:package_changed
end
end
defaultto :installed
# Override the parent method, because we've got all kinds of
# funky definitions of 'in sync'.
def insync?(is)
@lateststamp ||= (Time.now.to_i - 1000)
# Iterate across all of the should values, and see how they
# turn out.
@should.each { |should|
case should
when :present
return true unless [:absent, :purged, :disabled].include?(is)
when :latest
# Short-circuit packages that are not present
return false if is == :absent || is == :purged
# Don't run 'latest' more than about every 5 minutes
if @latest and ((Time.now.to_i - @lateststamp) / 60) < 5
#self.debug "Skipping latest check"
else
begin
@latest = provider.latest
@lateststamp = Time.now.to_i
rescue => detail
error = Puppet::Error.new(_("Could not get latest version: %{detail}") % { detail: detail })
error.set_backtrace(detail.backtrace)
raise error
end
end
case
when is.is_a?(Array) && is.include?(@latest)
return true
when is == @latest
return true
when is == :present
# This will only happen on packaging systems
# that can't query versions.
return true
else
self.debug "#{@resource.name} #{is.inspect} is installed, latest is #{@latest.inspect}"
end
when :absent
return true if is == :absent || is == :purged
when :purged
return true if is == :purged
# this handles version number matches and
# supports providers that can have multiple versions installed
when *Array(is)
return true
else
# We have version numbers, and no match. If the provider has
# additional logic, run it here.
return provider.insync?(is) if provider.respond_to?(:insync?)
end
}
false
end
# This retrieves the current state. LAK: I think this method is unused.
def retrieve
provider.properties[:ensure]
end
# Provide a bit more information when logging upgrades.
def should_to_s(newvalue = @should)
if @latest
super(@latest)
else
super(newvalue)
end
end
def change_to_s(currentvalue, newvalue)
# Handle transitioning from any previous state to 'purged'
return 'purged' if newvalue == :purged
# Check for transitions from nil/purged/absent to 'created' (any state that is not absent and not purged)
return 'created' if (currentvalue.nil? || currentvalue == :absent || currentvalue == :purged) && (newvalue != :absent && newvalue != :purged)
# The base should handle the normal property transitions
super(currentvalue, newvalue)
end
end
newparam(:name) do
desc "The package name. This is the name that the packaging
system uses internally, which is sometimes (especially on Solaris)
a name that is basically useless to humans. If a package goes by
several names, you can use a single title and then set the name
conditionally:
# In the 'openssl' class
$ssl = $operatingsystem ? {
solaris => SMCossl,
default => openssl
}
package { 'openssl':
ensure => installed,
name => $ssl,
}
...
$ssh = $operatingsystem ? {
solaris => SMCossh,
default => openssh
}
package { 'openssh':
ensure => installed,
name => $ssh,
require => Package['openssl'],
}
"
isnamevar
validate do |value|
if !value.is_a?(String)
raise ArgumentError, _("Name must be a String not %{klass}") % { klass: value.class }
end
end
end
# We call providify here so that we can set provider as a namevar.
# Normally this method is called after newtype finishes constructing this
# Type class.
providify
paramclass(:provider).isnamevar
def self.parameters_to_include
[:provider]
end
# Specify a targeted package management command.
newparam(:command, :required_features => :targetable) do
desc <<-EOT
The targeted command to use when managing a package:
package { 'mysql':
provider => gem,
}
package { 'mysql-opt':
name => 'mysql',
provider => gem,
command => '/opt/ruby/bin/gem',
}
Each provider defines a package management command; and uses the first
instance of the command found in the PATH.
Providers supporting the targetable feature allow you to specify the
absolute path of the package management command; useful when multiple
instances of the command are installed, or the command is not in the PATH.
EOT
isnamevar
defaultto :default
end
# We have more than one namevar, so we need title_patterns.
# However, we cheat and set the patterns to map to name only
# and completely ignore provider (and command, for targetable providers).
# So far, the logic that determines uniqueness appears to just
# "Do The Right Thing™" when provider (and command) are explicitly set.
#
# The following resources will be seen as unique by puppet:
#
# # Uniqueness Key: ['mysql', nil]
# package {'mysql': }
#
# # Uniqueness Key: ['mysql', 'gem', nil]
# package {'gem-mysql':
# name => 'mysql,
# provider => gem,
# }
#
# # Uniqueness Key: ['mysql', 'gem', '/opt/ruby/bin/gem']
# package {'gem-mysql-opt':
# name => 'mysql,
# provider => gem
# command => '/opt/ruby/bin/gem',
# }
#
# This does not handle the case where providers like 'yum' and 'rpm' should
# clash. Also, declarations that implicitly use the default provider will
# clash with those that explicitly use the default.
def self.title_patterns
# This is the default title pattern for all types, except hard-wired to
# set only name.
[ [ /(.*)/m, [ [:name] ] ] ]
end
newproperty(:package_settings, :required_features=>:package_settings) do
desc "Settings that can change the contents or configuration of a package.
The formatting and effects of package_settings are provider-specific; any
provider that implements them must explain how to use them in its
documentation. (Our general expectation is that if a package is
installed but its settings are out of sync, the provider should
re-install that package with the desired settings.)
An example of how package_settings could be used is FreeBSD's port build
options --- a future version of the provider could accept a hash of options,
and would reinstall the port if the installed version lacked the correct
settings.
package { 'www/apache22':
package_settings => { 'SUEXEC' => false }
}
Again, check the documentation of your platform's package provider to see
the actual usage."
validate do |value|
if provider.respond_to?(:package_settings_validate)
provider.package_settings_validate(value)
else
super(value)
end
end
munge do |value|
if provider.respond_to?(:package_settings_munge)
provider.package_settings_munge(value)
else
super(value)
end
end
def insync?(is)
provider.package_settings_insync?(should, is)
end
def should_to_s(newvalue)
if provider.respond_to?(:package_settings_should_to_s)
provider.package_settings_should_to_s(should, newvalue)
else
super(newvalue)
end
end
def is_to_s(currentvalue)
if provider.respond_to?(:package_settings_is_to_s)
provider.package_settings_is_to_s(should, currentvalue)
else
super(currentvalue)
end
end
def change_to_s(currentvalue, newvalue)
if provider.respond_to?(:package_settings_change_to_s)
provider.package_settings_change_to_s(currentvalue, newvalue)
else
super(currentvalue,newvalue)
end
end
end
newproperty(:flavor, :required_features => :supports_flavors) do
desc "OpenBSD and DNF modules support 'flavors', which are
further specifications for which type of package you want."
validate do |value|
if [:disabled, "disabled"].include?(@resource[:ensure]) && value
raise ArgumentError, _('Cannot have both `ensure => disabled` and `flavor`')
end
end
end
newparam(:source) do
desc "Where to find the package file. This is mostly used by providers that don't
automatically download packages from a central repository. (For example:
the `yum` provider ignores this attribute, `apt` provider uses it if present
and the `rpm` and `dpkg` providers require it.)
Different providers accept different values for `source`. Most providers
accept paths to local files stored on the target system. Some providers
may also accept URLs or network drive paths. Puppet will not
automatically retrieve source files for you, and usually just passes the
value of `source` to the package installation command.
You can use a `file` resource if you need to manually copy package files
to the target system."
validate do |value|
provider.validate_source(value)
end
end
newparam(:instance) do
desc "A read-only parameter set by the package."
end
newparam(:status) do
desc "A read-only parameter set by the package."
end
newparam(:adminfile) do
desc "A file containing package defaults for installing packages.
This attribute is only used on Solaris. Its value should be a path to a
local file stored on the target system. Solaris's package tools expect
either an absolute file path or a relative path to a file in
`/var/sadm/install/admin`.
The value of `adminfile` will be passed directly to the `pkgadd` or
`pkgrm` command with the `-a <ADMINFILE>` option."
end
newparam(:responsefile) do
desc "A file containing any necessary answers to questions asked by
the package. This is currently used on Solaris and Debian. The
value will be validated according to system rules, but it should
generally be a fully qualified path."
end
newparam(:configfiles) do
desc "Whether to keep or replace modified config files when installing or
upgrading a package. This only affects the `apt` and `dpkg` providers."
defaultto :keep
newvalues(:keep, :replace)
end
newparam(:category) do
desc "A read-only parameter set by the package."
end
newparam(:platform) do
desc "A read-only parameter set by the package."
end
newparam(:root) do
desc "A read-only parameter set by the package."
end
newparam(:vendor) do
desc "A read-only parameter set by the package."
end
newparam(:description) do
desc "A read-only parameter set by the package."
end
newparam(:allowcdrom) do
desc "Tells apt to allow cdrom sources in the sources.list file.
Normally apt will bail if you try this."
newvalues(:true, :false)
end
newparam(:enable_only, :boolean => false, :parent => Puppet::Parameter::Boolean) do
desc <<-EOT
Tells `dnf module` to only enable a specific module, instead
of installing its default profile.
Modules with no default profile will be enabled automatically
without the use of this parameter.
Conflicts with the `flavor` property, which selects a profile
to install.
EOT
defaultto false
validate do |value|
if [true, :true, "true"].include?(value) && @resource[:flavor]
raise ArgumentError, _('Cannot have both `enable_only => true` and `flavor`')
end
if [:disabled, "disabled"].include?(@resource[:ensure])
raise ArgumentError, _('Cannot have both `ensure => disabled` and `enable_only => true`')
end
end
end
newparam(:install_only, :boolean => false, :parent => Puppet::Parameter::Boolean, :required_features => :install_only) do
desc <<-EOT
It should be set for packages that should only ever be installed,
never updated. Kernels in particular fall into this category.
EOT
defaultto false
end
newparam(:install_options, :parent => Puppet::Parameter::PackageOptions, :required_features => :install_options) do
desc <<-EOT
An array of additional options to pass when installing a package. These
options are package-specific, and should be documented by the software
vendor. One commonly implemented option is `INSTALLDIR`:
package { 'mysql':
ensure => installed,
source => 'N:/packages/mysql-5.5.16-winx64.msi',
install_options => [ '/S', { 'INSTALLDIR' => 'C:\\mysql-5.5' } ],
}
Each option in the array can either be a string or a hash, where each
key and value pair are interpreted in a provider specific way. Each
option will automatically be quoted when passed to the install command.
With Windows packages, note that file paths in an install option must
use backslashes. (Since install options are passed directly to the
installation command, forward slashes won't be automatically converted
like they are in `file` resources.) Note also that backslashes in
double-quoted strings _must_ be escaped and backslashes in single-quoted
strings _can_ be escaped.
EOT
end
newparam(:uninstall_options, :parent => Puppet::Parameter::PackageOptions, :required_features => :uninstall_options) do
desc <<-EOT
An array of additional options to pass when uninstalling a package. These
options are package-specific, and should be documented by the software
vendor. For example:
package { 'VMware Tools':
ensure => absent,
uninstall_options => [ { 'REMOVE' => 'Sync,VSS' } ],
}
Each option in the array can either be a string or a hash, where each
key and value pair are interpreted in a provider specific way. Each
option will automatically be quoted when passed to the uninstall
command.
On Windows, this is the **only** place in Puppet where backslash
separators should be used. Note that backslashes in double-quoted
strings _must_ be double-escaped and backslashes in single-quoted
strings _may_ be double-escaped.
EOT
end
newparam(:allow_virtual, :boolean => true, :parent => Puppet::Parameter::Boolean, :required_features => :virtual_packages) do
desc 'Specifies if virtual package names are allowed for install and uninstall.'
defaultto do
provider_class = provider.class
if provider_class.respond_to?(:defaultto_allow_virtual)
provider_class.defaultto_allow_virtual
else
true
end
end
end
autorequire(:file) do
autos = []
[:responsefile, :adminfile].each { |param|
val = self[param]
if val
autos << val
end
}
source = self[:source]
if source && absolute_path?(source)
autos << source
end
autos
end
# This only exists for testing.
def clear
obj = @parameters[:ensure]
if obj
obj.latest = nil
end
end
# The 'query' method returns a hash of info if the package
# exists and returns nil if it does not.
def exists?
@provider.get(:ensure) != :absent
end
def present?(current_values)
super && current_values[:ensure] != :purged
end
# This parameter exists to ensure backwards compatibility is preserved.
# See https://github.com/puppetlabs/puppet/pull/2614 for discussion.
# If/when a metaparameter for controlling how arbitrary resources respond
# to refreshing is created, that will supersede this, and this will be
# deprecated.
newparam(:reinstall_on_refresh) do
desc "Whether this resource should respond to refresh events (via `subscribe`,
`notify`, or the `~>` arrow) by reinstalling the package. Only works for
providers that support the `reinstallable` feature.
This is useful for source-based distributions, where you may want to
recompile a package if the build options change.
If you use this, be careful of notifying classes when you want to restart
services. If the class also contains a refreshable package, doing so could
cause unnecessary re-installs."
newvalues(:true, :false)
defaultto :false
end
# When a refresh event is triggered, calls reinstall on providers
# that support the reinstall_on_refresh parameter.
def refresh
if provider.reinstallable? &&
@parameters[:reinstall_on_refresh].value == :true &&
@parameters[:ensure].value != :purged &&
@parameters[:ensure].value != :absent
provider.reinstall
end
end
newproperty(:mark, :required_features => :holdable) do
mark_doc='Valid values are: hold/none'
desc <<-EOT
Set to hold to tell Debian apt/Solaris pkg to hold the package version
#{mark_doc}
Default is "none". Mark can be specified with or without `ensure`,
if `ensure` is missing will default to "present".
Mark cannot be specified together with "purged", or "absent"
values for `ensure`.
EOT
newvalues(:hold, :none)
munge do |value|
case value
when "hold", :hold
:hold
when "none", :none
:none
else
raise ArgumentError, _('Invalid hold value %{value}. %{doc}') % { value: value.inspect, doc: mark_doc}
end
end
def insync?(is)
@should[0] == is
end
def should
@should[0] if @should && @should.is_a?(Array) && @should.size == 1
end
def retrieve
provider.properties[:mark]
end
def sync
if @should[0] == :hold
provider.hold
else
provider.unhold
end
end
end
validate do
if @parameters[:mark] && [:absent, :purged].include?(@parameters[:ensure].should)
raise ArgumentError, _('You cannot use "mark" property while "ensure" is one of ["absent", "purged"]')
end
end
end
end
|