summaryrefslogtreecommitdiff
path: root/lib/chef/provider/package.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/provider/package.rb')
-rw-r--r--lib/chef/provider/package.rb163
1 files changed, 118 insertions, 45 deletions
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 2e8e29981b..ba256f6bea 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,17 +16,24 @@
# limitations under the License.
#
-require 'chef/mixin/shell_out'
-require 'chef/mixin/command'
-require 'chef/log'
-require 'chef/file_cache'
-require 'chef/platform'
+require "chef/mixin/shell_out"
+require "chef/mixin/command"
+require "chef/mixin/subclass_directive"
+require "chef/log"
+require "chef/file_cache"
+require "chef/platform"
class Chef
class Provider
class Package < Chef::Provider
include Chef::Mixin::Command
include Chef::Mixin::ShellOut
+ extend Chef::Mixin::SubclassDirective
+
+ # subclasses declare this if they want all their arguments as arrays of packages and names
+ subclass_directive :use_multipackage_api
+ # subclasses declare this if they want sources (filenames) pulled from their package names
+ subclass_directive :use_package_name_for_source
#
# Hook that subclasses use to populate the candidate_version(s)
@@ -43,6 +50,14 @@ class Chef
true
end
+ def check_resource_semantics!
+ # FIXME: this is not universally true and subclasses are needing to override this and no-ops it. It should be turned into
+ # another "subclass_directive" and the apt and yum providers should declare that they need this behavior.
+ if new_resource.package_name.is_a?(Array) && new_resource.source != nil
+ raise Chef::Exceptions::InvalidResourceSpecification, "You may not specify both multipackage and source"
+ end
+ end
+
def load_current_resource
end
@@ -80,11 +95,10 @@ class Chef
end
end
- # XXX: mutating the new resource is generally bad
- @new_resource.version(versions_for_new_resource)
-
converge_by(install_description) do
- install_package(package_names_for_targets, versions_for_targets)
+ multipackage_api_adapter(package_names_for_targets, versions_for_targets) do |name, version|
+ install_package(name, version)
+ end
Chef::Log.info("#{@new_resource} installed #{package_names_for_targets} at #{versions_for_targets}")
end
end
@@ -107,18 +121,17 @@ class Chef
return
end
- # XXX: mutating the new resource is generally bad
- @new_resource.version(versions_for_new_resource)
-
converge_by(upgrade_description) do
- upgrade_package(package_names_for_targets, versions_for_targets)
- log_allow_downgrade = allow_downgrade ? '(allow_downgrade)' : ''
+ multipackage_api_adapter(package_names_for_targets, versions_for_targets) do |name, version|
+ upgrade_package(name, version)
+ end
+ log_allow_downgrade = allow_downgrade ? "(allow_downgrade)" : ""
Chef::Log.info("#{@new_resource} upgraded#{log_allow_downgrade} #{package_names_for_targets} to #{versions_for_targets}")
end
end
def upgrade_description
- log_allow_downgrade = allow_downgrade ? '(allow_downgrade)' : ''
+ log_allow_downgrade = allow_downgrade ? "(allow_downgrade)" : ""
description = []
target_version_array.each_with_index do |target_version, i|
next if target_version.nil?
@@ -132,12 +145,13 @@ class Chef
private :upgrade_description
- # @todo: ability to remove an array of packages
def action_remove
if removing_package?
description = @new_resource.version ? "version #{@new_resource.version} of " : ""
- converge_by("remove #{description} package #{@current_resource.package_name}") do
- remove_package(@current_resource.package_name, @new_resource.version)
+ converge_by("remove #{description}package #{@current_resource.package_name}") do
+ multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version|
+ remove_package(name, version)
+ end
Chef::Log.info("#{@new_resource} removed")
end
else
@@ -166,18 +180,18 @@ class Chef
end
end
- # @todo: ability to purge an array of packages
def action_purge
if removing_package?
description = @new_resource.version ? "version #{@new_resource.version} of" : ""
converge_by("purge #{description} package #{@current_resource.package_name}") do
- purge_package(@current_resource.package_name, @new_resource.version)
+ multipackage_api_adapter(@current_resource.package_name, @new_resource.version) do |name, version|
+ purge_package(name, version)
+ end
Chef::Log.info("#{@new_resource} purged")
end
end
end
- # @todo: ability to reconfigure an array of packages
def action_reconfig
if @current_resource.version == nil then
Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do")
@@ -192,7 +206,10 @@ class Chef
if preseed_file = get_preseed_file(@new_resource.package_name, @current_resource.version)
converge_by("reconfigure package #{@new_resource.package_name}") do
preseed_package(preseed_file)
- reconfig_package(@new_resource.package_name, @current_resource.version)
+ multipackage_api_adapter(@new_resource.package_name, @current_resource.version) do |name, version|
+ reconfig_package(name, version)
+
+ end
Chef::Log.info("#{@new_resource} reconfigured")
end
else
@@ -201,31 +218,40 @@ class Chef
end
# @todo use composition rather than inheritance
+
+ def multipackage_api_adapter(name, version)
+ if use_multipackage_api?
+ yield [name].flatten, [version].flatten
+ else
+ yield name, version
+ end
+ end
+
def install_package(name, version)
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :install"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :install"
end
def upgrade_package(name, version)
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :upgrade"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :upgrade"
end
def remove_package(name, version)
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remove"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :remove"
end
def purge_package(name, version)
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :purge"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :purge"
end
def preseed_package(file)
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support pre-seeding package install/upgrade instructions"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support pre-seeding package install/upgrade instructions"
end
def reconfig_package(name, version)
- raise( Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reconfig" )
+ raise( Chef::Exceptions::UnsupportedAction, "#{self} does not support :reconfig" )
end
- # this is heavily used by subclasses
+ # used by subclasses. deprecated. use #a_to_s instead.
def expand_options(options)
options ? " #{options}" : ""
end
@@ -316,18 +342,6 @@ class Chef
multipackage? ? versions_for_targets : versions_for_targets[0]
end
- # We need to mutate @new_resource.version() for some reason and this is a helper so that we inject the right
- # class (String or Array) into that attribute based on if we're handling an array of package names or not.
- #
- # @return [String, Array<String>] target_versions coerced into the correct type for back-compat
- def versions_for_new_resource
- if multipackage?
- target_version_array
- else
- target_version_array[0]
- end
- end
-
# Return an array indexed the same as *_version_array which contains either the target version to install/upgrade to
# or else nil if the package is not being modified.
#
@@ -464,10 +478,38 @@ class Chef
# @return [Array] new_version(s) as an array
def new_version_array
- @new_version_array ||=
- [ new_resource.version ].flatten.map do |v|
- ( v.nil? || v.empty? ) ? nil : v
+ [ new_resource.version ].flatten.map { |v| v.to_s.empty? ? nil : v }
+ end
+
+ # TIP: less error prone to simply always call resolved_source_array, even if you
+ # don't think that you need to.
+ #
+ # @return [Array] new_resource.source as an array
+ def source_array
+ if new_resource.source.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.source ].flatten
+ end
+ end
+
+ # Helper to handle use_package_name_for_source to convert names into local packages to install.
+ #
+ # @return [Array] Array of sources with package_names converted to sources
+ def resolved_source_array
+ @resolved_source_array ||=
+ begin
+ source_array.each_with_index.map do |source, i|
+ package_name = package_name_array[i]
+ # we require at least one '/' in the package_name to avoid [XXX_]package 'foo' breaking due to a random 'foo' file in cwd
+ if use_package_name_for_source? && source.nil? && package_name.match(/#{::File::SEPARATOR}/) && ::File.exist?(package_name)
+ Chef::Log.debug("No package source specified, but #{package_name} exists on filesystem, using #{package_name} as source.")
+ package_name
+ else
+ source
+ end
end
+ end
end
# @todo: extract apt/dpkg specific preseeding to a helper class
@@ -487,6 +529,37 @@ class Chef
false
end
end
+
+ def shell_out_with_timeout(*command_args)
+ shell_out(*add_timeout_option(command_args))
+ end
+
+ def shell_out_with_timeout!(*command_args)
+ shell_out!(*add_timeout_option(command_args))
+ end
+
+ def add_timeout_option(command_args)
+ args = command_args.dup
+ if args.last.is_a?(Hash)
+ options = args.pop.dup
+ options[:timeout] = new_resource.timeout if new_resource.timeout
+ options[:timeout] = 900 unless options.has_key?(:timeout)
+ args << options
+ else
+ args << { :timeout => new_resource.timeout ? new_resource.timeout : 900 }
+ end
+ args
+ end
+
+ # Helper for sublcasses to convert an array of string args into a string. It
+ # will compact nil or empty strings in the array and will join the array elements
+ # with spaces, without introducing any double spaces for nil/empty elements.
+ #
+ # @param args [String] variable number of string arguments
+ # @return [String] nicely concatenated string or empty string
+ def a_to_s(*args)
+ args.reject {|i| i.nil? || i == "" }.join(" ")
+ end
end
end
end