summaryrefslogtreecommitdiff
path: root/lib/chef/provider
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/provider')
-rw-r--r--lib/chef/provider/batch.rb14
-rw-r--r--lib/chef/provider/cookbook_file.rb6
-rw-r--r--lib/chef/provider/cookbook_file/content.rb4
-rw-r--r--lib/chef/provider/cron.rb8
-rw-r--r--lib/chef/provider/cron/unix.rb9
-rw-r--r--lib/chef/provider/deploy.rb16
-rw-r--r--lib/chef/provider/deploy/revision.rb6
-rw-r--r--lib/chef/provider/directory.rb31
-rw-r--r--lib/chef/provider/dsc_resource.rb97
-rw-r--r--lib/chef/provider/dsc_script.rb26
-rw-r--r--lib/chef/provider/env.rb32
-rw-r--r--lib/chef/provider/env/windows.rb4
-rw-r--r--lib/chef/provider/erl_call.rb8
-rw-r--r--lib/chef/provider/execute.rb35
-rw-r--r--lib/chef/provider/file.rb43
-rw-r--r--lib/chef/provider/file/content.rb4
-rw-r--r--lib/chef/provider/git.rb42
-rw-r--r--lib/chef/provider/group.rb10
-rw-r--r--lib/chef/provider/group/aix.rb7
-rw-r--r--lib/chef/provider/group/dscl.rb8
-rw-r--r--lib/chef/provider/group/gpasswd.rb3
-rw-r--r--lib/chef/provider/group/groupadd.rb10
-rw-r--r--lib/chef/provider/group/pw.rb3
-rw-r--r--lib/chef/provider/group/suse.rb4
-rw-r--r--lib/chef/provider/group/usermod.rb13
-rw-r--r--lib/chef/provider/group/windows.rb4
-rw-r--r--lib/chef/provider/http_request.rb14
-rw-r--r--lib/chef/provider/ifconfig.rb30
-rw-r--r--lib/chef/provider/ifconfig/aix.rb3
-rw-r--r--lib/chef/provider/ifconfig/debian.rb6
-rw-r--r--lib/chef/provider/ifconfig/redhat.rb3
-rw-r--r--lib/chef/provider/link.rb20
-rw-r--r--lib/chef/provider/lwrp_base.rb153
-rw-r--r--lib/chef/provider/mdadm.rb4
-rw-r--r--lib/chef/provider/mount.rb31
-rw-r--r--lib/chef/provider/mount/aix.rb21
-rw-r--r--lib/chef/provider/mount/mount.rb30
-rw-r--r--lib/chef/provider/mount/solaris.rb56
-rw-r--r--lib/chef/provider/mount/windows.rb6
-rw-r--r--lib/chef/provider/ohai.rb3
-rw-r--r--lib/chef/provider/osx_profile.rb254
-rw-r--r--lib/chef/provider/package.rb163
-rw-r--r--lib/chef/provider/package/aix.rb25
-rw-r--r--lib/chef/provider/package/apt.rb27
-rw-r--r--lib/chef/provider/package/chocolatey.rb257
-rw-r--r--lib/chef/provider/package/dpkg.rb231
-rw-r--r--lib/chef/provider/package/easy_install.rb23
-rw-r--r--lib/chef/provider/package/freebsd/base.rb10
-rw-r--r--lib/chef/provider/package/freebsd/pkg.rb16
-rw-r--r--lib/chef/provider/package/freebsd/pkgng.rb14
-rw-r--r--lib/chef/provider/package/freebsd/port.rb10
-rw-r--r--lib/chef/provider/package/homebrew.rb29
-rw-r--r--lib/chef/provider/package/ips.rb19
-rw-r--r--lib/chef/provider/package/macports.rb12
-rw-r--r--lib/chef/provider/package/openbsd.rb34
-rw-r--r--lib/chef/provider/package/pacman.rb19
-rw-r--r--lib/chef/provider/package/paludis.rb14
-rw-r--r--lib/chef/provider/package/portage.rb18
-rw-r--r--lib/chef/provider/package/rpm.rb30
-rw-r--r--lib/chef/provider/package/rubygems.rb113
-rw-r--r--lib/chef/provider/package/smartos.rb22
-rw-r--r--lib/chef/provider/package/solaris.rb31
-rw-r--r--lib/chef/provider/package/windows.rb216
-rw-r--r--lib/chef/provider/package/windows/exe.rb117
-rw-r--r--lib/chef/provider/package/windows/msi.rb58
-rw-r--r--lib/chef/provider/package/windows/registry_uninstall_entry.rb89
-rw-r--r--lib/chef/provider/package/yum.rb193
-rw-r--r--lib/chef/provider/package/zypper.rb42
-rw-r--r--lib/chef/provider/powershell_script.rb215
-rw-r--r--lib/chef/provider/reboot.rb7
-rw-r--r--lib/chef/provider/registry_key.rb30
-rw-r--r--lib/chef/provider/remote_directory.rb300
-rw-r--r--lib/chef/provider/remote_file.rb7
-rw-r--r--lib/chef/provider/remote_file/cache_control_data.rb51
-rw-r--r--lib/chef/provider/remote_file/content.rb19
-rw-r--r--lib/chef/provider/remote_file/fetcher.rb30
-rw-r--r--lib/chef/provider/remote_file/ftp.rb20
-rw-r--r--lib/chef/provider/remote_file/http.rb14
-rw-r--r--lib/chef/provider/remote_file/local_file.rb20
-rw-r--r--lib/chef/provider/remote_file/network_file.rb48
-rw-r--r--lib/chef/provider/route.rb84
-rw-r--r--lib/chef/provider/script.rb7
-rw-r--r--lib/chef/provider/service.rb81
-rw-r--r--lib/chef/provider/service/aix.rb27
-rw-r--r--lib/chef/provider/service/aixinit.rb16
-rw-r--r--lib/chef/provider/service/arch.rb4
-rw-r--r--lib/chef/provider/service/debian.rb14
-rw-r--r--lib/chef/provider/service/freebsd.rb14
-rw-r--r--lib/chef/provider/service/gentoo.rb12
-rw-r--r--lib/chef/provider/service/init.rb11
-rw-r--r--lib/chef/provider/service/insserv.rb10
-rw-r--r--lib/chef/provider/service/invokercd.rb8
-rw-r--r--lib/chef/provider/service/macosx.rb114
-rw-r--r--lib/chef/provider/service/openbsd.rb29
-rw-r--r--lib/chef/provider/service/redhat.rb72
-rw-r--r--lib/chef/provider/service/simple.rb18
-rw-r--r--lib/chef/provider/service/solaris.rb66
-rw-r--r--lib/chef/provider/service/systemd.rb14
-rw-r--r--lib/chef/provider/service/upstart.rb25
-rw-r--r--lib/chef/provider/service/windows.rb102
-rw-r--r--lib/chef/provider/subversion.rb32
-rw-r--r--lib/chef/provider/template.rb8
-rw-r--r--lib/chef/provider/template/content.rb26
-rw-r--r--lib/chef/provider/user.rb14
-rw-r--r--lib/chef/provider/user/aix.rb5
-rw-r--r--lib/chef/provider/user/dscl.rb243
-rw-r--r--lib/chef/provider/user/pw.rb13
-rw-r--r--lib/chef/provider/user/solaris.rb38
-rw-r--r--lib/chef/provider/user/useradd.rb13
-rw-r--r--lib/chef/provider/user/windows.rb24
-rw-r--r--lib/chef/provider/windows_script.rb14
111 files changed, 3249 insertions, 1583 deletions
diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb
index b6b386e5a8..63286f91f6 100644
--- a/lib/chef/provider/batch.rb
+++ b/lib/chef/provider/batch.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/provider/windows_script'
+require "chef/provider/windows_script"
class Chef
class Provider
@@ -25,11 +25,19 @@ class Chef
provides :batch, os: "windows"
def initialize (new_resource, run_context)
- super(new_resource, run_context, '.bat')
+ super(new_resource, run_context, ".bat")
+ end
+
+ def command
+ basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+
+ interpreter_path = Chef::Util::PathHelper.join(basepath, interpreter)
+
+ "\"#{interpreter_path}\" #{flags} \"#{script_file.path}\""
end
def flags
- @new_resource.flags.nil? ? '/c' : new_resource.flags + ' /c'
+ @new_resource.flags.nil? ? "/c" : new_resource.flags + " /c"
end
end
diff --git a/lib/chef/provider/cookbook_file.rb b/lib/chef/provider/cookbook_file.rb
index b501a9b41d..a537ca5fd5 100644
--- a/lib/chef/provider/cookbook_file.rb
+++ b/lib/chef/provider/cookbook_file.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/provider/file'
-require 'chef/deprecation/provider/cookbook_file'
-require 'chef/deprecation/warnings'
+require "chef/provider/file"
+require "chef/deprecation/provider/cookbook_file"
+require "chef/deprecation/warnings"
class Chef
class Provider
diff --git a/lib/chef/provider/cookbook_file/content.rb b/lib/chef/provider/cookbook_file/content.rb
index 9f49ba885c..97290ff456 100644
--- a/lib/chef/provider/cookbook_file/content.rb
+++ b/lib/chef/provider/cookbook_file/content.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require 'chef/file_content_management/content_base'
-require 'chef/file_content_management/tempfile'
+require "chef/file_content_management/content_base"
+require "chef/file_content_management/tempfile"
class Chef
class Provider
diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb
index 6d86e336ec..361691aa0a 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/mixin/command'
-require 'chef/provider'
+require "chef/log"
+require "chef/mixin/command"
+require "chef/provider"
class Chef
class Provider
@@ -199,7 +199,7 @@ class Chef
private
def set_environment_var(attr_name, attr_value)
- if %w(MAILTO PATH SHELL HOME).include?(attr_name)
+ if %w{MAILTO PATH SHELL HOME}.include?(attr_name)
@current_resource.send(attr_name.downcase.to_sym, attr_value)
else
@current_resource.environment(@current_resource.environment.merge(attr_name => attr_value))
diff --git a/lib/chef/provider/cron/unix.rb b/lib/chef/provider/cron/unix.rb
index 0750c0420b..0c4235df45 100644
--- a/lib/chef/provider/cron/unix.rb
+++ b/lib/chef/provider/cron/unix.rb
@@ -18,8 +18,9 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/provider'
+require "chef/log"
+require "chef/provider"
+require "chef/provider/cron"
class Chef
class Provider
@@ -27,12 +28,12 @@ class Chef
class Unix < Chef::Provider::Cron
include Chef::Mixin::ShellOut
- provides :cron, os: 'solaris2'
+ provides :cron, os: "solaris2"
private
def read_crontab
- crontab = shell_out('/usr/bin/crontab -l', :user => @new_resource.user)
+ crontab = shell_out("/usr/bin/crontab -l", :user => @new_resource.user)
status = crontab.status.exitstatus
Chef::Log.debug crontab.format_for_exception if status > 0
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
index 19e7c01ab1..fe6b288eda 100644
--- a/lib/chef/provider/deploy.rb
+++ b/lib/chef/provider/deploy.rb
@@ -201,7 +201,7 @@ class Chef
converge_by("execute migration command #{@new_resource.migration_command}") do
Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}"
- run_command(run_options(:command => @new_resource.migration_command, :cwd=>release_path, :log_level => :info))
+ shell_out!(@new_resource.migration_command,run_options(:cwd=>release_path, :log_level => :info))
end
end
end
@@ -221,7 +221,7 @@ class Chef
else
converge_by("restart app using command #{@new_resource.restart_command}") do
Chef::Log.info("#{@new_resource} restarting app")
- run_command(run_options(:command => @new_resource.restart_command, :cwd => @new_resource.current_path))
+ shell_out!(@new_resource.restart_command,run_options(:cwd=>@new_resource.current_path))
end
end
end
@@ -276,7 +276,7 @@ class Chef
def enforce_ownership
converge_by("force ownership of #{@new_resource.deploy_to} to #{@new_resource.group}:#{@new_resource.user}") do
- FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to)
+ FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to, :force => true)
Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user
Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group
end
@@ -365,7 +365,7 @@ class Chef
end
def release_slug
- raise Chef::Exceptions::Override, "You must override release_slug in #{self.to_s}"
+ raise Chef::Exceptions::Override, "You must override release_slug in #{self}"
end
def install_gems
@@ -373,11 +373,9 @@ class Chef
end
def gem_resource_collection_runner
- gems_collection = Chef::ResourceCollection.new
- gem_packages.each { |rbgem| gems_collection.insert(rbgem) }
- gems_run_context = run_context.dup
- gems_run_context.resource_collection = gems_collection
- Chef::Runner.new(gems_run_context)
+ child_context = run_context.create_child
+ gem_packages.each { |rbgem| child_context.resource_collection.insert(rbgem) }
+ Chef::Runner.new(child_context)
end
def gem_packages
diff --git a/lib/chef/provider/deploy/revision.rb b/lib/chef/provider/deploy/revision.rb
index 62aa0e87f6..c3b61a564e 100644
--- a/lib/chef/provider/deploy/revision.rb
+++ b/lib/chef/provider/deploy/revision.rb
@@ -19,9 +19,9 @@
# limitations under the License.
#
-require 'chef/provider'
-require 'chef/provider/deploy'
-require 'chef/json_compat'
+require "chef/provider"
+require "chef/provider/deploy"
+require "chef/json_compat"
class Chef
class Provider
diff --git a/lib/chef/provider/directory.rb b/lib/chef/provider/directory.rb
index 416393ac60..c9b8b3a844 100644
--- a/lib/chef/provider/directory.rb
+++ b/lib/chef/provider/directory.rb
@@ -16,12 +16,12 @@
# limitations under the License.
#
-require 'chef/config'
-require 'chef/log'
-require 'chef/resource/directory'
-require 'chef/provider'
-require 'chef/provider/file'
-require 'fileutils'
+require "chef/config"
+require "chef/log"
+require "chef/resource/directory"
+require "chef/provider"
+require "chef/provider/file"
+require "fileutils"
class Chef
class Provider
@@ -43,6 +43,9 @@ class Chef
end
def define_resource_requirements
+ # deep inside FAC we have to assert requirements, so call FACs hook to set that up
+ access_controls.define_resource_requirements
+
requirements.assert(:create) do |a|
# Make sure the parent dir exists, or else fail.
# for why run, print a message explaining the potential error.
@@ -61,7 +64,13 @@ class Chef
is_parent_writable = lambda do |base_dir|
base_dir = ::File.dirname(base_dir)
if ::File.exists?(base_dir)
- Chef::FileAccessControl.writable?(base_dir)
+ if Chef::FileAccessControl.writable?(base_dir)
+ true
+ elsif Chef::Util::PathHelper.is_sip_path?(base_dir, node)
+ Chef::Util::PathHelper.writable_sip_path?(base_dir)
+ else
+ false
+ end
else
is_parent_writable.call(base_dir)
end
@@ -71,7 +80,13 @@ class Chef
# in why run mode & parent directory does not exist no permissions check is required
# If not in why run, permissions must be valid and we rely on prior assertion that dir exists
if !whyrun_mode? || ::File.exists?(parent_directory)
- Chef::FileAccessControl.writable?(parent_directory)
+ if Chef::FileAccessControl.writable?(parent_directory)
+ true
+ elsif Chef::Util::PathHelper.is_sip_path?(parent_directory, node)
+ Chef::Util::PathHelper.writable_sip_path?(@new_resource.path)
+ else
+ false
+ end
else
true
end
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
index 2812c154c6..b2946352fe 100644
--- a/lib/chef/provider/dsc_resource.rb
+++ b/lib/chef/provider/dsc_resource.rb
@@ -15,29 +15,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-require 'chef/util/powershell/cmdlet'
-require 'chef/util/dsc/local_configuration_manager'
-require 'chef/mixin/powershell_type_coercions'
-require 'chef/util/dsc/resource_store'
+require "chef/util/powershell/cmdlet"
+require "chef/util/dsc/local_configuration_manager"
+require "chef/mixin/powershell_type_coercions"
+require "chef/util/dsc/resource_store"
class Chef
class Provider
class DscResource < Chef::Provider
include Chef::Mixin::PowershellTypeCoercions
-
provides :dsc_resource, os: "windows"
-
def initialize(new_resource, run_context)
super
@new_resource = new_resource
@module_name = new_resource.module_name
+ @reboot_resource = nil
end
def action_run
if ! test_resource
converge_by(generate_description) do
result = set_resource
+ reboot_if_required
end
end
end
@@ -53,17 +52,16 @@ class Chef
requirements.assert(:run) do |a|
a.assertion { supports_dsc_invoke_resource? }
err = ["You must have Powershell version >= 5.0.10018.0 to use dsc_resource."]
- a.failure_message Chef::Exceptions::NoProviderAvailable,
+ a.failure_message Chef::Exceptions::ProviderNotFound,
err
a.whyrun err + ["Assuming a previous resource installs Powershell 5.0.10018.0 or higher."]
a.block_action!
end
requirements.assert(:run) do |a|
- a.assertion {
- meta_configuration['RefreshMode'] == 'Disabled'
- }
- err = ["The LCM must have its RefreshMode set to Disabled. "]
- a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ')
+ a.assertion { supports_refresh_mode_enabled? || dsc_refresh_mode_disabled? }
+ err = ["The LCM must have its RefreshMode set to Disabled for" \
+ " PowerShell versions before 5.0.10586.0."]
+ a.failure_message Chef::Exceptions::ProviderNotFound, err.join(" ")
a.whyrun err + ["Assuming a previous resource sets the RefreshMode."]
a.block_action!
end
@@ -74,7 +72,7 @@ class Chef
def local_configuration_manager
@local_configuration_manager ||= Chef::Util::DSC::LocalConfigurationManager.new(
node,
- nil
+ nil,
)
end
@@ -86,6 +84,14 @@ class Chef
run_context && Chef::Platform.supports_dsc_invoke_resource?(node)
end
+ def dsc_refresh_mode_disabled?
+ Chef::Platform.dsc_refresh_mode_disabled?(node)
+ end
+
+ def supports_refresh_mode_enabled?
+ Chef::Platform.supports_refresh_mode_enabled?(node)
+ end
+
def generate_description
@converge_description
end
@@ -97,17 +103,16 @@ class Chef
def module_name
@module_name ||= begin
found = resource_store.find(dsc_resource_name)
-
r = case found.length
when 0
raise Chef::Exceptions::ResourceNotFound,
"Could not find #{dsc_resource_name}. Check to make "\
"sure that it shows up when running Get-DscResource"
when 1
- if found[0]['Module'].nil?
+ if found[0]["Module"].nil?
:none
else
- found[0]['Module']['Name']
+ found[0]["Module"]["Name"]
end
else
raise Chef::Exceptions::MultipleDscResourcesFound, found
@@ -117,41 +122,73 @@ class Chef
def test_resource
result = invoke_resource(:test)
+ @converge_description = result.stream(:verbose)
+
# We really want this information from the verbose stream,
- # however Invoke-DscResource is not correctly writing to that
- # stream and instead just dumping to stdout
- @converge_description = result.stdout
- result.return_value[0]["InDesiredState"]
+ # however in some versions of WMF, Invoke-DscResource is not correctly
+ # writing to that stream and instead just dumping to stdout
+ if @converge_description.empty?
+ @converge_description = result.stdout
+ end
+
+ return_dsc_resource_result(result, "InDesiredState")
end
def set_resource
result = invoke_resource(:set)
+ if return_dsc_resource_result(result, "RebootRequired")
+ create_reboot_resource
+ end
result.return_value
end
def invoke_resource(method, output_format=:object)
properties = translate_type(@new_resource.properties)
- switches = "-Method #{method.to_s} -Name #{@new_resource.resource}"\
+ switches = "-Method #{method} -Name #{@new_resource.resource}"\
" -Property #{properties} -Verbose"
-
if module_name != :none
switches += " -Module #{module_name}"
end
-
cmdlet = Chef::Util::Powershell::Cmdlet.new(
node,
"Invoke-DscResource #{switches}",
- output_format
+ output_format,
)
- cmdlet.run!
+ cmdlet.run!({}, {:timeout => new_resource.timeout})
end
- def meta_configuration
- cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
- result = cmdlet.run!
- result.return_value
+ def return_dsc_resource_result(result, property_name)
+ if result.return_value.is_a?(Array)
+ # WMF Feb 2015 Preview
+ result.return_value[0][property_name]
+ else
+ # WMF April 2015 Preview
+ result.return_value[property_name]
+ end
end
+ def create_reboot_resource
+ @reboot_resource = Chef::Resource::Reboot.new(
+ "Reboot for #{@new_resource.name}",
+ run_context,
+ ).tap do |r|
+ r.reason("Reboot for #{@new_resource.resource}.")
+ end
+ end
+
+ def reboot_if_required
+ reboot_action = @new_resource.reboot_action
+ unless @reboot_resource.nil?
+ case reboot_action
+ when :nothing
+ Chef::Log.debug("A reboot was requested by the DSC resource, but reboot_action is :nothing.")
+ Chef::Log.debug("This dsc_resource will not reboot the node.")
+ else
+ Chef::Log.debug("Requesting node reboot with #{reboot_action}.")
+ @reboot_resource.run_action(reboot_action)
+ end
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb
index a75e68a475..3577c23e7b 100644
--- a/lib/chef/provider/dsc_script.rb
+++ b/lib/chef/provider/dsc_script.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require 'chef/util/powershell/cmdlet'
-require 'chef/util/dsc/configuration_generator'
-require 'chef/util/dsc/local_configuration_manager'
-require 'chef/util/path_helper'
+require "chef/util/powershell/cmdlet"
+require "chef/util/dsc/configuration_generator"
+require "chef/util/dsc/local_configuration_manager"
+require "chef/util/path_helper"
class Chef
class Provider
@@ -65,12 +65,12 @@ class Chef
def define_resource_requirements
requirements.assert(:run) do |a|
err = [
- 'Could not find PowerShell DSC support on the system',
+ "Could not find PowerShell DSC support on the system",
powershell_info_str,
"Powershell 4.0 or higher was not detected on your system and is required to use the dsc_script resource.",
]
a.assertion { supports_dsc? }
- a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ')
+ a.failure_message Chef::Exceptions::ProviderNotFound, err.join(" ")
a.whyrun err + ["Assuming a previous resource installs Powershell 4.0 or higher."]
a.block_action!
end
@@ -92,14 +92,14 @@ class Chef
shellout_flags = {
:cwd => @dsc_resource.cwd,
:environment => @dsc_resource.environment,
- :timeout => @dsc_resource.timeout
+ :timeout => @dsc_resource.timeout,
}
begin
configuration_document = generate_configuration_document(config_directory, configuration_flags)
@operations[operation].call(config_manager, configuration_document, shellout_flags)
rescue Exception => e
- Chef::Log.error("DSC operation failed: #{e.message.to_s}")
+ Chef::Log.error("DSC operation failed: #{e.message}")
raise e
ensure
::FileUtils.rm_rf(config_directory)
@@ -119,7 +119,7 @@ class Chef
shellout_flags = {
:cwd => @dsc_resource.cwd,
:environment => @dsc_resource.environment,
- :timeout => @dsc_resource.timeout
+ :timeout => @dsc_resource.timeout,
}
generator = Chef::Util::DSC::ConfigurationGenerator.new(@run_context.node, config_directory)
@@ -129,7 +129,7 @@ class Chef
else
# If code is also not provided, we mimic what the other script resources do (execute nothing)
Chef::Log.warn("Neither code or command were provided for dsc_resource[#{@dsc_resource.name}].") unless @dsc_resource.code
- generator.configuration_document_from_script_code(@dsc_resource.code || '', configuration_flags, @dsc_resource.imports, shellout_flags)
+ generator.configuration_document_from_script_code(@dsc_resource.code || "", configuration_flags, @dsc_resource.imports, shellout_flags)
end
end
@@ -138,7 +138,7 @@ class Chef
@dsc_resource.configuration_data_script
elsif @dsc_resource.configuration_data
configuration_data_path = "#{config_directory}/chef_dsc_config_data.psd1"
- ::File.open(configuration_data_path, 'wt') do | script |
+ ::File.open(configuration_data_path, "wt") do | script |
script.write(@dsc_resource.configuration_data)
end
configuration_data_path
@@ -164,7 +164,7 @@ class Chef
@dsc_resources_info.map do |resource|
if resource.changes_state?
# We ignore the last log message because it only contains the time it took, which looks weird
- cleaned_messages = resource.change_log[0..-2].map { |c| c.sub(/^#{Regexp.escape(resource.name)}/, '').strip }
+ cleaned_messages = resource.change_log[0..-2].map { |c| c.sub(/^#{Regexp.escape(resource.name)}/, "").strip }
"converge DSC resource #{resource.name} by #{cleaned_messages.find_all{ |c| c != ''}.join("\n")}"
else
# This is needed because a dsc script can have resources that are both converged and not
@@ -177,7 +177,7 @@ class Chef
if run_context && run_context.node[:languages] && run_context.node[:languages][:powershell]
install_info = "Powershell #{run_context.node[:languages][:powershell][:version]} was found on the system."
else
- install_info = 'Powershell was not found.'
+ install_info = "Powershell was not found."
end
end
end
diff --git a/lib/chef/provider/env.rb b/lib/chef/provider/env.rb
index cf75ff7d85..e8ac88e8c4 100644
--- a/lib/chef/provider/env.rb
+++ b/lib/chef/provider/env.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/provider'
-require 'chef/mixin/command'
-require 'chef/resource/env'
+require "chef/provider"
+require "chef/mixin/command"
+require "chef/resource/env"
class Chef
class Provider
@@ -48,7 +48,7 @@ class Chef
end
def env_value(key_name)
- raise Chef::Exceptions::Env, "#{self.to_s} provider does not implement env_value!"
+ raise Chef::Exceptions::Env, "#{self} provider does not implement env_value!"
end
def env_key_exists(key_name)
@@ -108,15 +108,15 @@ class Chef
not new_values.include?(item)
end.join(@new_resource.delim)
- if new_value.empty?
- return false #nothing left here, delete the key
- else
- old_value = @new_resource.value(new_value)
- create_env
- Chef::Log.debug("#{@new_resource} deleted #{old_value} element")
- @new_resource.updated_by_last_action(true)
- return true #we removed the element and updated; do not delete the key
- end
+ if new_value.empty?
+ return false #nothing left here, delete the key
+ else
+ old_value = @new_resource.value(new_value)
+ create_env
+ Chef::Log.debug("#{@new_resource} deleted #{old_value} element")
+ @new_resource.updated_by_last_action(true)
+ return true #we removed the element and updated; do not delete the key
+ end
end
end
@@ -141,11 +141,11 @@ class Chef
end
def create_env
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :#{@new_resource.action}"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :#{@new_resource.action}"
end
def delete_env
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :delete"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :delete"
end
def modify_env
@@ -164,6 +164,6 @@ class Chef
def new_values
@new_values ||= @new_resource.value.split(@new_resource.delim)
end
- end
+ end
end
end
diff --git a/lib/chef/provider/env/windows.rb b/lib/chef/provider/env/windows.rb
index 56cebdb888..ec05420471 100644
--- a/lib/chef/provider/env/windows.rb
+++ b/lib/chef/provider/env/windows.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/mixin/windows_env_helper'
+require "chef/mixin/windows_env_helper"
class Chef
class Provider
@@ -36,7 +36,7 @@ class Chef
obj.variablevalue = @new_resource.value
obj.put_
value = @new_resource.value
- value = expand_path(value) if @new_resource.key_name.upcase == 'PATH'
+ value = expand_path(value) if @new_resource.key_name.upcase == "PATH"
ENV[@new_resource.key_name] = value
broadcast_env_change
end
diff --git a/lib/chef/provider/erl_call.rb b/lib/chef/provider/erl_call.rb
index f5855bcce6..dc3a8ea50e 100644
--- a/lib/chef/provider/erl_call.rb
+++ b/lib/chef/provider/erl_call.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/mixin/command'
-require 'chef/provider'
+require "chef/log"
+require "chef/mixin/command"
+require "chef/provider"
class Chef
class Provider
@@ -89,7 +89,7 @@ class Chef
end
# fail if the first 4 characters aren't "{ok,"
- unless stdout_output[0..3].include?('{ok,')
+ unless stdout_output[0..3].include?("{ok,")
raise Chef::Exceptions::ErlCall, stdout_output
end
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index b44112c19e..b291786391 100644
--- a/lib/chef/provider/execute.rb
+++ b/lib/chef/provider/execute.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/provider'
-require 'forwardable'
+require "chef/log"
+require "chef/provider"
+require "forwardable"
class Chef
class Provider
@@ -41,7 +41,7 @@ class Chef
def define_resource_requirements
# @todo: this should change to raise in some appropriate major version bump.
if creates && creates_relative? && !cwd
- Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail (CHEF-3819)"
+ Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail in the future (CHEF-3819)"
end
end
@@ -58,7 +58,16 @@ class Chef
end
converge_by("execute #{description}") do
- result = shell_out!(command, opts)
+ begin
+ shell_out!(command, opts)
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ if sensitive?
+ raise Mixlib::ShellOut::ShellCommandFailed,
+ "Command execution failed. STDOUT/STDERR suppressed for sensitive resource"
+ else
+ raise
+ end
+ end
Chef::Log.info("#{new_resource} ran successfully")
end
end
@@ -69,6 +78,14 @@ class Chef
!!new_resource.sensitive
end
+ def live_stream?
+ Chef::Config[:stream_execute_output] || !!new_resource.live_stream
+ end
+
+ def stream_to_stdout?
+ STDOUT.tty? && !Chef::Config[:daemon]
+ end
+
def opts
opts = {}
opts[:timeout] = timeout
@@ -80,8 +97,12 @@ class Chef
opts[:umask] = umask if umask
opts[:log_level] = :info
opts[:log_tag] = new_resource.to_s
- if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info? && !sensitive?
- opts[:live_stream] = STDOUT
+ if (Chef::Log.info? || live_stream?) && !sensitive?
+ if run_context.events.formatter?
+ opts[:live_stream] = Chef::EventDispatch::EventsOutputStream.new(run_context.events, :name => :execute)
+ elsif stream_to_stdout?
+ opts[:live_stream] = STDOUT
+ end
end
opts
end
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
index c070d29458..a605682718 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Author:: Lamont Granquist (<lamont@opscode.com>)
-# Copyright:: Copyright (c) 2008-2013 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");
@@ -17,20 +17,22 @@
# limitations under the License.
#
-require 'chef/config'
-require 'chef/log'
-require 'chef/resource/file'
-require 'chef/provider'
-require 'etc'
-require 'fileutils'
-require 'chef/scan_access_control'
-require 'chef/mixin/checksum'
-require 'chef/mixin/file_class'
-require 'chef/util/backup'
-require 'chef/util/diff'
-require 'chef/deprecation/provider/file'
-require 'chef/deprecation/warnings'
-require 'chef/file_content_management/deploy'
+require "chef/config"
+require "chef/log"
+require "chef/resource/file"
+require "chef/provider"
+require "etc"
+require "fileutils"
+require "chef/scan_access_control"
+require "chef/mixin/checksum"
+require "chef/mixin/file_class"
+require "chef/mixin/enforce_ownership_and_permissions"
+require "chef/util/backup"
+require "chef/util/diff"
+require "chef/util/selinux"
+require "chef/deprecation/provider/file"
+require "chef/deprecation/warnings"
+require "chef/file_content_management/deploy"
# The Tao of File Providers:
# - the content provider must always return a tempfile that we can delete/mv
@@ -244,7 +246,7 @@ class Chef
else
[ Chef::Exceptions::FileTypeMismatch,
"File #{path} exists, but is a #{file_type_string(@new_resource.path)}, set force_unlink to true to remove",
- "Assuming #{file_type_string(@new_resource.path)} at #{@new_resource.path} would have been removed by a previous resource"
+ "Assuming #{file_type_string(@new_resource.path)} at #{@new_resource.path} would have been removed by a previous resource",
]
end
end
@@ -265,7 +267,7 @@ class Chef
[ Chef::Exceptions::FileTypeMismatch,
"File #{path} exists, but is a symlink to #{real_path} which is a #{file_type_string(real_path)}. " +
"Disable manage_symlink_source and set force_unlink to remove it.",
- "Assuming symlink #{path} or source file #{real_path} would have been fixed by a previous resource"
+ "Assuming symlink #{path} or source file #{real_path} would have been fixed by a previous resource",
]
end
rescue Errno::ELOOP
@@ -386,10 +388,11 @@ class Chef
def update_file_contents
do_backup unless needs_creating?
- deployment_strategy.deploy(tempfile.path, ::File.realpath(@new_resource.path))
- Chef::Log.info("#{@new_resource} updated file contents #{@new_resource.path}")
+ deployment_strategy.deploy(tempfile.path, ::File.realpath(new_resource.path))
+ Chef::Log.info("#{new_resource} updated file contents #{new_resource.path}")
if managing_content?
- @new_resource.checksum(checksum(@new_resource.path)) # for reporting
+ # save final checksum for reporting.
+ new_resource.final_checksum = checksum(new_resource.path)
end
end
diff --git a/lib/chef/provider/file/content.rb b/lib/chef/provider/file/content.rb
index f82bc49db4..96dda1bcbc 100644
--- a/lib/chef/provider/file/content.rb
+++ b/lib/chef/provider/file/content.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require 'chef/file_content_management/content_base'
-require 'chef/file_content_management/tempfile'
+require "chef/file_content_management/content_base"
+require "chef/file_content_management/tempfile"
class Chef
class Provider
diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb
index 8418f22933..a0f6d076df 100644
--- a/lib/chef/provider/git.rb
+++ b/lib/chef/provider/git.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require 'chef/exceptions'
-require 'chef/log'
-require 'chef/provider'
-require 'fileutils'
+require "chef/exceptions"
+require "chef/log"
+require "chef/provider"
+require "fileutils"
class Chef
class Provider
@@ -105,7 +105,7 @@ class Chef
end
def git_minor_version
- @git_minor_version ||= Gem::Version.new(shell_out!('git --version', run_options).stdout.split.last)
+ @git_minor_version ||= Gem::Version.new(shell_out!("git --version", run_options).stdout.split.last)
end
def existing_git_clone?
@@ -113,14 +113,14 @@ class Chef
end
def target_dir_non_existent_or_empty?
- !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
+ !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == [".",".."]
end
def find_current_revision
Chef::Log.debug("#{@new_resource} finding current git revision")
if ::File.exist?(::File.join(cwd, ".git"))
# 128 is returned when we're not in a git repo. this is fine
- result = shell_out!('git rev-parse HEAD', :cwd => cwd, :returns => [0,128]).stdout.strip
+ result = shell_out!("git rev-parse HEAD", :cwd => cwd, :returns => [0,128]).stdout.strip
end
sha_hash?(result) ? result : nil
end
@@ -141,9 +141,9 @@ class Chef
remote = @new_resource.remote
args = []
- args << "-o #{remote}" unless remote == 'origin'
+ args << "-o #{remote}" unless remote == "origin"
args << "--depth #{@new_resource.depth}" if @new_resource.depth
- args << "--no-single-branch" if @new_resource.depth and git_minor_version >= Gem::Version.new('1.7.10')
+ args << "--no-single-branch" if @new_resource.depth and git_minor_version >= Gem::Version.new("1.7.10")
Chef::Log.info "#{@new_resource} cloning repo #{@new_resource.repository} to #{@new_resource.destination}"
@@ -250,18 +250,18 @@ class Chef
# Using such a degenerate annotated tag would be very
# confusing. We avoid the issue by disallowing the use of
# annotated tags named 'HEAD'.
- if rev_search_pattern != 'HEAD'
- found = find_revision(refs, @new_resource.revision, '^{}')
+ if rev_search_pattern != "HEAD"
+ found = find_revision(refs, @new_resource.revision, "^{}")
else
- found = refs_search(refs, 'HEAD')
+ found = refs_search(refs, "HEAD")
end
found = find_revision(refs, @new_resource.revision) if found.empty?
found.size == 1 ? found.first[0] : nil
end
def find_revision(refs, revision, suffix="")
- found = refs_search(refs, rev_match_pattern('refs/tags/', revision) + suffix)
- found = refs_search(refs, rev_match_pattern('refs/heads/', revision) + suffix) if found.empty?
+ found = refs_search(refs, rev_match_pattern("refs/tags/", revision) + suffix)
+ found = refs_search(refs, rev_match_pattern("refs/heads/", revision) + suffix) if found.empty?
found = refs_search(refs, revision + suffix) if found.empty?
found
end
@@ -275,15 +275,15 @@ class Chef
end
def rev_search_pattern
- if ['', 'HEAD'].include? @new_resource.revision
- 'HEAD'
+ if ["", "HEAD"].include? @new_resource.revision
+ "HEAD"
else
- @new_resource.revision + '*'
+ @new_resource.revision + "*"
end
end
def git_ls_remote(rev_pattern)
- command = git(%Q(ls-remote "#{@new_resource.repository}" "#{rev_pattern}"))
+ command = git(%Q{ls-remote "#{@new_resource.repository}" "#{rev_pattern}"})
shell_out!(command, run_options).stdout
end
@@ -300,15 +300,15 @@ class Chef
# Certain versions of `git` misbehave if git configuration is
# inaccessible in $HOME. We need to ensure $HOME matches the
# user who is executing `git` not the user running Chef.
- env['HOME'] = begin
- require 'etc'
+ env["HOME"] = begin
+ require "etc"
Etc.getpwnam(@new_resource.user).dir
rescue ArgumentError # user not found
raise Chef::Exceptions::User, "Could not determine HOME for specified user '#{@new_resource.user}' for resource '#{@new_resource.name}'"
end
end
run_opts[:group] = @new_resource.group if @new_resource.group
- env['GIT_SSH'] = @new_resource.ssh_wrapper if @new_resource.ssh_wrapper
+ env["GIT_SSH"] = @new_resource.ssh_wrapper if @new_resource.ssh_wrapper
run_opts[:log_tag] = @new_resource.to_s
run_opts[:timeout] = @new_resource.timeout if @new_resource.timeout
env.merge!(@new_resource.environment) if @new_resource.environment
diff --git a/lib/chef/provider/group.rb b/lib/chef/provider/group.rb
index a802758dce..2aa9889b17 100644
--- a/lib/chef/provider/group.rb
+++ b/lib/chef/provider/group.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require 'chef/provider'
-require 'chef/mixin/shell_out'
-require 'chef/mixin/command'
-require 'etc'
+require "chef/provider"
+require "chef/mixin/shell_out"
+require "chef/mixin/command"
+require "etc"
class Chef
class Provider
@@ -125,7 +125,7 @@ class Chef
def action_create
case @group_exists
when false
- converge_by("create #{@new_resource.group_name}") do
+ converge_by("create group #{@new_resource.group_name}") do
create_group
Chef::Log.info("#{@new_resource} created")
end
diff --git a/lib/chef/provider/group/aix.rb b/lib/chef/provider/group/aix.rb
index 6ac9d03357..1059208ed8 100644
--- a/lib/chef/provider/group/aix.rb
+++ b/lib/chef/provider/group/aix.rb
@@ -16,13 +16,14 @@
# limitations under the License.
#
-require 'chef/provider/group/groupadd'
-require 'chef/mixin/shell_out'
+require "chef/provider/group/groupadd"
+require "chef/mixin/shell_out"
class Chef
class Provider
class Group
class Aix < Chef::Provider::Group::Groupadd
+ provides :group, platform: "aix"
def required_binaries
[ "/usr/bin/mkgroup",
@@ -71,7 +72,7 @@ class Chef
{ :gid => "id" }.sort { |a,b| a[0] <=> b[0] }.each do |field, option|
if @current_resource.send(field) != @new_resource.send(field)
if @new_resource.send(field)
- Chef::Log.debug("#{@new_resource} setting #{field.to_s} to #{@new_resource.send(field)}")
+ Chef::Log.debug("#{@new_resource} setting #{field} to #{@new_resource.send(field)}")
opts << " '#{option}=#{@new_resource.send(field)}'"
end
end
diff --git a/lib/chef/provider/group/dscl.rb b/lib/chef/provider/group/dscl.rb
index d7e8f2e827..0b39571458 100644
--- a/lib/chef/provider/group/dscl.rb
+++ b/lib/chef/provider/group/dscl.rb
@@ -53,14 +53,14 @@ class Chef
if group_info
group_info.each_line do |line|
- key, val = line.split(': ')
+ key, val = line.split(": ")
val.strip! if val
case key.downcase
- when 'primarygroupid'
+ when "primarygroupid"
@new_resource.gid(val) unless @new_resource.gid
@current_resource.gid(val)
- when 'groupmembership'
- @current_resource.members(val.split(' '))
+ when "groupmembership"
+ @current_resource.members(val.split(" "))
end
end
end
diff --git a/lib/chef/provider/group/gpasswd.rb b/lib/chef/provider/group/gpasswd.rb
index 521affac11..011a9d1e63 100644
--- a/lib/chef/provider/group/gpasswd.rb
+++ b/lib/chef/provider/group/gpasswd.rb
@@ -16,12 +16,13 @@
# limitations under the License.
#
-require 'chef/provider/group/groupadd'
+require "chef/provider/group/groupadd"
class Chef
class Provider
class Group
class Gpasswd < Chef::Provider::Group::Groupadd
+ provides :group
def load_current_resource
super
diff --git a/lib/chef/provider/group/groupadd.rb b/lib/chef/provider/group/groupadd.rb
index cb480aab54..684df8455e 100644
--- a/lib/chef/provider/group/groupadd.rb
+++ b/lib/chef/provider/group/groupadd.rb
@@ -96,15 +96,15 @@ class Chef
end
def add_member(member)
- raise Chef::Exceptions::Group, "you must override add_member in #{self.to_s}"
+ raise Chef::Exceptions::Group, "you must override add_member in #{self}"
end
def remove_member(member)
- raise Chef::Exceptions::Group, "you must override remove_member in #{self.to_s}"
+ raise Chef::Exceptions::Group, "you must override remove_member in #{self}"
end
def set_members(members)
- raise Chef::Exceptions::Group, "you must override set_members in #{self.to_s}"
+ raise Chef::Exceptions::Group, "you must override set_members in #{self}"
end
# Little bit of magic as per Adam's useradd provider to pull the assign the command line flags
@@ -117,7 +117,7 @@ class Chef
if @current_resource.send(field) != @new_resource.send(field)
if @new_resource.send(field)
opts << " #{option} '#{@new_resource.send(field)}'"
- Chef::Log.debug("#{@new_resource} set #{field.to_s} to #{@new_resource.send(field)}")
+ Chef::Log.debug("#{@new_resource} set #{field} to #{@new_resource.send(field)}")
end
end
end
@@ -125,7 +125,7 @@ class Chef
end
def groupadd_options
- opts = ''
+ opts = ""
opts << " -r" if @new_resource.system
opts << " -o" if @new_resource.non_unique
opts
diff --git a/lib/chef/provider/group/pw.rb b/lib/chef/provider/group/pw.rb
index 7a66ab4d69..e4c129ba9f 100644
--- a/lib/chef/provider/group/pw.rb
+++ b/lib/chef/provider/group/pw.rb
@@ -20,6 +20,7 @@ class Chef
class Provider
class Group
class Pw < Chef::Provider::Group
+ provides :group, platform: "freebsd"
def load_current_resource
super
@@ -108,7 +109,7 @@ class Chef
else
# Append is not set so we're resetting the membership of
# the group to the given members.
- members_to_be_added = @new_resource.members
+ members_to_be_added = @new_resource.members.dup
@current_resource.members.each do |member|
# No need to re-add a member if it's present in the new
# list of members
diff --git a/lib/chef/provider/group/suse.rb b/lib/chef/provider/group/suse.rb
index 7ac2831d02..42e525169f 100644
--- a/lib/chef/provider/group/suse.rb
+++ b/lib/chef/provider/group/suse.rb
@@ -16,12 +16,14 @@
# limitations under the License.
#
-require 'chef/provider/group/groupadd'
+require "chef/provider/group/groupadd"
class Chef
class Provider
class Group
class Suse < Chef::Provider::Group::Groupadd
+ provides :group, platform: "opensuse", platform_version: "< 12.3"
+ provides :group, platform: "suse", platform_version: "< 12.0"
def load_current_resource
super
diff --git a/lib/chef/provider/group/usermod.rb b/lib/chef/provider/group/usermod.rb
index e50e13c443..f4f3ac55ae 100644
--- a/lib/chef/provider/group/usermod.rb
+++ b/lib/chef/provider/group/usermod.rb
@@ -16,14 +16,15 @@
# limitations under the License.
#
-require 'chef/provider/group/groupadd'
+require "chef/provider/group/groupadd"
class Chef
class Provider
class Group
class Usermod < Chef::Provider::Group::Groupadd
- provides :group, os: "openbsd"
+ provides :group, os: %w{openbsd solaris2 hpux}
+ provides :group, platform: "opensuse"
def load_current_resource
super
@@ -40,13 +41,13 @@ class Chef
requirements.assert(:modify, :manage) do |a|
a.assertion { @new_resource.members.empty? || @new_resource.append }
- a.failure_message Chef::Exceptions::Group, "setting group members directly is not supported by #{self.to_s}, must set append true in group"
+ a.failure_message Chef::Exceptions::Group, "setting group members directly is not supported by #{self}, must set append true in group"
# No whyrun alternative - this action is simply not supported.
end
requirements.assert(:all_actions) do |a|
a.assertion { @new_resource.excluded_members.empty? }
- a.failure_message Chef::Exceptions::Group, "excluded_members is not supported by #{self.to_s}"
+ a.failure_message Chef::Exceptions::Group, "excluded_members is not supported by #{self}"
# No whyrun alternative - this action is simply not supported.
end
end
@@ -61,7 +62,7 @@ class Chef
add_member(member)
end
else
- raise Chef::Exceptions::UnsupportedAction, "Setting members directly is not supported by #{self.to_s}"
+ raise Chef::Exceptions::UnsupportedAction, "Setting members directly is not supported by #{self}"
end
end
@@ -72,7 +73,7 @@ class Chef
def remove_member(member)
# This provider only supports adding members with
# append. This function should never be called.
- raise Chef::Exceptions::UnsupportedAction, "Removing members members is not supported by #{self.to_s}"
+ raise Chef::Exceptions::UnsupportedAction, "Removing members members is not supported by #{self}"
end
def append_flags
diff --git a/lib/chef/provider/group/windows.rb b/lib/chef/provider/group/windows.rb
index 54e49b0e06..a665757df4 100644
--- a/lib/chef/provider/group/windows.rb
+++ b/lib/chef/provider/group/windows.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/provider/user'
+require "chef/provider/user"
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require 'chef/util/windows/net_group'
+ require "chef/util/windows/net_group"
end
class Chef
diff --git a/lib/chef/provider/http_request.rb b/lib/chef/provider/http_request.rb
index 61aff434ed..62dc0b0a46 100644
--- a/lib/chef/provider/http_request.rb
+++ b/lib/chef/provider/http_request.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require 'tempfile'
-require 'chef/http/simple'
+require "tempfile"
+require "chef/http/simple"
class Chef
class Provider
@@ -42,7 +42,7 @@ class Chef
# and false for a "304 Not Modified" response
modified = @http.head(
"#{@new_resource.url}",
- @new_resource.headers
+ @new_resource.headers,
)
Chef::Log.info("#{@new_resource} HEAD to #{@new_resource.url} successful")
Chef::Log.debug("#{@new_resource} HEAD request response: #{modified}")
@@ -59,7 +59,7 @@ class Chef
message = check_message(@new_resource.message)
body = @http.get(
"#{@new_resource.url}",
- @new_resource.headers
+ @new_resource.headers,
)
Chef::Log.info("#{@new_resource} GET to #{@new_resource.url} successful")
Chef::Log.debug("#{@new_resource} GET request response: #{body}")
@@ -73,7 +73,7 @@ class Chef
body = @http.put(
"#{@new_resource.url}",
message,
- @new_resource.headers
+ @new_resource.headers,
)
Chef::Log.info("#{@new_resource} PUT to #{@new_resource.url} successful")
Chef::Log.debug("#{@new_resource} PUT request response: #{body}")
@@ -87,7 +87,7 @@ class Chef
body = @http.post(
"#{@new_resource.url}",
message,
- @new_resource.headers
+ @new_resource.headers,
)
Chef::Log.info("#{@new_resource} POST to #{@new_resource.url} message: #{message.inspect} successful")
Chef::Log.debug("#{@new_resource} POST request response: #{body}")
@@ -99,7 +99,7 @@ class Chef
converge_by("#{@new_resource} DELETE to #{@new_resource.url}") do
body = @http.delete(
"#{@new_resource.url}",
- @new_resource.headers
+ @new_resource.headers,
)
@new_resource.updated_by_last_action(true)
Chef::Log.info("#{@new_resource} DELETE to #{@new_resource.url} successful")
diff --git a/lib/chef/provider/ifconfig.rb b/lib/chef/provider/ifconfig.rb
index 06080c90c3..95f4b979f5 100644
--- a/lib/chef/provider/ifconfig.rb
+++ b/lib/chef/provider/ifconfig.rb
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/mixin/command'
-require 'chef/mixin/shell_out'
-require 'chef/provider'
-require 'chef/resource/file'
-require 'chef/exceptions'
-require 'erb'
+require "chef/log"
+require "chef/mixin/command"
+require "chef/mixin/shell_out"
+require "chef/provider"
+require "chef/resource/file"
+require "chef/exceptions"
+require "erb"
# Recipe example:
#
@@ -39,6 +39,8 @@ require 'erb'
class Chef
class Provider
class Ifconfig < Chef::Provider
+ provides :ifconfig
+
include Chef::Mixin::ShellOut
include Chef::Mixin::Command
@@ -107,7 +109,7 @@ class Chef
command = add_command
converge_by ("run #{command} to add #{@new_resource}") do
run_command(
- :command => command
+ :command => command,
)
Chef::Log.info("#{@new_resource} added")
end
@@ -125,7 +127,7 @@ class Chef
command = enable_command
converge_by ("run #{command} to enable #{@new_resource}") do
run_command(
- :command => command
+ :command => command,
)
Chef::Log.info("#{@new_resource} enabled")
end
@@ -139,7 +141,7 @@ class Chef
command = delete_command
converge_by ("run #{command} to delete #{@new_resource}") do
run_command(
- :command => command
+ :command => command,
)
Chef::Log.info("#{@new_resource} deleted")
end
@@ -156,7 +158,7 @@ class Chef
command = disable_command
converge_by ("run #{command} to disable #{@new_resource}") do
run_command(
- :command => command
+ :command => command,
)
Chef::Log.info("#{@new_resource} disabled")
end
@@ -192,7 +194,7 @@ class Chef
private
def add_command
- command = "ifconfig #{@new_resource.device} #{@new_resource.name}"
+ command = "ifconfig #{@new_resource.device} #{@new_resource.target}"
command << " netmask #{@new_resource.mask}" if @new_resource.mask
command << " metric #{@new_resource.metric}" if @new_resource.metric
command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
@@ -200,7 +202,7 @@ class Chef
end
def enable_command
- command = "ifconfig #{@new_resource.device} #{@new_resource.name}"
+ command = "ifconfig #{@new_resource.device} #{@new_resource.target}"
command << " netmask #{@new_resource.mask}" if @new_resource.mask
command << " metric #{@new_resource.metric}" if @new_resource.metric
command << " mtu #{@new_resource.mtu}" if @new_resource.mtu
@@ -216,7 +218,7 @@ class Chef
end
def loopback_device
- 'lo'
+ "lo"
end
end
end
diff --git a/lib/chef/provider/ifconfig/aix.rb b/lib/chef/provider/ifconfig/aix.rb
index 8fead44bc6..30e702fe10 100644
--- a/lib/chef/provider/ifconfig/aix.rb
+++ b/lib/chef/provider/ifconfig/aix.rb
@@ -16,12 +16,13 @@
# limitations under the License.
#
-require 'chef/provider/ifconfig'
+require "chef/provider/ifconfig"
class Chef
class Provider
class Ifconfig
class Aix < Chef::Provider::Ifconfig
+ provides :ifconfig, platform: %w{aix}
def load_current_resource
@current_resource = Chef::Resource::Ifconfig.new(@new_resource.name)
diff --git a/lib/chef/provider/ifconfig/debian.rb b/lib/chef/provider/ifconfig/debian.rb
index 7589971143..3885e55998 100644
--- a/lib/chef/provider/ifconfig/debian.rb
+++ b/lib/chef/provider/ifconfig/debian.rb
@@ -16,13 +16,15 @@
# limitations under the License.
#
-require 'chef/provider/ifconfig'
-require 'chef/util/file_edit'
+require "chef/provider/ifconfig"
+require "chef/util/file_edit"
class Chef
class Provider
class Ifconfig
class Debian < Chef::Provider::Ifconfig
+ provides :ifconfig, platform: %w{ubuntu}, platform_version: ">= 11.10"
+ provides :ifconfig, platform: %w{debian}, platform_version: ">= 7.0"
INTERFACES_FILE = "/etc/network/interfaces"
INTERFACES_DOT_D_DIR = "/etc/network/interfaces.d"
diff --git a/lib/chef/provider/ifconfig/redhat.rb b/lib/chef/provider/ifconfig/redhat.rb
index ef35b0e012..8c02c1be07 100644
--- a/lib/chef/provider/ifconfig/redhat.rb
+++ b/lib/chef/provider/ifconfig/redhat.rb
@@ -16,12 +16,13 @@
# limitations under the License.
#
-require 'chef/provider/ifconfig'
+require "chef/provider/ifconfig"
class Chef
class Provider
class Ifconfig
class Redhat < Chef::Provider::Ifconfig
+ provides :ifconfig, platform_family: %w{fedora rhel}
def initialize(new_resource, run_context)
super(new_resource, run_context)
diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb
index c811c13cdf..571610aeed 100644
--- a/lib/chef/provider/link.rb
+++ b/lib/chef/provider/link.rb
@@ -16,13 +16,13 @@
# limitations under the License.
#
-require 'chef/config'
-require 'chef/log'
-require 'chef/mixin/file_class'
-require 'chef/resource/link'
-require 'chef/provider'
-require 'chef/scan_access_control'
-require 'chef/util/path_helper'
+require "chef/config"
+require "chef/log"
+require "chef/mixin/file_class"
+require "chef/resource/link"
+require "chef/provider"
+require "chef/scan_access_control"
+require "chef/util/path_helper"
class Chef
class Provider
@@ -75,18 +75,18 @@ class Chef
a.assertion do
if @current_resource.to
@current_resource.link_type == @new_resource.link_type and
- (@current_resource.link_type == :symbolic or @current_resource.to != '')
+ (@current_resource.link_type == :symbolic or @current_resource.to != "")
else
true
end
end
- a.failure_message Chef::Exceptions::Link, "Cannot delete #{@new_resource} at #{@new_resource.target_file}! Not a #{@new_resource.link_type.to_s} link."
+ a.failure_message Chef::Exceptions::Link, "Cannot delete #{@new_resource} at #{@new_resource.target_file}! Not a #{@new_resource.link_type} link."
a.whyrun("Would assume the link at #{@new_resource.target_file} was previously created")
end
end
def canonicalize(path)
- Chef::Platform.windows? ? path.gsub('/', '\\') : path
+ Chef::Platform.windows? ? path.gsub("/", '\\') : path
end
def action_create
diff --git a/lib/chef/provider/lwrp_base.rb b/lib/chef/provider/lwrp_base.rb
index 492ddda6da..4298a4aa98 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/lwrp_base.rb
@@ -18,7 +18,9 @@
# limitations under the License.
#
-require 'chef/provider'
+require "chef/provider"
+require "chef/dsl/recipe"
+require "chef/dsl/include_recipe"
class Chef
class Provider
@@ -27,124 +29,71 @@ class Chef
# Base class from which LWRP providers inherit.
class LWRPBase < Provider
- # Chef::Provider::LWRPBase::InlineResources
- # Implementation of inline resource convergence for LWRP providers. See
- # Provider::LWRPBase.use_inline_resources for a longer explanation.
- #
- # This code is restricted to a module so that it can be selectively
- # applied to providers on an opt-in basis.
- module InlineResources
-
- # Class methods for InlineResources. Overrides the `action` DSL method
- # with one that enables inline resource convergence.
- module ClassMethods
- # Defines an action method on the provider, using
- # recipe_eval_with_update_check to execute the given block.
- def action(name, &block)
- define_method("action_#{name}") do
- recipe_eval_with_update_check(&block)
- end
- end
- end
-
- # Executes the given block in a temporary run_context with its own
- # resource collection. After the block is executed, any resources
- # declared inside are converged, and if any are updated, the
- # new_resource will be marked updated.
- def recipe_eval_with_update_check(&block)
- saved_run_context = @run_context
- temp_run_context = @run_context.dup
- @run_context = temp_run_context
- @run_context.resource_collection = Chef::ResourceCollection.new
-
- return_value = instance_eval(&block)
- Chef::Runner.new(@run_context).converge
- return_value
- ensure
- @run_context = saved_run_context
- if temp_run_context.resource_collection.any? {|r| r.updated? }
- new_resource.updated_by_last_action(true)
- end
- end
-
- end
-
- extend Chef::Mixin::ConvertToClassName
- extend Chef::Mixin::FromFile
-
include Chef::DSL::Recipe
# These were previously provided by Chef::Mixin::RecipeDefinitionDSLCore.
- # They are not included by its replacment, Chef::DSL::Recipe, but
+ # They are not included by its replacement, Chef::DSL::Recipe, but
# they may be used in existing LWRPs.
include Chef::DSL::PlatformIntrospection
include Chef::DSL::DataQuery
- def self.build_from_file(cookbook_name, filename, run_context)
- provider_class = nil
- provider_name = filename_to_qualified_string(cookbook_name, filename)
+ # Allow include_recipe from within LWRP provider code
+ include Chef::DSL::IncludeRecipe
+
+ # no-op `load_current_resource`. Allows simple LWRP providers to work
+ # without defining this method explicitly (silences
+ # Chef::Exceptions::Override exception)
+ def load_current_resource
+ end
+
+ # class methods
+ class <<self
+ include Chef::Mixin::ConvertToClassName
+ include Chef::Mixin::FromFile
+
+ def build_from_file(cookbook_name, filename, run_context)
+ if LWRPBase.loaded_lwrps[filename]
+ Chef::Log.debug("LWRP provider #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ return loaded_lwrps[filename]
+ end
- class_name = convert_to_class_name(provider_name)
+ resource_name = filename_to_qualified_string(cookbook_name, filename)
- if Chef::Provider.const_defined?(class_name, false)
- Chef::Log.info("#{class_name} light-weight provider is already initialized -- Skipping loading #{filename}!")
- Chef::Log.debug("Overriding already defined LWRPs is not supported anymore starting with Chef 12.")
- provider_class = Chef::Provider.const_get(class_name)
- else
+ # We load the class first to give it a chance to set its own name
provider_class = Class.new(self)
- Chef::Provider.const_set(class_name, provider_class)
+ provider_class.provides resource_name.to_sym
provider_class.class_from_file(filename)
- Chef::Log.debug("Loaded contents of #{filename} into a provider named #{provider_name} defined in Chef::Provider::#{class_name}")
- end
- provider_class
- end
+ # Respect resource_name set inside the LWRP
+ provider_class.instance_eval do
+ define_singleton_method(:to_s) do
+ "LWRP provider #{resource_name} from cookbook #{cookbook_name}"
+ end
+ define_singleton_method(:inspect) { to_s }
+ end
- # Enables inline evaluation of resources in provider actions.
- #
- # Without this option, any resources declared inside the LWRP are added
- # to the resource collection after the current position at the time the
- # action is executed. Because they are added to the primary resource
- # collection for the chef run, they can notify other resources outside
- # the LWRP, and potentially be notified by resources outside the LWRP
- # (but this is complicated by the fact that they don't exist until the
- # provider executes). In this mode, it is impossible to correctly set the
- # updated_by_last_action flag on the parent LWRP resource, since it
- # executes and returns before its component resources are run.
- #
- # With this option enabled, each action creates a temporary run_context
- # with its own resource collection, evaluates the action's code in that
- # context, and then converges the resources created. If any resources
- # were updated, then this provider's new_resource will be marked updated.
- #
- # In this mode, resources created within the LWRP cannot interact with
- # external resources via notifies, though notifications to other
- # resources within the LWRP will work. Delayed notifications are executed
- # at the conclusion of the provider's action, *not* at the end of the
- # main chef run.
- #
- # This mode of evaluation is experimental, but is believed to be a better
- # set of tradeoffs than the append-after mode, so it will likely become
- # the default in a future major release of Chef.
- #
- def self.use_inline_resources
- extend InlineResources::ClassMethods
- include InlineResources
- end
+ Chef::Log.debug("Loaded contents of #{filename} into provider #{resource_name} (#{provider_class})")
- # DSL for defining a provider's actions.
- def self.action(name, &block)
- define_method("action_#{name}") do
- instance_eval(&block)
+ LWRPBase.loaded_lwrps[filename] = true
+
+ Chef::Provider.register_deprecated_lwrp_class(provider_class, convert_to_class_name(resource_name))
+
+ provider_class
end
- end
- # no-op `load_current_resource`. Allows simple LWRP providers to work
- # without defining this method explicitly (silences
- # Chef::Exceptions::Override exception)
- def load_current_resource
- end
+ # DSL for defining a provider's actions.
+ def action(name, &block)
+ define_method("action_#{name}") do
+ instance_eval(&block)
+ end
+ end
+
+ protected
+ def loaded_lwrps
+ @loaded_lwrps ||= {}
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/mdadm.rb b/lib/chef/provider/mdadm.rb
index 325f1b5977..b55e6abc1a 100644
--- a/lib/chef/provider/mdadm.rb
+++ b/lib/chef/provider/mdadm.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/provider'
+require "chef/log"
+require "chef/provider"
class Chef
class Provider
diff --git a/lib/chef/provider/mount.rb b/lib/chef/provider/mount.rb
index 1631d87033..ede36417d1 100644
--- a/lib/chef/provider/mount.rb
+++ b/lib/chef/provider/mount.rb
@@ -17,14 +17,13 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/mixin/shell_out'
-require 'chef/provider'
+require "chef/log"
+require "chef/mixin/shell_out"
+require "chef/provider"
class Chef
class Provider
class Mount < Chef::Provider
-
include Chef::Mixin::ShellOut
attr_accessor :unmount_retries
@@ -43,13 +42,17 @@ class Chef
end
def action_mount
- unless current_resource.mounted
+ if current_resource.mounted
+ if mount_options_unchanged?
+ Chef::Log.debug("#{new_resource} is already mounted")
+ else
+ action_remount
+ end
+ else
converge_by("mount #{current_resource.device} to #{current_resource.mount_point}") do
mount_fs
Chef::Log.info("#{new_resource} mounted")
end
- else
- Chef::Log.debug("#{new_resource} is already mounted")
end
end
@@ -115,12 +118,12 @@ class Chef
# should actually check if the filesystem is mounted (not just return current_resource) and return true/false
def mounted?
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not implement #mounted?"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not implement #mounted?"
end
# should check new_resource against current_resource to see if mount options need updating, returns true/false
def mount_options_unchanged?
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not implement #mount_options_unchanged?"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not implement #mount_options_unchanged?"
end
#
@@ -131,28 +134,28 @@ class Chef
# should implement mounting of the filesystem, raises if action does not succeed
def mount_fs
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :mount"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :mount"
end
# should implement unmounting of the filesystem, raises if action does not succeed
def umount_fs
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :umount"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :umount"
end
# should implement remounting of the filesystem (via a -o remount or some other atomic-ish action that isn't
# simply a umount/mount style remount), raises if action does not succeed
def remount_fs
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :remount"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :remount"
end
# should implement enabling of the filesystem (e.g. in /etc/fstab), raises if action does not succeed
def enable_fs
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :enable"
end
# should implement disabling of the filesystem (e.g. in /etc/fstab), raises if action does not succeed
def disable_fs
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :disable"
end
private
diff --git a/lib/chef/provider/mount/aix.rb b/lib/chef/provider/mount/aix.rb
index 0d7e11a1b8..a6fccfadf4 100644
--- a/lib/chef/provider/mount/aix.rb
+++ b/lib/chef/provider/mount/aix.rb
@@ -16,12 +16,13 @@
# limitations under the License.
#
-require 'chef/provider/mount'
+require "chef/provider/mount"
class Chef
class Provider
class Mount
class Aix < Chef::Provider::Mount::Mount
+ provides :mount, platform: %w{aix}
# Override for aix specific handling
def initialize(new_resource, run_context)
@@ -31,7 +32,7 @@ class Chef
@new_resource.options.clear
end
if @new_resource.fstype == "auto"
- @new_resource.fstype = nil
+ @new_resource.send(:clear_fstype)
end
end
@@ -98,13 +99,13 @@ class Chef
end
command << case @new_resource.device_type
- when :device
- " #{device_real}"
- when :label
- " -L #{@new_resource.device}"
- when :uuid
- " -U #{@new_resource.device}"
- end
+ when :device
+ " #{device_real}"
+ when :label
+ " -L #{@new_resource.device}"
+ when :uuid
+ " -U #{@new_resource.device}"
+ end
command << " #{@new_resource.mount_point}"
shell_out!(command)
Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
@@ -173,7 +174,7 @@ class Chef
end
end
+ end
end
- end
end
end
diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb
index 0a6e269d2d..e0bddb454a 100644
--- a/lib/chef/provider/mount/mount.rb
+++ b/lib/chef/provider/mount/mount.rb
@@ -16,14 +16,16 @@
# limitations under the License.
#
-require 'chef/provider/mount'
-require 'chef/log'
+require "chef/provider/mount"
+require "chef/log"
class Chef
class Provider
class Mount
class Mount < Chef::Provider::Mount
+ provides :mount
+
def initialize(new_resource, run_context)
super
@real_device = nil
@@ -102,13 +104,13 @@ class Chef
command = "mount -t #{@new_resource.fstype}"
command << " -o #{@new_resource.options.join(',')}" unless @new_resource.options.nil? || @new_resource.options.empty?
command << case @new_resource.device_type
- when :device
- " #{device_real}"
- when :label
- " -L #{@new_resource.device}"
- when :uuid
- " -U #{@new_resource.device}"
- end
+ when :device
+ " #{device_real}"
+ when :label
+ " -L #{@new_resource.device}"
+ when :uuid
+ " -U #{@new_resource.device}"
+ end
command << " #{@new_resource.mount_point}"
shell_out!(command)
Chef::Log.debug("#{@new_resource} is mounted at #{@new_resource.mount_point}")
@@ -168,7 +170,7 @@ class Chef
found = false
::File.readlines("/etc/fstab").reverse_each do |line|
if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s/
- found = true
+ found = true
Chef::Log.debug("#{@new_resource} is removed from fstab")
next
else
@@ -191,7 +193,7 @@ class Chef
def device_should_exist?
( @new_resource.device != "none" ) &&
( not network_device? ) &&
- ( not %w[ cgroup tmpfs fuse ].include? @new_resource.fstype )
+ ( not %w{ cgroup tmpfs fuse }.include? @new_resource.fstype )
end
private
@@ -257,9 +259,9 @@ class Chef
def mount_options_unchanged?
@current_resource.fstype == @new_resource.fstype and
- @current_resource.options == @new_resource.options and
- @current_resource.dump == @new_resource.dump and
- @current_resource.pass == @new_resource.pass
+ @current_resource.options == @new_resource.options and
+ @current_resource.dump == @new_resource.dump and
+ @current_resource.pass == @new_resource.pass
end
end
diff --git a/lib/chef/provider/mount/solaris.rb b/lib/chef/provider/mount/solaris.rb
index d8cec24138..76f1ac0137 100644
--- a/lib/chef/provider/mount/solaris.rb
+++ b/lib/chef/provider/mount/solaris.rb
@@ -18,18 +18,20 @@
# limitations under the License.
#
-require 'chef/provider/mount'
-require 'chef/log'
-require 'forwardable'
+require "chef/provider/mount"
+require "chef/log"
+require "forwardable"
class Chef
class Provider
class Mount
# Mount Solaris File systems
class Solaris < Chef::Provider::Mount
+ provides :mount, platform: %w{openindiana opensolaris nexentacore omnios solaris2 smartos}
+
extend Forwardable
- VFSTAB = '/etc/vfstab'.freeze
+ VFSTAB = "/etc/vfstab".freeze
def_delegator :@new_resource, :device, :device
def_delegator :@new_resource, :device_type, :device_type
@@ -56,7 +58,7 @@ class Chef
a.whyrun("Assuming device #{device} would have been created")
end
- unless fsck_device == '-'
+ unless fsck_device == "-"
requirements.assert(:mount, :remount) do |a|
a.assertion { ::File.exist?(fsck_device) }
a.failure_message(Chef::Exceptions::Mount, "Device #{fsck_device} does not exist")
@@ -73,7 +75,7 @@ class Chef
def mount_fs
actual_options = options || []
- actual_options.delete('noauto')
+ actual_options.delete("noauto")
command = "mount -F #{fstype}"
command << " -o #{actual_options.join(',')}" unless actual_options.empty?
command << " #{device} #{mount_point}"
@@ -87,8 +89,8 @@ class Chef
def remount_fs
# FIXME: Should remount always do the remount or only if the options change?
actual_options = options || []
- actual_options.delete('noauto')
- mount_options = actual_options.empty? ? '' : ",#{actual_options.join(',')}"
+ actual_options.delete("noauto")
+ mount_options = actual_options.empty? ? "" : ",#{actual_options.join(',')}"
shell_out!("mount -o remount#{mount_options} #{mount_point}")
end
@@ -115,7 +117,7 @@ class Chef
end
def etc_tempfile
- yield Tempfile.open('vfstab', '/etc')
+ yield Tempfile.open("vfstab", "/etc")
end
def mount_options_unchanged?
@@ -127,7 +129,7 @@ class Chef
current_options == new_options &&
current_resource.dump == dump &&
current_resource.pass == pass &&
- current_resource.options.include?('noauto') == !mount_at_boot?
+ current_resource.options.include?("noauto") == !mount_at_boot?
end
def update_current_resource_state
@@ -148,7 +150,7 @@ class Chef
# /dev/dsk/c1t0d0s0 on / type ufs read/write/setuid/devices/intr/largefiles/logging/xattr/onerror=panic/dev=700040 on Tue May 1 11:33:55 2012
def mounted?
mounted = false
- shell_out!('mount -v').stdout.each_line do |line|
+ shell_out!("mount -v").stdout.each_line do |line|
case line
when /^#{device_regex}\s+on\s+#{Regexp.escape(mount_point)}\s+/
Chef::Log.debug("Special device #{device} is mounted as #{mount_point}")
@@ -180,14 +182,14 @@ class Chef
options = Regexp.last_match[4]
# Store the 'mount at boot' column from vfstab as the 'noauto' option
# in current_resource.options (linux style)
- if Regexp.last_match[3] == 'no'
+ if Regexp.last_match[3] == "no"
if options.nil? || options.empty?
- options = 'noauto'
+ options = "noauto"
else
- options += ',noauto'
+ options += ",noauto"
end
end
- pass = (Regexp.last_match[2] == '-') ? 0 : Regexp.last_match[2].to_i
+ pass = (Regexp.last_match[2] == "-") ? 0 : Regexp.last_match[2].to_i
Chef::Log.debug("Found mount #{device} to #{mount_point} in #{VFSTAB}")
next
when /^[-\/\w]+\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+/
@@ -200,16 +202,16 @@ class Chef
end
def device_should_exist?
- !%w(tmpfs nfs ctfs proc mntfs objfs sharefs fd smbfs vxfs).include?(fstype)
+ !%w{tmpfs nfs ctfs proc mntfs objfs sharefs fd smbfs vxfs}.include?(fstype)
end
def mount_at_boot?
- options.nil? || !options.include?('noauto')
+ options.nil? || !options.include?("noauto")
end
def vfstab_write(contents)
etc_tempfile do |f|
- f.write(contents.join(''))
+ f.write(contents.join(""))
f.close
# move, preserving modes of destination file
mover = Chef::FileContentManagement::Deploy.strategy(true)
@@ -219,13 +221,13 @@ class Chef
def vfstab_entry
actual_options = unless options.nil?
- tempops = options.dup
- tempops.delete('noauto')
- tempops
- end
- autostr = mount_at_boot? ? 'yes' : 'no'
- passstr = pass == 0 ? '-' : pass
- optstr = (actual_options.nil? || actual_options.empty?) ? '-' : actual_options.join(',')
+ tempops = options.dup
+ tempops.delete("noauto")
+ tempops
+ end
+ autostr = mount_at_boot? ? "yes" : "no"
+ passstr = pass == 0 ? "-" : pass
+ optstr = (actual_options.nil? || actual_options.empty?) ? "-" : actual_options.join(",")
"\n#{device}\t#{fsck_device}\t#{mount_point}\t#{fstype}\t#{passstr}\t#{autostr}\t#{optstr}\n"
end
@@ -234,7 +236,7 @@ class Chef
found = false
::File.readlines(VFSTAB).reverse_each do |line|
if !found && line =~ /^#{device_regex}\s+\S+\s+#{Regexp.escape(mount_point)}/
- found = true
+ found = true
Chef::Log.debug("#{new_resource} is removed from vfstab")
next
end
@@ -252,7 +254,7 @@ class Chef
def options_remove_noauto(temp_options)
new_options = []
new_options += temp_options.nil? ? [] : temp_options
- new_options.delete('noauto')
+ new_options.delete("noauto")
new_options
end
diff --git a/lib/chef/provider/mount/windows.rb b/lib/chef/provider/mount/windows.rb
index 87873474b3..0cf50d07bf 100644
--- a/lib/chef/provider/mount/windows.rb
+++ b/lib/chef/provider/mount/windows.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require 'chef/provider/mount'
+require "chef/provider/mount"
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require 'chef/util/windows/net_use'
- require 'chef/util/windows/volume'
+ require "chef/util/windows/net_use"
+ require "chef/util/windows/volume"
end
class Chef
diff --git a/lib/chef/provider/ohai.rb b/lib/chef/provider/ohai.rb
index a6b5ab5daa..6535805013 100644
--- a/lib/chef/provider/ohai.rb
+++ b/lib/chef/provider/ohai.rb
@@ -16,11 +16,12 @@
# limitations under the License.
#
-require 'ohai'
+require "ohai"
class Chef
class Provider
class Ohai < Chef::Provider
+ provides :ohai
def whyrun_supported?
true
diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb
new file mode 100644
index 0000000000..071b4b0462
--- /dev/null
+++ b/lib/chef/provider/osx_profile.rb
@@ -0,0 +1,254 @@
+#
+# Author:: Nate Walck (<nate.walck@gmail.com>)
+# Copyright:: Copyright (c) 2015 Facebook, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/log"
+require "chef/provider"
+require "chef/resource"
+require "chef/resource/file"
+require "uuidtools"
+
+class Chef
+ class Provider
+ class OsxProfile < Chef::Provider
+ include Chef::Mixin::Command
+ provides :osx_profile, os: "darwin"
+ provides :osx_config_profile, os: "darwin"
+
+ def whyrun_supported?
+ true
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::OsxProfile.new(@new_resource.name)
+ @current_resource.profile_name(@new_resource.profile_name)
+
+ all_profiles = get_installed_profiles
+ @new_resource.profile(
+ @new_resource.profile ||
+ @new_resource.profile_name
+ )
+
+ @new_profile_hash = get_profile_hash(@new_resource.profile)
+ @new_profile_hash["PayloadUUID"] =
+ config_uuid(@new_profile_hash) if @new_profile_hash
+
+ if @new_profile_hash
+ @new_profile_identifier = @new_profile_hash["PayloadIdentifier"]
+ else
+ @new_profile_identifier = @new_resource.identifier ||
+ @new_resource.profile_name
+ end
+
+ current_profile = all_profiles["_computerlevel"].find {
+ |item| item["ProfileIdentifier"] ==
+ @new_profile_identifier
+ }
+ @current_resource.profile(current_profile)
+
+ end
+
+ def define_resource_requirements
+ requirements.assert(:remove) do |a|
+ if @new_profile_identifier
+ a.assertion {
+ !@new_profile_identifier.nil? and
+ !@new_profile_identifier.end_with?(".mobileconfig") and
+ /^\w+(?:\.\w+)+$/.match(@new_profile_identifier)
+ }
+ a.failure_message RuntimeError, "when removing using the identifier attribute, it must match the profile identifier"
+ else
+ new_profile_name = @new_resource.profile_name
+ a.assertion {
+ !new_profile_name.end_with?(".mobileconfig") and
+ /^\w+(?:\.\w+)+$/.match(new_profile_name)
+ }
+ a.failure_message RuntimeError, "When removing by resource name, it must match the profile identifier "
+ end
+ end
+
+ requirements.assert(:install) do |a|
+ if @new_profile_hash.is_a?(Hash)
+ a.assertion {
+ @new_profile_hash.include?("PayloadIdentifier")
+ }
+ a.failure_message RuntimeError, "The specified profile does not seem to be valid"
+ end
+ if @new_profile_hash.is_a?(String)
+ a.assertion {
+ @new_profile_hash.end_with?(".mobileconfig")
+ }
+ a.failure_message RuntimeError, "#{new_profile_hash}' is not a valid profile"
+ end
+ end
+ end
+
+ def action_install
+ unless profile_installed?
+ converge_by("install profile #{@new_profile_identifier}") do
+ profile_path = write_profile_to_disk
+ install_profile(profile_path)
+ get_installed_profiles(true)
+ end
+ end
+ end
+
+ def action_remove
+ # Clean up profile after removing it
+ if profile_installed?
+ converge_by("remove profile #{@new_profile_identifier}") do
+ remove_profile
+ get_installed_profiles(true)
+ end
+ end
+ end
+
+ def load_profile_hash(new_profile)
+ # file must exist in cookbook
+ if new_profile.end_with?(".mobileconfig")
+ unless cookbook_file_available?(new_profile)
+ error_string = "#{self}: '#{new_profile}' not found in cookbook"
+ raise Chef::Exceptions::FileNotFound, error_string
+ end
+ cookbook_profile = cache_cookbook_profile(new_profile)
+ return read_plist(cookbook_profile)
+ else
+ return nil
+ end
+ end
+
+ def cookbook_file_available?(cookbook_file)
+ run_context.has_cookbook_file_in_cookbook?(
+ @new_resource.cookbook_name, cookbook_file
+ )
+ end
+
+ def get_cache_dir
+ cache_dir = Chef::FileCache.create_cache_path(
+ "profiles/#{@new_resource.cookbook_name}"
+ )
+ end
+
+ def cache_cookbook_profile(cookbook_file)
+ Chef::FileCache.create_cache_path(
+ ::File.join(
+ "profiles",
+ @new_resource.cookbook_name,
+ ::File.dirname(cookbook_file),
+ )
+ )
+ remote_file = Chef::Resource::CookbookFile.new(
+ ::File.join(
+ get_cache_dir,
+ "#{cookbook_file}.remote",
+ ),
+ run_context,
+ )
+ remote_file.cookbook_name = @new_resource.cookbook_name
+ remote_file.source(cookbook_file)
+ remote_file.backup(false)
+ remote_file.run_action(:create)
+ remote_file.path
+ end
+
+ def get_profile_hash(new_profile)
+ if new_profile.is_a?(Hash)
+ return new_profile
+ elsif new_profile.is_a?(String)
+ return load_profile_hash(new_profile)
+ end
+ end
+
+ def config_uuid(profile)
+ # Make a UUID of the profile contents and return as string
+ UUIDTools::UUID.sha1_create(
+ UUIDTools::UUID_DNS_NAMESPACE,
+ profile.to_s,
+ ).to_s
+ end
+
+ def write_profile_to_disk
+ @new_resource.path(Chef::FileCache.create_cache_path("profiles"))
+ tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
+ tempfile.write(@new_profile_hash.to_plist)
+ tempfile.close
+ tempfile.path
+ end
+
+ def install_profile(profile_path)
+ cmd = "profiles -I -F '#{profile_path}'"
+ Chef::Log.debug("cmd: #{cmd}")
+ shellout_results = shell_out(cmd)
+ shellout_results.exitstatus
+ end
+
+ def remove_profile
+ cmd = "profiles -R -p '#{@new_profile_identifier}'"
+ Chef::Log.debug("cmd: #{cmd}")
+ shellout_results = shell_out(cmd)
+ shellout_results.exitstatus
+ end
+
+ def get_installed_profiles(update=nil)
+ if update
+ node.run_state[:config_profiles] = query_installed_profiles
+ else
+ node.run_state[:config_profiles] ||= query_installed_profiles
+ end
+ end
+
+ def query_installed_profiles
+ # Dump all profile metadata to a tempfile
+ tempfile = generate_tempfile
+ write_installed_profiles(tempfile)
+ installed_profiles = read_plist(tempfile)
+ Chef::Log.debug("Saved profiles to run_state")
+ # Clean up the temp file as we do not need it anymore
+ ::File.unlink(tempfile)
+ installed_profiles
+ end
+
+ def generate_tempfile
+ tempfile = ::Dir::Tmpname.create("allprofiles.plist") {}
+ end
+
+ def write_installed_profiles(tempfile)
+ cmd = "profiles -P -o '#{tempfile}'"
+ shell_out!(cmd)
+ end
+
+ def read_plist(xml_file)
+ Plist::parse_xml(xml_file)
+ end
+
+ def profile_installed?
+ # Profile Identifier and UUID must match a currently installed profile
+ if @current_resource.profile.nil? or @current_resource.profile.empty?
+ false
+ else
+ if @new_resource.action.include?(:remove)
+ true
+ else
+ @current_resource.profile["ProfileUUID"] ==
+ @new_profile_hash["PayloadUUID"]
+ end
+ end
+ end
+
+ end
+ end
+end
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
diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb
index 107f914c05..29e2d1eb94 100644
--- a/lib/chef/provider/package/aix.rb
+++ b/lib/chef/provider/package/aix.rb
@@ -16,16 +16,17 @@
# limitations under the License.
#
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
+require "chef/mixin/get_source_from_package"
class Chef
class Provider
class Package
class Aix < Chef::Provider::Package
+ provides :package, os: "aix"
provides :bff_package, os: "aix"
include Chef::Mixin::GetSourceFromPackage
@@ -46,13 +47,12 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @new_resource.version(nil)
if @new_resource.source
@package_source_found = ::File.exists?(@new_resource.source)
if @package_source_found
Chef::Log.debug("#{@new_resource} checking pkg status")
- ret = shell_out("installp -L -d #{@new_resource.source}")
+ ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}")
ret.stdout.each_line do | line |
case line
when /#{@new_resource.package_name}:/
@@ -60,11 +60,12 @@ class Chef
@new_resource.version(fields[2])
end
end
+ raise Chef::Exceptions::Package, "package source #{@new_resource.source} does not provide package #{@new_resource.package_name}" unless @new_resource.version
end
end
Chef::Log.debug("#{@new_resource} checking install state")
- ret = shell_out("lslpp -lcq #{@current_resource.package_name}")
+ ret = shell_out_with_timeout("lslpp -lcq #{@current_resource.package_name}")
ret.stdout.each_line do | line |
case line
when /#{@current_resource.package_name}/
@@ -83,7 +84,7 @@ class Chef
def candidate_version
return @candidate_version if @candidate_version
- ret = shell_out("installp -L -d #{@new_resource.source}")
+ ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}")
ret.stdout.each_line do | line |
case line
when /\w:#{Regexp.escape(@new_resource.package_name)}:(.*)/
@@ -109,10 +110,10 @@ class Chef
def install_package(name, version)
Chef::Log.debug("#{@new_resource} package install options: #{@new_resource.options}")
if @new_resource.options.nil?
- shell_out!( "installp -aYF -d #{@new_resource.source} #{@new_resource.package_name}" )
+ shell_out_with_timeout!( "installp -aYF -d #{@new_resource.source} #{@new_resource.package_name}" )
Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
else
- shell_out!( "installp -aYF #{expand_options(@new_resource.options)} -d #{@new_resource.source} #{@new_resource.package_name}" )
+ shell_out_with_timeout!( "installp -aYF #{expand_options(@new_resource.options)} -d #{@new_resource.source} #{@new_resource.package_name}" )
Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
end
end
@@ -121,10 +122,10 @@ class Chef
def remove_package(name, version)
if @new_resource.options.nil?
- shell_out!( "installp -u #{name}" )
+ shell_out_with_timeout!( "installp -u #{name}" )
Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
else
- shell_out!( "installp -u #{expand_options(@new_resource.options)} #{name}" )
+ shell_out_with_timeout!( "installp -u #{expand_options(@new_resource.options)} #{name}" )
Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
end
end
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index e426b51992..03cbff4d1c 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -16,15 +16,16 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
class Chef
class Provider
class Package
class Apt < Chef::Provider::Package
+ provides :package, platform_family: "debian"
provides :apt_package, os: "linux"
# return [Hash] mapping of package name to Boolean value
@@ -47,7 +48,7 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { !@new_resource.source }
- a.failure_message(Chef::Exceptions::Package, 'apt package provider cannot handle source attribute. Use dpkg provider instead')
+ a.failure_message(Chef::Exceptions::Package, "apt package provider cannot handle source attribute. Use dpkg provider instead")
end
end
@@ -62,11 +63,11 @@ class Chef
installed_version = nil
candidate_version = nil
- shell_out!("apt-cache#{expand_options(default_release_options)} policy #{pkg}", {:timeout=>900}).stdout.each_line do |line|
+ shell_out_with_timeout!("apt-cache#{expand_options(default_release_options)} policy #{pkg}").stdout.each_line do |line|
case line
when /^\s{2}Installed: (.+)$/
installed_version = $1
- if installed_version == '(none)'
+ if installed_version == "(none)"
Chef::Log.debug("#{@new_resource} current version is nil")
installed_version = nil
else
@@ -75,10 +76,10 @@ class Chef
end
when /^\s{2}Candidate: (.+)$/
candidate_version = $1
- if candidate_version == '(none)'
+ if candidate_version == "(none)"
# This may not be an appropriate assumption, but it shouldn't break anything that already worked -- btm
is_virtual_package = true
- showpkg = shell_out!("apt-cache showpkg #{pkg}", {:timeout => 900}).stdout
+ showpkg = shell_out_with_timeout!("apt-cache showpkg #{pkg}").stdout
providers = Hash.new
showpkg.rpartition(/Reverse Provides: ?#{$/}/)[2].each_line do |line|
provider, version = line.split
@@ -140,7 +141,7 @@ class Chef
version_array = [ version ].flatten
package_name = name_array.zip(version_array).map do |n, v|
is_virtual_package[n] ? n : "#{n}=#{v}"
- end.join(' ')
+ end.join(" ")
run_noninteractive("apt-get -q -y#{expand_options(default_release_options)}#{expand_options(@new_resource.options)} install #{package_name}")
end
@@ -149,12 +150,12 @@ class Chef
end
def remove_package(name, version)
- package_name = [ name ].flatten.join(' ')
+ package_name = [ name ].flatten.join(" ")
run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}")
end
def purge_package(name, version)
- package_name = [ name ].flatten.join(' ')
+ package_name = [ name ].flatten.join(" ")
run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} purge #{package_name}")
end
@@ -164,7 +165,7 @@ class Chef
end
def reconfig_package(name, version)
- package_name = [ name ].flatten.join(' ')
+ package_name = [ name ].flatten.join(" ")
Chef::Log.info("#{@new_resource} reconfiguring")
run_noninteractive("dpkg-reconfigure #{package_name}")
end
@@ -175,7 +176,7 @@ class Chef
# interactive prompts. Command is run with default localization rather
# than forcing locale to "C", so command output may not be stable.
def run_noninteractive(command)
- shell_out!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil }, :timeout => @new_resource.timeout)
+ shell_out_with_timeout!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil })
end
end
diff --git a/lib/chef/provider/package/chocolatey.rb b/lib/chef/provider/package/chocolatey.rb
new file mode 100644
index 0000000000..87abe299b6
--- /dev/null
+++ b/lib/chef/provider/package/chocolatey.rb
@@ -0,0 +1,257 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/provider/package"
+require "chef/resource/chocolatey_package"
+require "chef/mixin/powershell_out"
+
+class Chef
+ class Provider
+ class Package
+ class Chocolatey < Chef::Provider::Package
+ include Chef::Mixin::PowershellOut
+
+ provides :chocolatey_package, os: "windows"
+
+ # Declare that our arguments should be arrays
+ use_multipackage_api
+
+ # Responsible for building the current_resource.
+ #
+ # @return [Chef::Resource::ChocolateyPackage] the current_resource
+ def load_current_resource
+ @current_resource = Chef::Resource::ChocolateyPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(build_current_versions)
+ current_resource
+ end
+
+ def define_resource_requirements
+ super
+
+ # Chocolatey source attribute points to an alternate feed
+ # and not a package specific alternate source like other providers
+ # so we want to assert candidates exist for the alternate source
+ requirements.assert(:upgrade, :install) do |a|
+ a.assertion { candidates_exist_for_all_uninstalled? }
+ a.failure_message(Chef::Exceptions::Package, "No candidate version available for #{packages_missing_candidates.join(", ")}")
+ a.whyrun("Assuming a repository that offers #{packages_missing_candidates.join(", ")} would have been configured")
+ end
+ end
+
+ # Lazy initializer for candidate_version. A nil value means that there is no candidate
+ # version and the package is not installable (generally an error).
+ #
+ # @return [Array] list of candidate_versions indexed same as new_resource.package_name/version
+ def candidate_version
+ @candidate_version ||= build_candidate_versions
+ end
+
+ # Install multiple packages via choco.exe
+ #
+ # @param names [Array<String>] array of package names to install
+ # @param versions [Array<String>] array of versions to install
+ def install_package(names, versions)
+ name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) }
+
+ name_nil_versions = name_versions_to_install.select { |n,v| v.nil? }
+ name_has_versions = name_versions_to_install.reject { |n,v| v.nil? }
+
+ # choco does not support installing multiple packages with version pins
+ name_has_versions.each do |name, version|
+ choco_command("install -y -version", version, cmd_args, name)
+ end
+
+ # but we can do all the ones without version pins at once
+ unless name_nil_versions.empty?
+ cmd_names = name_nil_versions.keys
+ choco_command("install -y", cmd_args, *cmd_names)
+ end
+ end
+
+ # Upgrade multiple packages via choco.exe
+ #
+ # @param names [Array<String>] array of package names to install
+ # @param versions [Array<String>] array of versions to install
+ def upgrade_package(names, versions)
+ name_versions_to_install = desired_name_versions.select { |n, v| lowercase_names(names).include?(n) }
+
+ name_nil_versions = name_versions_to_install.select { |n,v| v.nil? }
+ name_has_versions = name_versions_to_install.reject { |n,v| v.nil? }
+
+ # choco does not support installing multiple packages with version pins
+ name_has_versions.each do |name, version|
+ choco_command("upgrade -y -version", version, cmd_args, name)
+ end
+
+ # but we can do all the ones without version pins at once
+ unless name_nil_versions.empty?
+ cmd_names = name_nil_versions.keys
+ choco_command("upgrade -y", cmd_args, *cmd_names)
+ end
+ end
+
+ # Remove multiple packages via choco.exe
+ #
+ # @param names [Array<String>] array of package names to install
+ # @param versions [Array<String>] array of versions to install
+ def remove_package(names, versions)
+ choco_command("uninstall -y", cmd_args, *names)
+ end
+
+ # Support :uninstall as an action in order for users to easily convert
+ # from the `chocolatey` provider in the cookbook. It is, however,
+ # already deprecated.
+ def action_uninstall
+ Chef::Log.deprecation "The use of action :uninstall on the chocolatey_package provider is deprecated, please use :remove"
+ action_remove
+ end
+
+ # Choco does not have dpkg's distinction between purge and remove
+ alias_method :purge_package, :remove_package
+
+ # Override the superclass check. The semantics for our new_resource.source is not files to
+ # install from, but like the rubygem provider's sources which are more like repos.
+ def check_resource_semantics!
+ end
+
+ private
+
+ # Magic to find where chocolatey is installed in the system, and to
+ # return the full path of choco.exe
+ #
+ # @return [String] full path of choco.exe
+ def choco_exe
+ @choco_exe ||=
+ ::File.join(
+ powershell_out!(
+ "[System.Environment]::GetEnvironmentVariable('ChocolateyInstall', 'MACHINE')"
+ ).stdout.chomp,
+ "bin",
+ "choco.exe",
+ )
+ end
+
+ # Helper to dispatch a choco command through shell_out using the timeout
+ # set on the new resource, with nice command formatting.
+ #
+ # @param args [String] variable number of string arguments
+ # @return [Mixlib::ShellOut] object returned from shell_out!
+ def choco_command(*args)
+ shell_out_with_timeout!(args_to_string(choco_exe, *args))
+ end
+
+ # Use the available_packages Hash helper to create an array suitable for
+ # using in candidate_version
+ #
+ # @return [Array] list of candidate_version, same index as new_resource.package_name/version
+ def build_candidate_versions
+ new_resource.package_name.map do |package_name|
+ available_packages[package_name.downcase]
+ end
+ end
+
+ # Use the installed_packages Hash helper to create an array suitable for
+ # using in current_resource.version
+ #
+ # @return [Array] list of candidate_version, same index as new_resource.package_name/version
+ def build_current_versions
+ new_resource.package_name.map do |package_name|
+ installed_packages[package_name.downcase]
+ end
+ end
+
+ # Helper to construct Hash of names-to-versions, requested on the new_resource.
+ # If new_resource.version is nil, then all values will be nil.
+ #
+ # @return [Hash] Mapping of requested names to versions
+ def desired_name_versions
+ desired_versions = new_resource.version || new_resource.package_name.map { nil }
+ Hash[*lowercase_names(new_resource.package_name).zip(desired_versions).flatten]
+ end
+
+ # Helper to construct optional args out of new_resource
+ #
+ # @return [String] options from new_resource or empty string
+ def cmd_args
+ cmd_args = [ new_resource.options ]
+ cmd_args.push( "-source #{new_resource.source}" ) if new_resource.source
+ args_to_string(*cmd_args)
+ end
+
+ # Helper to nicely convert variable string args into a single command line. It
+ # will compact nulls or empty strings and join arguments with single spaces, without
+ # introducing any double-spaces for missing args.
+ #
+ # @param args [String] variable number of string arguments
+ # @return [String] nicely concatenated string or empty string
+ def args_to_string(*args)
+ args.reject {|i| i.nil? || i == "" }.join(" ")
+ end
+
+ # Available packages in chocolatey as a Hash of names mapped to versions
+ # If pinning a package to a specific version, filter out all non matching versions
+ # (names are downcased for case-insensitive matching)
+ #
+ # @return [Hash] name-to-version mapping of available packages
+ def available_packages
+ @available_packages ||=
+ begin
+ cmd = [ "list -ar #{package_name_array.join ' '}" ]
+ cmd.push( "-source #{new_resource.source}" ) if new_resource.source
+ parse_list_output(*cmd).each_with_object({}) do |name_version, available|
+ name, version = name_version
+ if desired_name_versions[name].nil? || desired_name_versions[name] == version
+ available[name] = version
+ end
+ end
+ end
+ end
+
+ # Installed packages in chocolatey as a Hash of names mapped to versions
+ # (names are downcased for case-insensitive matching)
+ #
+ # @return [Hash] name-to-version mapping of installed packages
+ def installed_packages
+ @installed_packages ||= Hash[*parse_list_output("list -l -r").flatten]
+ end
+
+ # Helper to convert choco.exe list output to a Hash
+ # (names are downcased for case-insenstive matching)
+ #
+ # @param cmd [String] command to run
+ # @return [Array] list output converted to ruby Hash
+ def parse_list_output(*args)
+ list = []
+ choco_command(*args).stdout.each_line do |line|
+ name, version = line.split("|")
+ list << [ name.downcase, version.chomp ]
+ end
+ list
+ end
+
+ # Helper to downcase all names in an array
+ #
+ # @param names [Array] original mixed case names
+ # @return [Array] same names in lower case
+ def lowercase_names(names)
+ names.map { |name| name.downcase }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/dpkg.rb b/lib/chef/provider/package/dpkg.rb
index 11691a2479..bcf7cff4e4 100644
--- a/lib/chef/provider/package/dpkg.rb
+++ b/lib/chef/provider/package/dpkg.rb
@@ -16,131 +16,208 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
+require "chef/provider/package"
+require "chef/resource/package"
class Chef
class Provider
class Package
class Dpkg < Chef::Provider::Package
- # http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
- DPKG_INFO = /([a-z\d\-\+\.]+)\t([\w\d.~:-]+)/
+ DPKG_REMOVED = /^Status: deinstall ok config-files/
DPKG_INSTALLED = /^Status: install ok installed/
DPKG_VERSION = /^Version: (.+)$/
provides :dpkg_package, os: "linux"
- include Chef::Mixin::GetSourceFromPackage
+ use_multipackage_api
+ use_package_name_for_source
def define_resource_requirements
super
- requirements.assert(:install) do |a|
- a.assertion{ not @new_resource.source.nil? }
- a.failure_message Chef::Exceptions::Package, "Source for package #{@new_resource.name} required for action install"
+
+ requirements.assert(:install, :upgrade) do |a|
+ a.assertion { !resolved_source_array.compact.empty? }
+ a.failure_message Chef::Exceptions::Package, "#{new_resource} the source property is required for action :install or :upgrade"
end
- # TODO this was originally written for any action in which .source is provided
- # but would it make more sense to only look at source if the action is :install?
- requirements.assert(:all_actions) do |a|
- a.assertion { @source_exists }
- a.failure_message Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
- a.whyrun "Assuming it would have been previously downloaded."
+ requirements.assert(:install, :upgrade) do |a|
+ a.assertion { source_files_exist? }
+ a.failure_message Chef::Exceptions::Package, "#{new_resource} source file(s) do not exist: #{missing_sources}"
+ a.whyrun "Assuming they would have been previously created."
end
end
def load_current_resource
- @source_exists = true
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
- @new_resource.version(nil)
-
- if @new_resource.source
- @source_exists = ::File.exists?(@new_resource.source)
- if @source_exists
- # Get information from the package if supplied
- Chef::Log.debug("#{@new_resource} checking dpkg status")
-
- shell_out("dpkg-deb -W #{@new_resource.source}").stdout.each_line do |line|
- if pkginfo = DPKG_INFO.match(line)
- @current_resource.package_name(pkginfo[1])
- @new_resource.version(pkginfo[2])
- @candidate_version = pkginfo[2]
- end
- end
- else
- # Source provided but not valid means we can't safely do further processing
- return
- end
-
+ @current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+
+ if source_files_exist?
+ @candidate_version = get_candidate_version
+ current_resource.package_name(get_package_name)
+ # if the source file exists then our package_name is right
+ current_resource.version(get_current_version_from(current_package_name_array))
+ elsif !installing?
+ # we can't do this if we're installing with no source, because our package_name
+ # is probably not right.
+ #
+ # if we're removing or purging we don't use source, and our package_name must
+ # be right so we can do this.
+ #
+ # we don't error here on the dpkg command since we'll handle the exception or
+ # the why-run message in define_resource_requirements.
+ current_resource.version(get_current_version_from(current_package_name_array))
end
- # Check to see if it is installed
- package_installed = nil
- Chef::Log.debug("#{@new_resource} checking install state")
- status = shell_out("dpkg -s #{@current_resource.package_name}")
+ current_resource
+ end
+
+ def install_package(name, version)
+ sources = name.map { |n| name_sources[n] }
+ Chef::Log.info("#{new_resource} installing package(s): #{name.join(' ')}")
+ run_noninteractive("dpkg -i", new_resource.options, *sources)
+ end
+
+ def remove_package(name, version)
+ Chef::Log.info("#{new_resource} removing package(s): #{name.join(' ')}")
+ run_noninteractive("dpkg -r", new_resource.options, *name)
+ end
+
+ def purge_package(name, version)
+ Chef::Log.info("#{new_resource} purging packages(s): #{name.join(' ')}")
+ run_noninteractive("dpkg -P", new_resource.options, *name)
+ end
+
+ def upgrade_package(name, version)
+ install_package(name, version)
+ end
+
+ def preseed_package(preseed_file)
+ Chef::Log.info("#{new_resource} pre-seeding package installation instructions")
+ run_noninteractive("debconf-set-selections", *preseed_file)
+ end
+
+ def reconfig_package(name, version)
+ Chef::Log.info("#{new_resource} reconfiguring")
+ run_noninteractive("dpkg-reconfigure", *name)
+ end
+
+ # Override the superclass check. Multiple sources are required here.
+ def check_resource_semantics!
+ end
+
+ private
+
+ def read_current_version_of_package(package_name)
+ Chef::Log.debug("#{new_resource} checking install state of #{package_name}")
+ status = shell_out_with_timeout!("dpkg -s #{package_name}", returns: [0, 1])
+ package_installed = false
status.stdout.each_line do |line|
case line
+ when DPKG_REMOVED
+ # if we are 'purging' then we consider 'removed' to be 'installed'
+ package_installed = true if action == :purge
when DPKG_INSTALLED
package_installed = true
when DPKG_VERSION
if package_installed
- Chef::Log.debug("#{@new_resource} current version is #{$1}")
- @current_resource.version($1)
+ Chef::Log.debug("#{new_resource} current version is #{$1}")
+ return $1
end
end
end
+ return nil
+ end
- unless status.exitstatus == 0 || status.exitstatus == 1
- raise Chef::Exceptions::Package, "dpkg failed - #{status.inspect}!"
+ def get_current_version_from(array)
+ array.map do |name|
+ read_current_version_of_package(name)
end
+ end
- @current_resource
+ # Runs command via shell_out_with_timeout with magic environment to disable
+ # interactive prompts.
+ def run_noninteractive(*command)
+ shell_out_with_timeout!(a_to_s(*command), :env => { "DEBIAN_FRONTEND" => "noninteractive" })
end
- def install_package(name, version)
- Chef::Log.info("#{@new_resource} installing #{@new_resource.source}")
- run_noninteractive(
- "dpkg -i#{expand_options(@new_resource.options)} #{@new_resource.source}"
- )
+ # Returns true if all sources exist. Returns false if any do not, or if no
+ # sources were specified.
+ #
+ # @return [Boolean] True if all sources exist
+ def source_files_exist?
+ resolved_source_array.all? {|s| s && ::File.exist?(s) }
end
- def remove_package(name, version)
- Chef::Log.info("#{@new_resource} removing #{@new_resource.package_name}")
- run_noninteractive(
- "dpkg -r#{expand_options(@new_resource.options)} #{@new_resource.package_name}"
- )
+ # Helper to return all the nanes of the missing sources for error messages.
+ #
+ # @return [Array<String>] Array of missing sources
+ def missing_sources
+ resolved_source_array.select {|s| s.nil? || !::File.exist?(s) }
end
- def purge_package(name, version)
- Chef::Log.info("#{@new_resource} purging #{@new_resource.package_name}")
- run_noninteractive(
- "dpkg -P#{expand_options(@new_resource.options)} #{@new_resource.package_name}"
- )
+ def current_package_name_array
+ [ current_resource.package_name ].flatten
end
- def upgrade_package(name, version)
- install_package(name, version)
+ # Helper to construct Hash of names-to-sources.
+ #
+ # @return [Hash] Mapping of package names to sources
+ def name_sources
+ @name_sources =
+ begin
+ Hash[*package_name_array.zip(resolved_source_array).flatten]
+ end
end
- def preseed_package(preseed_file)
- Chef::Log.info("#{@new_resource} pre-seeding package installation instructions")
- run_noninteractive("debconf-set-selections #{preseed_file}")
+ # Helper to construct Hash of names-to-package-information.
+ #
+ # @return [Hash] Mapping of package names to package information
+ def name_pkginfo
+ @name_pkginfo ||=
+ begin
+ pkginfos = resolved_source_array.map do |src|
+ Chef::Log.debug("#{new_resource} checking #{src} dpkg status")
+ status = shell_out_with_timeout!("dpkg-deb -W #{src}")
+ status.stdout
+ end
+ Hash[*package_name_array.zip(pkginfos).flatten]
+ end
end
- def reconfig_package(name, version)
- Chef::Log.info("#{@new_resource} reconfiguring")
- run_noninteractive("dpkg-reconfigure #{name}")
+ def name_candidate_version
+ @name_candidate_version ||=
+ begin
+ Hash[name_pkginfo.map {|k, v| [k, v ? v.split("\t")[1].strip : nil] }]
+ end
+ end
+
+ def name_package_name
+ @name_package_name ||=
+ begin
+ Hash[name_pkginfo.map {|k, v| [k, v ? v.split("\t")[0] : nil] }]
+ end
+ end
+
+ # Return candidate version array from pkg-deb -W against the source file(s).
+ #
+ # @return [Array] Array of candidate versions read from the source files
+ def get_candidate_version
+ package_name_array.map { |name| name_candidate_version[name] }
+ end
+
+ # Return package names from the candidate source file(s).
+ #
+ # @return [Array] Array of actual package names read from the source files
+ def get_package_name
+ package_name_array.map { |name| name_package_name[name] }
end
- # Runs command via shell_out with magic environment to disable
- # interactive prompts. Command is run with default localization rather
- # than forcing locale to "C", so command output may not be stable.
+ # Since upgrade just calls install, this is a helper to determine
+ # if our action means that we'll be calling install_package.
#
- # FIXME: This should be "LC_ALL" => "en_US.UTF-8" in order to stabilize the output and get UTF-8
- def run_noninteractive(command)
- shell_out!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil })
+ # @return [Boolean] true if we're doing :install or :upgrade
+ def installing?
+ [:install, :upgrade].include?(action)
end
end
diff --git a/lib/chef/provider/package/easy_install.rb b/lib/chef/provider/package/easy_install.rb
index 90727b738d..91bb54999b 100644
--- a/lib/chef/provider/package/easy_install.rb
+++ b/lib/chef/provider/package/easy_install.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
class Chef
class Provider
@@ -32,10 +32,10 @@ class Chef
begin
# first check to see if we can import it
- output = shell_out!("#{python_binary_path} -c \"import #{name}\"", :returns=>[0,1]).stderr
+ output = shell_out_with_timeout!("#{python_binary_path} -c \"import #{name}\"", :returns=>[0,1]).stderr
if output.include? "ImportError"
# then check to see if its on the path
- output = shell_out!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout
+ output = shell_out_with_timeout!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout
if output.downcase.include? "#{name.downcase}"
check = true
end
@@ -51,12 +51,12 @@ class Chef
def easy_install_binary_path
path = @new_resource.easy_install_binary
- path ? path : 'easy_install'
+ path ? path : "easy_install"
end
def python_binary_path
path = @new_resource.python_binary
- path ? path : 'python'
+ path ? path : "python"
end
def module_name
@@ -67,18 +67,17 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @current_resource.version(nil)
# get the currently installed version if installed
package_version = nil
if install_check(module_name)
begin
- output = shell_out!("#{python_binary_path} -c \"import #{module_name}; print #{module_name}.__version__\"").stdout
+ output = shell_out_with_timeout!("#{python_binary_path} -c \"import #{module_name}; print #{module_name}.__version__\"").stdout
package_version = output.strip
rescue
- output = shell_out!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout
+ output = shell_out_with_timeout!("#{python_binary_path} -c \"import sys; print sys.path\"", :returns=>[0,1]).stdout
- output_array = output.gsub(/[\[\]]/,'').split(/\s*,\s*/)
+ output_array = output.gsub(/[\[\]]/,"").split(/\s*,\s*/)
package_path = ""
output_array.each do |entry|
@@ -107,7 +106,7 @@ class Chef
return @candidate_version if @candidate_version
# do a dry run to get the latest version
- result = shell_out!("#{easy_install_binary_path} -n #{@new_resource.package_name}", :returns=>[0,1])
+ result = shell_out_with_timeout!("#{easy_install_binary_path} -n #{@new_resource.package_name}", :returns=>[0,1])
@candidate_version = result.stdout[/(.*)Best match: (.*) (.*)$/, 3]
@candidate_version
end
diff --git a/lib/chef/provider/package/freebsd/base.rb b/lib/chef/provider/package/freebsd/base.rb
index 6a3b97a4fd..4957812e99 100644
--- a/lib/chef/provider/package/freebsd/base.rb
+++ b/lib/chef/provider/package/freebsd/base.rb
@@ -19,9 +19,9 @@
# limitations under the License.
#
-require 'chef/resource/package'
-require 'chef/provider/package'
-require 'chef/mixin/get_source_from_package'
+require "chef/resource/package"
+require "chef/provider/package"
+require "chef/mixin/get_source_from_package"
class Chef
class Provider
@@ -47,7 +47,7 @@ class Chef
# Otherwise look up the path to the ports directory using 'whereis'
else
- whereis = shell_out!("whereis -s #{port}", :env => nil)
+ whereis = shell_out_with_timeout!("whereis -s #{port}", :env => nil)
unless path = whereis.stdout[/^#{Regexp.escape(port)}:\s+(.+)$/, 1]
raise Chef::Exceptions::Package, "Could not find port with the name #{port}"
end
@@ -57,7 +57,7 @@ class Chef
def makefile_variable_value(variable, dir = nil)
options = dir ? { :cwd => dir } : {}
- make_v = shell_out!("make -V #{variable}", options.merge!(:env => nil, :returns => [0,1]))
+ make_v = shell_out_with_timeout!("make -V #{variable}", options.merge!(:env => nil, :returns => [0,1]))
make_v.exitstatus.zero? ? make_v.stdout.strip.split($\).first : nil # $\ is the line separator, i.e. newline.
end
end
diff --git a/lib/chef/provider/package/freebsd/pkg.rb b/lib/chef/provider/package/freebsd/pkg.rb
index ebbfbb19b4..2d13a0d2c1 100644
--- a/lib/chef/provider/package/freebsd/pkg.rb
+++ b/lib/chef/provider/package/freebsd/pkg.rb
@@ -19,8 +19,8 @@
# limitations under the License.
#
-require 'chef/provider/package/freebsd/base'
-require 'chef/util/path_helper'
+require "chef/provider/package/freebsd/base"
+require "chef/util/path_helper"
class Chef
class Provider
@@ -34,24 +34,24 @@ class Chef
case @new_resource.source
when /^http/, /^ftp/
if @new_resource.source =~ /\/$/
- shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, 'LC_ALL' => nil }).status
+ shell_out_with_timeout!("pkg_add -r #{package_name}", :env => { "PACKAGESITE" => @new_resource.source, "LC_ALL" => nil }).status
else
- shell_out!("pkg_add -r #{package_name}", :env => { "PACKAGEROOT" => @new_resource.source, 'LC_ALL' => nil }).status
+ shell_out_with_timeout!("pkg_add -r #{package_name}", :env => { "PACKAGEROOT" => @new_resource.source, "LC_ALL" => nil }).status
end
Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
when /^\//
- shell_out!("pkg_add #{file_candidate_version_path}", :env => { "PKG_PATH" => @new_resource.source , 'LC_ALL'=>nil}).status
+ shell_out_with_timeout!("pkg_add #{file_candidate_version_path}", :env => { "PKG_PATH" => @new_resource.source , "LC_ALL"=>nil}).status
Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
else
- shell_out!("pkg_add -r #{latest_link_name}", :env => nil).status
+ shell_out_with_timeout!("pkg_add -r #{latest_link_name}", :env => nil).status
end
end
end
def remove_package(name, version)
- shell_out!("pkg_delete #{package_name}-#{version || @current_resource.version}", :env => nil).status
+ shell_out_with_timeout!("pkg_delete #{package_name}-#{version || @current_resource.version}", :env => nil).status
end
# The name of the package (without the version number) as understood by pkg_add and pkg_info.
@@ -72,7 +72,7 @@ class Chef
end
def current_installed_version
- pkg_info = shell_out!("pkg_info -E \"#{package_name}*\"", :env => nil, :returns => [0,1])
+ pkg_info = shell_out_with_timeout!("pkg_info -E \"#{package_name}*\"", :env => nil, :returns => [0,1])
pkg_info.stdout[/^#{Regexp.escape(package_name)}-(.+)/, 1]
end
diff --git a/lib/chef/provider/package/freebsd/pkgng.rb b/lib/chef/provider/package/freebsd/pkgng.rb
index bfe6dca617..2d23620a66 100644
--- a/lib/chef/provider/package/freebsd/pkgng.rb
+++ b/lib/chef/provider/package/freebsd/pkgng.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/provider/package/freebsd/base'
+require "chef/provider/package/freebsd/base"
class Chef
class Provider
@@ -28,23 +28,23 @@ class Chef
unless @current_resource.version
case @new_resource.source
when /^(http|ftp|\/)/
- shell_out!("pkg add#{expand_options(@new_resource.options)} #{@new_resource.source}", :env => { 'LC_ALL' => nil }).status
+ shell_out_with_timeout!("pkg add#{expand_options(@new_resource.options)} #{@new_resource.source}", :env => { "LC_ALL" => nil }).status
Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
else
- shell_out!("pkg install -y#{expand_options(@new_resource.options)} #{name}", :env => { 'LC_ALL' => nil }).status
+ shell_out_with_timeout!("pkg install -y#{expand_options(@new_resource.options)} #{name}", :env => { "LC_ALL" => nil }).status
end
end
end
def remove_package(name, version)
- options = @new_resource.options && @new_resource.options.sub(repo_regex, '')
+ options = @new_resource.options && @new_resource.options.sub(repo_regex, "")
options && !options.empty? || options = nil
- shell_out!("pkg delete -y#{expand_options(options)} #{name}#{version ? '-' + version : ''}", :env => nil).status
+ shell_out_with_timeout!("pkg delete -y#{expand_options(options)} #{name}#{version ? '-' + version : ''}", :env => nil).status
end
def current_installed_version
- pkg_info = shell_out!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70])
+ pkg_info = shell_out_with_timeout!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70])
pkg_info.stdout[/^Version +: (.+)$/, 1]
end
@@ -63,7 +63,7 @@ class Chef
options = $1
end
- pkg_query = shell_out!("pkg rquery#{expand_options(options)} '%v' #{@new_resource.package_name}", :env => nil)
+ pkg_query = shell_out_with_timeout!("pkg rquery#{expand_options(options)} '%v' #{@new_resource.package_name}", :env => nil)
pkg_query.exitstatus.zero? ? pkg_query.stdout.strip.split(/\n/).last : nil
end
diff --git a/lib/chef/provider/package/freebsd/port.rb b/lib/chef/provider/package/freebsd/port.rb
index 8b191179f0..89220e38b4 100644
--- a/lib/chef/provider/package/freebsd/port.rb
+++ b/lib/chef/provider/package/freebsd/port.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/provider/package/freebsd/base'
+require "chef/provider/package/freebsd/base"
class Chef
class Provider
@@ -26,18 +26,18 @@ class Chef
include PortsHelper
def install_package(name, version)
- shell_out!("make -DBATCH install clean", :timeout => 1800, :env => nil, :cwd => port_dir).status
+ shell_out_with_timeout!("make -DBATCH install clean", :timeout => 1800, :env => nil, :cwd => port_dir).status
end
def remove_package(name, version)
- shell_out!("make deinstall", :timeout => 300, :env => nil, :cwd => port_dir).status
+ shell_out_with_timeout!("make deinstall", :timeout => 300, :env => nil, :cwd => port_dir).status
end
def current_installed_version
pkg_info = if @new_resource.supports_pkgng?
- shell_out!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70])
+ shell_out_with_timeout!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70])
else
- shell_out!("pkg_info -E \"#{@new_resource.package_name}*\"", :env => nil, :returns => [0,1])
+ shell_out_with_timeout!("pkg_info -E \"#{@new_resource.package_name}*\"", :env => nil, :returns => [0,1])
end
pkg_info.stdout[/^#{Regexp.escape(@new_resource.package_name)}-(.+)/, 1]
end
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
index 603899646f..661236a56c 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -18,16 +18,16 @@
# limitations under the License.
#
-require 'etc'
-require 'chef/mixin/homebrew_user'
+require "etc"
+require "chef/mixin/homebrew_user"
class Chef
class Provider
class Package
class Homebrew < Chef::Provider::Package
+ provides :package, os: "darwin", override: true
provides :homebrew_package
- provides :package, os: "darwin"
include Chef::Mixin::HomebrewUser
@@ -46,7 +46,7 @@ class Chef
def install_package(name, version)
unless current_resource.version == version
- brew('install', new_resource.options, name)
+ brew("install", new_resource.options, name)
end
end
@@ -56,19 +56,19 @@ class Chef
if current_version.nil? or current_version.empty?
install_package(name, version)
elsif current_version != version
- brew('upgrade', new_resource.options, name)
+ brew("upgrade", new_resource.options, name)
end
end
def remove_package(name, version)
if current_resource.version
- brew('uninstall', new_resource.options, name)
+ brew("uninstall", new_resource.options, name)
end
end
# Homebrew doesn't really have a notion of purging, do a "force remove"
def purge_package(name, version)
- new_resource.options((new_resource.options || '') << ' --force').strip
+ new_resource.options((new_resource.options || "") << " --force").strip
remove_package(name, version)
end
@@ -85,7 +85,7 @@ class Chef
#
# https://github.com/Homebrew/homebrew/wiki/Querying-Brew
def brew_info
- @brew_info ||= Chef::JSONCompat.from_json(brew('info', '--json=v1', new_resource.package_name)).first
+ @brew_info ||= Chef::JSONCompat.from_json(brew("info", "--json=v1", new_resource.package_name)).first
end
# Some packages (formula) are "keg only" and aren't linked,
@@ -95,14 +95,14 @@ class Chef
# that brew thinks is linked as the current version.
#
def current_installed_version
- if brew_info['keg_only']
- if brew_info['installed'].empty?
+ if brew_info["keg_only"]
+ if brew_info["installed"].empty?
nil
else
- brew_info['installed'].last['version']
+ brew_info["installed"].last["version"]
end
else
- brew_info['linked_keg']
+ brew_info["linked_keg"]
end
end
@@ -116,7 +116,7 @@ class Chef
#
# https://github.com/Homebrew/homebrew/wiki/Acceptable-Formulae#stable-versions
def candidate_version
- brew_info['versions']['stable']
+ brew_info["versions"]["stable"]
end
private
@@ -126,7 +126,8 @@ class Chef
homebrew_user = Etc.getpwuid(homebrew_uid)
Chef::Log.debug "Executing '#{command}' as user '#{homebrew_user.name}'"
- output = shell_out!(command, :timeout => 1800, :user => homebrew_uid, :environment => { 'HOME' => homebrew_user.dir, 'RUBYOPT' => nil })
+ # FIXME: this 1800 second default timeout should be deprecated
+ output = shell_out_with_timeout!(command, :timeout => 1800, :user => homebrew_uid, :environment => { "HOME" => homebrew_user.dir, "RUBYOPT" => nil })
output.stdout.chomp
end
diff --git a/lib/chef/provider/package/ips.rb b/lib/chef/provider/package/ips.rb
index 87022d770a..a69a2e24e0 100644
--- a/lib/chef/provider/package/ips.rb
+++ b/lib/chef/provider/package/ips.rb
@@ -17,16 +17,17 @@
# limitations under the License.
#
-require 'open3'
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
+require "open3"
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
class Chef
class Provider
class Package
class Ips < Chef::Provider::Package
+ provides :package, platform: %w{openindiana opensolaris omnios solaris2}
provides :ips_package, os: "solaris2"
attr_accessor :virtual
@@ -42,14 +43,14 @@ class Chef
end
def get_current_version
- shell_out("pkg info #{@new_resource.package_name}").stdout.each_line do |line|
+ shell_out_with_timeout("pkg info #{@new_resource.package_name}").stdout.each_line do |line|
return $1.split[0] if line =~ /^\s+Version: (.*)/
end
return nil
end
def get_candidate_version
- shell_out!("pkg info -r #{new_resource.package_name}").stdout.each_line do |line|
+ shell_out_with_timeout!("pkg info -r #{new_resource.package_name}").stdout.each_line do |line|
return $1.split[0] if line =~ /Version: (.*)/
end
return nil
@@ -69,11 +70,11 @@ class Chef
normal_command = "pkg#{expand_options(@new_resource.options)} install -q #{package_name}"
command =
if @new_resource.respond_to?(:accept_license) and @new_resource.accept_license
- normal_command.gsub('-q', '-q --accept')
+ normal_command.gsub("-q", "-q --accept")
else
normal_command
end
- shell_out(command)
+ shell_out_with_timeout(command)
end
def upgrade_package(name, version)
@@ -82,7 +83,7 @@ class Chef
def remove_package(name, version)
package_name = "#{name}@#{version}"
- shell_out!( "pkg#{expand_options(@new_resource.options)} uninstall -q #{package_name}" )
+ shell_out_with_timeout!( "pkg#{expand_options(@new_resource.options)} uninstall -q #{package_name}" )
end
end
end
diff --git a/lib/chef/provider/package/macports.rb b/lib/chef/provider/package/macports.rb
index b252344c99..c7ea71ac8c 100644
--- a/lib/chef/provider/package/macports.rb
+++ b/lib/chef/provider/package/macports.rb
@@ -3,8 +3,8 @@ class Chef
class Package
class Macports < Chef::Provider::Package
- provides :macports_package
provides :package, os: "darwin"
+ provides :macports_package
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@@ -49,21 +49,21 @@ class Chef
unless @current_resource.version == version
command = "port#{expand_options(@new_resource.options)} install #{name}"
command << " @#{version}" if version and !version.empty?
- shell_out!(command)
+ shell_out_with_timeout!(command)
end
end
def purge_package(name, version)
command = "port#{expand_options(@new_resource.options)} uninstall #{name}"
command << " @#{version}" if version and !version.empty?
- shell_out!(command)
+ shell_out_with_timeout!(command)
end
def remove_package(name, version)
command = "port#{expand_options(@new_resource.options)} deactivate #{name}"
command << " @#{version}" if version and !version.empty?
- shell_out!(command)
+ shell_out_with_timeout!(command)
end
def upgrade_package(name, version)
@@ -76,14 +76,14 @@ class Chef
# that hasn't been installed.
install_package(name, version)
elsif current_version != version
- shell_out!( "port#{expand_options(@new_resource.options)} upgrade #{name} @#{version}" )
+ shell_out_with_timeout!( "port#{expand_options(@new_resource.options)} upgrade #{name} @#{version}" )
end
end
private
def get_response_from_command(command)
output = nil
- status = shell_out(command)
+ status = shell_out_with_timeout(command)
begin
output = status.stdout
rescue Exception
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index 82048c3bd4..539af1c409 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -20,11 +20,10 @@
# limitations under the License.
#
-require 'chef/resource/package'
-require 'chef/provider/package'
-require 'chef/mixin/shell_out'
-require 'chef/mixin/get_source_from_package'
-require 'chef/exceptions'
+require "chef/resource/package"
+require "chef/provider/package"
+require "chef/mixin/get_source_from_package"
+require "chef/exceptions"
class Chef
class Provider
@@ -32,6 +31,7 @@ class Chef
class Openbsd < Chef::Provider::Package
provides :package, os: "openbsd"
+ provides :openbsd_package
include Chef::Mixin::ShellOut
include Chef::Mixin::GetSourceFromPackage
@@ -53,7 +53,7 @@ class Chef
# Below are incomplete/missing features for this package provider
requirements.assert(:all_actions) do |a|
a.assertion { !new_resource.source }
- a.failure_message(Chef::Exceptions::Package, 'The openbsd package provider does not support the source attribute')
+ a.failure_message(Chef::Exceptions::Package, "The openbsd package provider does not support the source attribute")
end
requirements.assert(:all_actions) do |a|
a.assertion do
@@ -63,7 +63,7 @@ class Chef
true
end
end
- a.failure_message(Chef::Exceptions::Package, 'The openbsd package provider does not support providing a version and flavor')
+ a.failure_message(Chef::Exceptions::Package, "The openbsd package provider does not support providing a version and flavor")
end
end
@@ -72,18 +72,16 @@ class Chef
if parts = name.match(/^(.+?)--(.+)/) # use double-dash for stems with flavors, see man page for pkg_add
name = parts[1]
end
- shell_out!("pkg_add -r #{name}#{version_string}", :env => {"PKG_PATH" => pkg_path}).status
+ shell_out_with_timeout!("pkg_add -r #{name}#{version_string(version)}", :env => {"PKG_PATH" => pkg_path}).status
Chef::Log.debug("#{new_resource.package_name} installed")
end
end
def remove_package(name, version)
- version_string = ''
- version_string += "-#{version}" if version
if parts = name.match(/^(.+?)--(.+)/)
name = parts[1]
end
- shell_out!("pkg_delete #{name}#{version_string}", :env => nil).status
+ shell_out_with_timeout!("pkg_delete #{name}#{version_string(version)}", :env => nil).status
end
private
@@ -94,7 +92,7 @@ class Chef
else
name = new_resource.package_name
end
- pkg_info = shell_out!("pkg_info -e \"#{name}->0\"", :env => nil, :returns => [0,1])
+ pkg_info = shell_out_with_timeout!("pkg_info -e \"#{name}->0\"", :env => nil, :returns => [0,1])
result = pkg_info.stdout[/^inst:#{Regexp.escape(name)}-(.+?)\s/, 1]
Chef::Log.debug("installed_version of '#{new_resource.package_name}' is '#{result}'")
result
@@ -103,7 +101,7 @@ class Chef
def candidate_version
@candidate_version ||= begin
results = []
- shell_out!("pkg_info -I \"#{new_resource.package_name}#{version_string}\"", :env => nil, :returns => [0,1]).stdout.each_line do |line|
+ shell_out_with_timeout!("pkg_info -I \"#{new_resource.package_name}#{version_string(new_resource.version)}\"", :env => nil, :returns => [0,1]).stdout.each_line do |line|
if parts = new_resource.package_name.match(/^(.+?)--(.+)/)
results << line[/^#{Regexp.escape(parts[1])}-(.+?)\s/, 1]
else
@@ -111,7 +109,7 @@ class Chef
end
end
results = results.reject(&:nil?)
- Chef::Log.debug("candidate versions of '#{new_resource.package_name}' are '#{results}'")
+ Chef::Log.debug("Candidate versions of '#{new_resource.package_name}' are '#{results}'")
case results.length
when 0
[]
@@ -123,13 +121,13 @@ class Chef
end
end
- def version_string
- ver = ''
- ver += "-#{new_resource.version}" if new_resource.version
+ def version_string(version)
+ ver = ""
+ ver += "-#{version}" if version
end
def pkg_path
- ENV['PKG_PATH'] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/"
+ ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/"
end
end
diff --git a/lib/chef/provider/package/pacman.rb b/lib/chef/provider/package/pacman.rb
index f16fc811f5..6bb2250da5 100644
--- a/lib/chef/provider/package/pacman.rb
+++ b/lib/chef/provider/package/pacman.rb
@@ -16,25 +16,24 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
class Chef
class Provider
class Package
class Pacman < Chef::Provider::Package
+ provides :package, platform: "arch"
provides :pacman_package, os: "linux"
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @current_resource.version(nil)
-
Chef::Log.debug("#{@new_resource} checking pacman for #{@new_resource.package_name}")
- status = shell_out("pacman -Qi #{@new_resource.package_name}")
+ status = shell_out_with_timeout("pacman -Qi #{@new_resource.package_name}")
status.stdout.each_line do |line|
case line
when /^Version(\s?)*: (.+)$/
@@ -60,9 +59,9 @@ class Chef
repos = pacman.scan(/\[(.+)\]/).flatten
end
- package_repos = repos.map {|r| Regexp.escape(r) }.join('|')
+ package_repos = repos.map {|r| Regexp.escape(r) }.join("|")
- status = shell_out("pacman -Sl")
+ status = shell_out_with_timeout("pacman -Sl")
status.stdout.each_line do |line|
case line
when /^(#{package_repos}) #{Regexp.escape(@new_resource.package_name)} (.+)$/
@@ -85,7 +84,7 @@ class Chef
end
def install_package(name, version)
- shell_out!( "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
+ shell_out_with_timeout!( "pacman --sync --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
end
def upgrade_package(name, version)
@@ -93,7 +92,7 @@ class Chef
end
def remove_package(name, version)
- shell_out!( "pacman --remove --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
+ shell_out_with_timeout!( "pacman --remove --noconfirm --noprogressbar#{expand_options(@new_resource.options)} #{name}" )
end
def purge_package(name, version)
diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb
index 407e0d0110..c2b1069e1e 100644
--- a/lib/chef/provider/package/paludis.rb
+++ b/lib/chef/provider/package/paludis.rb
@@ -16,38 +16,36 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/resource/package'
+require "chef/provider/package"
+require "chef/resource/package"
class Chef
class Provider
class Package
class Paludis < Chef::Provider::Package
+ provides :package, platform: "exherbo"
provides :paludis_package, os: "linux"
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.package_name)
@current_resource.package_name(@new_resource.package_name)
- @current_resource.version(nil)
-
Chef::Log.debug("Checking package status for #{@new_resource.package_name}")
installed = false
- re = Regexp.new('(.*)[[:blank:]](.*)[[:blank:]](.*)$')
+ re = Regexp.new("(.*)[[:blank:]](.*)[[:blank:]](.*)$")
shell_out!("cave -L warning print-ids -M none -m \"#{@new_resource.package_name}\" -f \"%c/%p %v %r\n\"").stdout.each_line do |line|
res = re.match(line)
unless res.nil?
case res[3]
- when 'accounts', 'installed-accounts'
+ when "accounts", "installed-accounts"
next
- when 'installed'
+ when "installed"
installed = true
@current_resource.version(res[2])
else
@candidate_version = res[2]
- @current_resource.version(nil)
end
end
end
diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb
index bb047ad2fa..4a1559637a 100644
--- a/lib/chef/provider/package/portage.rb
+++ b/lib/chef/provider/package/portage.rb
@@ -16,23 +16,25 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
-require 'chef/util/path_helper'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
+require "chef/util/path_helper"
class Chef
class Provider
class Package
class Portage < Chef::Provider::Package
+
+ provides :package, platform: "gentoo"
+ provides :portage_package
+
PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)}
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @current_resource.version(nil)
-
category, pkg = %r{^#{PACKAGE_NAME_PATTERN}$}.match(@new_resource.package_name)[1,2]
globsafe_category = category ? Chef::Util::PathHelper.escape_glob(category) : nil
@@ -46,7 +48,7 @@ class Chef
if versions.size > 1
atoms = versions.map {|v| v.first }.sort
- categories = atoms.map {|v| v.split('/')[0] }.uniq
+ categories = atoms.map {|v| v.split("/")[0] }.uniq
if !category && categories.size > 1
raise Chef::Exceptions::Package, "Multiple packages found for #{@new_resource.package_name}: #{atoms.join(" ")}. Specify a category."
end
@@ -64,7 +66,7 @@ class Chef
txt.each_line do |line|
if line =~ /\*\s+#{PACKAGE_NAME_PATTERN}/
- found_package_name = $&.gsub(/\*/, '').strip
+ found_package_name = $&.gsub(/\*/, "").strip
if package =~ /\// #the category is specified
if found_package_name == package
availables[found_package_name] = nil
diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb
index f10fe23c71..9ab10b062d 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -15,11 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/mixin/shell_out'
-require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
+require "chef/mixin/get_source_from_package"
class Chef
class Provider
@@ -51,7 +50,6 @@ class Chef
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @new_resource.version(nil)
if @new_resource.source
unless uri_scheme?(@new_resource.source) || ::File.exists?(@new_resource.source)
@@ -60,9 +58,9 @@ class Chef
end
Chef::Log.debug("#{@new_resource} checking rpm status")
- shell_out!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}").stdout.each_line do |line|
+ shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}").stdout.each_line do |line|
case line
- when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
+ when /^(\S+)\s(\S+)$/
@current_resource.package_name($1)
@new_resource.version($2)
@candidate_version = $2
@@ -76,10 +74,10 @@ class Chef
end
Chef::Log.debug("#{@new_resource} checking install state")
- @rpm_status = shell_out("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}")
+ @rpm_status = shell_out_with_timeout("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}")
@rpm_status.stdout.each_line do |line|
case line
- when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
+ when /^(\S+)\s(\S+)$/
Chef::Log.debug("#{@new_resource} current version is #{$2}")
@current_resource.version($2)
end
@@ -90,12 +88,12 @@ class Chef
def install_package(name, version)
unless @current_resource.version
- shell_out!( "rpm #{@new_resource.options} -i #{@new_resource.source}" )
+ shell_out_with_timeout!( "rpm #{@new_resource.options} -i #{@new_resource.source}" )
else
if allow_downgrade
- shell_out!( "rpm #{@new_resource.options} -U --oldpackage #{@new_resource.source}" )
+ shell_out_with_timeout!( "rpm #{@new_resource.options} -U --oldpackage #{@new_resource.source}" )
else
- shell_out!( "rpm #{@new_resource.options} -U #{@new_resource.source}" )
+ shell_out_with_timeout!( "rpm #{@new_resource.options} -U #{@new_resource.source}" )
end
end
end
@@ -104,9 +102,9 @@ class Chef
def remove_package(name, version)
if version
- shell_out!( "rpm #{@new_resource.options} -e #{name}-#{version}" )
+ shell_out_with_timeout!( "rpm #{@new_resource.options} -e #{name}-#{version}" )
else
- shell_out!( "rpm #{@new_resource.options} -e #{name}" )
+ shell_out_with_timeout!( "rpm #{@new_resource.options} -e #{name}" )
end
end
@@ -115,7 +113,7 @@ class Chef
def uri_scheme?(str)
scheme = URI.split(str).first
return false unless scheme
- %w(http https ftp file).include?(scheme.downcase)
+ %w{http https ftp file}.include?(scheme.downcase)
rescue URI::InvalidURIError
return false
end
diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb
index c53aa8934a..85796e8b9a 100644
--- a/lib/chef/provider/package/rubygems.rb
+++ b/lib/chef/provider/package/rubygems.rb
@@ -1,7 +1,7 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
+# Copyright:: Copyright (c) 2008, 2010-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,32 +17,25 @@
# limitations under the License.
#
-require 'uri'
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
+require "uri"
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
+require "chef/mixin/get_source_from_package"
# Class methods on Gem are defined in rubygems
-require 'rubygems'
+require "rubygems"
# Ruby 1.9's gem_prelude can interact poorly with loading the full rubygems
# explicitly like this. Make sure rubygems/specification is always last in this
# list
-require 'rubygems/version'
-require 'rubygems/dependency'
-require 'rubygems/spec_fetcher'
-require 'rubygems/platform'
-
-# Compatibility note: Rubygems 2.0 removes rubygems/format in favor of
-# rubygems/package.
-begin
- require 'rubygems/format'
-rescue LoadError
- require 'rubygems/package'
-end
-require 'rubygems/dependency_installer'
-require 'rubygems/uninstaller'
-require 'rubygems/specification'
+require "rubygems/version"
+require "rubygems/dependency"
+require "rubygems/spec_fetcher"
+require "rubygems/platform"
+require "rubygems/package"
+require "rubygems/dependency_installer"
+require "rubygems/uninstaller"
+require "rubygems/specification"
class Chef
class Provider
@@ -93,7 +86,7 @@ class Chef
# === Returns
# [Gem::Specification] an array of Gem::Specification objects
def installed_versions(gem_dep)
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.8.0')
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("1.8.0")
gem_specification.find_all_by_name(gem_dep.name, gem_dep.requirement)
else
gem_source_index.search(gem_dep)
@@ -144,7 +137,7 @@ class Chef
spec.version
else
# This is probably going to end badly...
- logger.warn { "#{@new_resource} gem package #{source} does not satisfy the requirements #{gem_dependency.to_s}" }
+ logger.warn { "#{@new_resource} gem package #{source} does not satisfy the requirements #{gem_dependency}" }
nil
end
end
@@ -168,17 +161,17 @@ class Chef
def find_newest_remote_version(gem_dependency, *sources)
available_gems = dependency_installer.find_gems_with_sources(gem_dependency)
spec, source = if available_gems.respond_to?(:last)
- # DependencyInstaller sorts the results such that the last one is
- # always the one it considers best.
- spec_with_source = available_gems.last
- spec_with_source && spec_with_source
- else
- # Rubygems 2.0 returns a Gem::Available set, which is a
- # collection of AvailableSet::Tuple structs
- available_gems.pick_best!
- best_gem = available_gems.set.first
- best_gem && [best_gem.spec, best_gem.source]
- end
+ # DependencyInstaller sorts the results such that the last one is
+ # always the one it considers best.
+ spec_with_source = available_gems.last
+ spec_with_source && spec_with_source
+ else
+ # Rubygems 2.0 returns a Gem::Available set, which is a
+ # collection of AvailableSet::Tuple structs
+ available_gems.pick_best!
+ best_gem = available_gems.set.first
+ best_gem && [best_gem.spec, best_gem.source]
+ end
version = spec && spec.version
if version
@@ -295,7 +288,7 @@ class Chef
end
def gem_source_index
- @source_index ||= Gem::SourceIndex.from_gems_in(*gem_paths.map { |p| p + '/specifications' })
+ @source_index ||= Gem::SourceIndex.from_gems_in(*gem_paths.map { |p| p + "/specifications" })
end
def gem_specification
@@ -327,7 +320,7 @@ class Chef
else
gem_environment = shell_out!("#{@gem_binary_location} env").stdout
if jruby = gem_environment[JRUBY_PLATFORM]
- self.class.platform_cache[@gem_binary_location] = ['ruby', Gem::Platform.new(jruby)]
+ self.class.platform_cache[@gem_binary_location] = ["ruby", Gem::Platform.new(jruby)]
else
self.class.platform_cache[@gem_binary_location] = Gem.platforms
end
@@ -401,11 +394,11 @@ class Chef
end
def is_omnibus?
- if RbConfig::CONFIG['bindir'] =~ %r!/opt/(opscode|chef)/embedded/bin!
+ if RbConfig::CONFIG["bindir"] =~ %r{/(opscode|chef|chefdk)/embedded/bin}
Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
# Omnibus installs to a static path because of linking on unix, find it.
true
- elsif RbConfig::CONFIG['bindir'].sub(/^[\w]:/, '') == "/opscode/chef/embedded/bin"
+ elsif RbConfig::CONFIG["bindir"].sub(/^[\w]:/, "") == "/opscode/chef/embedded/bin"
Chef::Log.debug("#{@new_resource} detected omnibus installation in #{RbConfig::CONFIG['bindir']}")
# windows, with the drive letter removed
true
@@ -417,7 +410,7 @@ class Chef
def find_gem_by_path
Chef::Log.debug("#{@new_resource} searching for 'gem' binary in path: #{ENV['PATH']}")
separator = ::File::ALT_SEPARATOR ? ::File::ALT_SEPARATOR : ::File::SEPARATOR
- path_to_first_gem = ENV['PATH'].split(::File::PATH_SEPARATOR).select { |path| ::File.exists?(path + separator + "gem") }.first
+ path_to_first_gem = ENV["PATH"].split(::File::PATH_SEPARATOR).select { |path| ::File.exists?(path + separator + "gem") }.first
raise Chef::Exceptions::FileNotFound, "Unable to find 'gem' binary in path: #{ENV['PATH']}" if path_to_first_gem.nil?
path_to_first_gem + separator + "gem"
end
@@ -445,14 +438,14 @@ class Chef
gemspec = matching_installed_versions.last
logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}"}
gemspec
- # If no version matching the requirements exists, the latest installed
- # version is the current version.
+ # If no version matching the requirements exists, the latest installed
+ # version is the current version.
elsif !all_installed_versions.empty?
gemspec = all_installed_versions.last
logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
gemspec
else
- logger.debug { "#{@new_resource} no installed version found for #{gem_dependency.to_s}"}
+ logger.debug { "#{@new_resource} no installed version found for #{gem_dependency}"}
nil
end
end
@@ -463,8 +456,8 @@ class Chef
def all_installed_versions
@all_installed_versions ||= begin
- @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, '>= 0'))
- end
+ @gem_env.installed_versions(Gem::Dependency.new(gem_dependency.name, ">= 0"))
+ end
end
def gem_sources
@@ -489,14 +482,14 @@ class Chef
def candidate_version
@candidate_version ||= begin
- if target_version_already_installed?(@current_resource.version, @new_resource.version)
- nil
- elsif source_is_remote?
- @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
- else
- @gem_env.candidate_version_from_file(gem_dependency, @new_resource.source).to_s
- end
- end
+ if target_version_already_installed?(@current_resource.version, @new_resource.version)
+ nil
+ elsif source_is_remote?
+ @gem_env.candidate_version_from_remote(gem_dependency, *gem_sources).to_s
+ else
+ @gem_env.candidate_version_from_file(gem_dependency, @new_resource.source).to_s
+ end
+ end
end
def target_version_already_installed?(current_version, new_version)
@@ -532,22 +525,22 @@ class Chef
end
def gem_binary_path
- @new_resource.gem_binary || 'gem'
+ @new_resource.gem_binary || "gem"
end
def install_via_gem_command(name, version)
if @new_resource.source =~ /\.gem$/i
name = @new_resource.source
elsif @new_resource.clear_sources
- src = ' --clear-sources'
- src << (@new_resource.source && " --source=#{@new_resource.source}" || '')
+ src = " --clear-sources"
+ src << (@new_resource.source && " --source=#{@new_resource.source}" || "")
else
src = @new_resource.source && " --source=#{@new_resource.source} --source=https://rubygems.org"
end
if !version.nil? && version.length > 0
- shell_out!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env=>nil)
+ shell_out_with_timeout!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src}#{opts}", :env=>nil)
else
- shell_out!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src}#{opts}", :env=>nil)
+ shell_out_with_timeout!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src}#{opts}", :env=>nil)
end
end
@@ -571,9 +564,9 @@ class Chef
def uninstall_via_gem_command(name, version)
if version
- shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", :env=>nil)
+ shell_out_with_timeout!("#{gem_binary_path} uninstall #{name} -q -x -I -v \"#{version}\"#{opts}", :env=>nil)
else
- shell_out!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", :env=>nil)
+ shell_out_with_timeout!("#{gem_binary_path} uninstall #{name} -q -x -I -a#{opts}", :env=>nil)
end
end
diff --git a/lib/chef/provider/package/smartos.rb b/lib/chef/provider/package/smartos.rb
index 7cef91953a..98264ecd1e 100644
--- a/lib/chef/provider/package/smartos.rb
+++ b/lib/chef/provider/package/smartos.rb
@@ -19,9 +19,9 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
+require "chef/provider/package"
+require "chef/resource/package"
+require "chef/mixin/get_source_from_package"
class Chef
class Provider
@@ -29,13 +29,13 @@ class Chef
class SmartOS < Chef::Provider::Package
attr_accessor :is_virtual_package
+ provides :package, platform: "smartos"
provides :smartos_package, os: "solaris2", platform_family: "smartos"
def load_current_resource
Chef::Log.debug("#{@new_resource} loading current resource")
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @current_resource.version(nil)
check_package_state(@new_resource.package_name)
@current_resource # modified by check_package_state
end
@@ -43,15 +43,13 @@ class Chef
def check_package_state(name)
Chef::Log.debug("#{@new_resource} checking package #{name}")
version = nil
- info = shell_out!("/opt/local/sbin/pkg_info -E \"#{name}*\"", :env => nil, :returns => [0,1])
+ info = shell_out_with_timeout!("/opt/local/sbin/pkg_info", "-E", "#{name}*", :env => nil, :returns => [0,1])
if info.stdout
version = info.stdout[/^#{@new_resource.package_name}-(.+)/, 1]
end
- if !version
- @current_resource.version(nil)
- else
+ if version
@current_resource.version(version)
end
end
@@ -60,11 +58,11 @@ class Chef
return @candidate_version if @candidate_version
name = nil
version = nil
- pkg = shell_out!("/opt/local/bin/pkgin se #{new_resource.package_name}", :env => nil, :returns => [0,1])
+ pkg = shell_out_with_timeout!("/opt/local/bin/pkgin", "se", new_resource.package_name, :env => nil, :returns => [0,1])
pkg.stdout.each_line do |line|
case line
when /^#{new_resource.package_name}/
- name, version = line.split[0].split(/-([^-]+)$/)
+ name, version = line.split(/[; ]/)[0].split(/-([^-]+)$/)
end
end
@candidate_version = version
@@ -74,7 +72,7 @@ class Chef
def install_package(name, version)
Chef::Log.debug("#{@new_resource} installing package #{name} version #{version}")
package = "#{name}-#{version}"
- out = shell_out!("/opt/local/bin/pkgin -y install #{package}", :env => nil)
+ out = shell_out_with_timeout!("/opt/local/bin/pkgin", "-y", "install", package, :env => nil)
end
def upgrade_package(name, version)
@@ -85,7 +83,7 @@ class Chef
def remove_package(name, version)
Chef::Log.debug("#{@new_resource} removing package #{name} version #{version}")
package = "#{name}"
- out = shell_out!("/opt/local/bin/pkgin -y remove #{package}", :env => nil)
+ out = shell_out_with_timeout!("/opt/local/bin/pkgin", "-y", "remove", package, :env => nil)
end
end
diff --git a/lib/chef/provider/package/solaris.rb b/lib/chef/provider/package/solaris.rb
index a2cfd93ef6..8442e57dbb 100644
--- a/lib/chef/provider/package/solaris.rb
+++ b/lib/chef/provider/package/solaris.rb
@@ -15,10 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
-require 'chef/mixin/get_source_from_package'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
+require "chef/mixin/get_source_from_package"
class Chef
class Provider
@@ -27,6 +27,8 @@ class Chef
include Chef::Mixin::GetSourceFromPackage
+ provides :package, platform: "nexentacore"
+ provides :package, platform: "solaris2", platform_version: "< 5.11"
provides :solaris_package, os: "solaris2"
# def initialize(*args)
@@ -49,13 +51,12 @@ class Chef
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
- @new_resource.version(nil)
if @new_resource.source
@package_source_found = ::File.exists?(@new_resource.source)
if @package_source_found
Chef::Log.debug("#{@new_resource} checking pkg status")
- shell_out("pkginfo -l -d #{@new_resource.source} #{@new_resource.package_name}").stdout.each_line do |line|
+ shell_out_with_timeout("pkginfo -l -d #{@new_resource.source} #{@new_resource.package_name}").stdout.each_line do |line|
case line
when /VERSION:\s+(.+)/
@new_resource.version($1)
@@ -65,7 +66,7 @@ class Chef
end
Chef::Log.debug("#{@new_resource} checking install state")
- status = shell_out("pkginfo -l #{@current_resource.package_name}")
+ status = shell_out_with_timeout("pkginfo -l #{@current_resource.package_name}")
status.stdout.each_line do |line|
case line
when /VERSION:\s+(.+)/
@@ -78,16 +79,12 @@ class Chef
raise Chef::Exceptions::Package, "pkginfo failed - #{status.inspect}!"
end
- unless @current_resource.version.nil?
- @current_resource.version(nil)
- end
-
@current_resource
end
def candidate_version
return @candidate_version if @candidate_version
- status = shell_out("pkginfo -l -d #{@new_resource.source} #{new_resource.package_name}")
+ status = shell_out_with_timeout("pkginfo -l -d #{@new_resource.source} #{new_resource.package_name}")
status.stdout.each_line do |line|
case line
when /VERSION:\s+(.+)/
@@ -110,7 +107,7 @@ class Chef
else
command = "pkgadd -n -d #{@new_resource.source} all"
end
- shell_out!(command)
+ shell_out_with_timeout!(command)
Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
else
if ::File.directory?(@new_resource.source) # CHEF-4469
@@ -118,17 +115,19 @@ class Chef
else
command = "pkgadd -n#{expand_options(@new_resource.options)} -d #{@new_resource.source} all"
end
- shell_out!(command)
+ shell_out_with_timeout!(command)
Chef::Log.debug("#{@new_resource} installed version #{@new_resource.version} from: #{@new_resource.source}")
end
end
+ alias_method :upgrade_package, :install_package
+
def remove_package(name, version)
if @new_resource.options.nil?
- shell_out!( "pkgrm -n #{name}" )
+ shell_out_with_timeout!( "pkgrm -n #{name}" )
Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
else
- shell_out!( "pkgrm -n#{expand_options(@new_resource.options)} #{name}" )
+ shell_out_with_timeout!( "pkgrm -n#{expand_options(@new_resource.options)} #{name}" )
Chef::Log.debug("#{@new_resource} removed version #{@new_resource.version}")
end
end
diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb
index 143d82f111..87fadc27cf 100644
--- a/lib/chef/provider/package/windows.rb
+++ b/lib/chef/provider/package/windows.rb
@@ -16,70 +16,244 @@
# limitations under the License.
#
-require 'chef/resource/windows_package'
-require 'chef/provider/package'
-require 'chef/util/path_helper'
+require "chef/mixin/uris"
+require "chef/resource/windows_package"
+require "chef/provider/package"
+require "chef/util/path_helper"
+require "chef/mixin/checksum"
class Chef
class Provider
class Package
class Windows < Chef::Provider::Package
+ include Chef::Mixin::Uris
+ include Chef::Mixin::Checksum
provides :package, os: "windows"
provides :windows_package, os: "windows"
- # Depending on the installer, we may need to examine installer_type or
- # source attributes, or search for text strings in the installer file
- # binary to determine the installer type for the user. Since the file
- # must be on disk to do so, we have to make this choice in the provider.
- require 'chef/provider/package/windows/msi.rb'
+ require "chef/provider/package/windows/registry_uninstall_entry.rb"
+
+ def define_resource_requirements
+ requirements.assert(:install) do |a|
+ a.assertion { new_resource.source unless package_provider == :msi }
+ a.failure_message Chef::Exceptions::NoWindowsPackageSource, "Source for package #{new_resource.name} must be specified in the resource's source property for package to be installed because the package_name property is used to test for the package installation state for this package type."
+ end
+ end
# load_current_resource is run in Chef::Provider#run_action when not in whyrun_mode?
def load_current_resource
- @new_resource.source(Chef::Util::PathHelper.validate_path(@new_resource.source))
+ @current_resource = Chef::Resource::WindowsPackage.new(new_resource.name)
+ if downloadable_file_missing?
+ Chef::Log.debug("We do not know the version of #{new_resource.source} because the file is not downloaded")
+ current_resource.version(:unknown.to_s)
+ else
+ current_resource.version(package_provider.installed_version)
+ new_resource.version(package_provider.package_version) if package_provider.package_version
+ end
- @current_resource = Chef::Resource::WindowsPackage.new(@new_resource.name)
- @current_resource.version(package_provider.installed_version)
- @new_resource.version(package_provider.package_version)
- @current_resource
+ current_resource
end
def package_provider
@package_provider ||= begin
case installer_type
when :msi
- Chef::Provider::Package::Windows::MSI.new(@new_resource)
+ Chef::Log.debug("#{new_resource} is MSI")
+ require "chef/provider/package/windows/msi"
+ Chef::Provider::Package::Windows::MSI.new(resource_for_provider, uninstall_registry_entries)
else
- raise "Unable to find a Chef::Provider::Package::Windows provider for installer_type '#{installer_type}'"
+ Chef::Log.debug("#{new_resource} is EXE with type '#{installer_type}'")
+ require "chef/provider/package/windows/exe"
+ Chef::Provider::Package::Windows::Exe.new(resource_for_provider, installer_type, uninstall_registry_entries)
end
end
end
def installer_type
+ # Depending on the installer, we may need to examine installer_type or
+ # source attributes, or search for text strings in the installer file
+ # binary to determine the installer type for the user. Since the file
+ # must be on disk to do so, we have to make this choice in the provider.
@installer_type ||= begin
- if @new_resource.installer_type
- @new_resource.installer_type
+ if new_resource.installer_type
+ new_resource.installer_type
+ elsif source_location.nil?
+ inferred_registry_type
else
- file_extension = ::File.basename(@new_resource.source).split(".").last.downcase
+ basename = ::File.basename(source_location)
+ file_extension = basename.split(".").last.downcase
if file_extension == "msi"
:msi
else
- raise ArgumentError, "Installer type for Windows Package '#{@new_resource.name}' not specified and cannot be determined from file extension '#{file_extension}'"
+ # search the binary file for installer type
+ ::Kernel.open(::File.expand_path(source_location), "rb") do |io|
+ filesize = io.size
+ bufsize = 4096 # read 4K buffers
+ overlap = 16 # bytes to overlap between buffer reads
+
+ until io.eof
+ contents = io.read(bufsize)
+
+ case contents
+ when /inno/i # Inno Setup
+ return :inno
+ when /wise/i # Wise InstallMaster
+ return :wise
+ when /nullsoft/i # Nullsoft Scriptable Install System
+ return :nsis
+ end
+
+ if (io.tell() < filesize)
+ io.seek(io.tell() - overlap)
+ end
+ end
+ end
+
+ # if file is named 'setup.exe' assume installshield
+ if basename == "setup.exe"
+ :installshield
+ else
+ raise Chef::Exceptions::CannotDetermineWindowsInstallerType, "Installer type for Windows Package '#{new_resource.name}' not specified and cannot be determined from file extension '#{file_extension}'"
+ end
end
end
end
end
+ def action_install
+ if uri_scheme?(new_resource.source)
+ download_source_file
+ load_current_resource
+ else
+ validate_content!
+ end
+
+ super
+ end
+
# Chef::Provider::Package action_install + action_remove call install_package + remove_package
# Pass those calls to the correct sub-provider
def install_package(name, version)
- package_provider.install_package(name, version)
+ package_provider.install_package
end
def remove_package(name, version)
- package_provider.remove_package(name, version)
+ package_provider.remove_package
+ end
+
+ # @return [Array] new_version(s) as an array
+ def new_version_array
+ # Because the one in the parent caches things
+ [new_resource.version]
+ end
+
+ # @return [String] candidate_version
+ def candidate_version
+ @candidate_version ||= (new_resource.version || "latest")
+ end
+
+ # @return [Array] current_version(s) as an array
+ # this package provider does not support package arrays
+ # However, There may be multiple versions for a single
+ # package so the first element may be a nested array
+ def current_version_array
+ [ current_resource.version ]
+ end
+
+ # @param current_version<String> one or more versions currently installed
+ # @param new_version<String> version of the new resource
+ #
+ # @return [Boolean] true if new_version is equal to or included in current_version
+ def target_version_already_installed?(current_version, new_version)
+ Chef::Log.debug("Checking if #{new_resource} version '#{new_version}' is already installed. #{current_version} is currently installed")
+ if current_version.is_a?(Array)
+ current_version.include?(new_version)
+ else
+ new_version == current_version
+ end
+ end
+
+ def have_any_matching_version?
+ target_version_already_installed?(current_resource.version, new_resource.version)
+ end
+
+ private
+
+ def uninstall_registry_entries
+ @uninstall_registry_entries ||= Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(new_resource.package_name)
end
+
+ def inferred_registry_type
+ uninstall_registry_entries.each do |entry|
+ return :inno if entry.key.end_with?("_is1")
+ return :msi if entry.uninstall_string.downcase.start_with?("msiexec.exe ")
+ return :nsis if entry.uninstall_string.downcase.end_with?("uninst.exe\"")
+ end
+ nil
+ end
+
+ def downloadable_file_missing?
+ !new_resource.source.nil? && uri_scheme?(new_resource.source) && !::File.exists?(source_location)
+ end
+
+ def resource_for_provider
+ @resource_for_provider = Chef::Resource::WindowsPackage.new(new_resource.name).tap do |r|
+ r.source(Chef::Util::PathHelper.validate_path(source_location)) unless source_location.nil?
+ r.version(new_resource.version)
+ r.timeout(new_resource.timeout)
+ r.returns(new_resource.returns)
+ r.options(new_resource.options)
+ end
+ end
+
+ def download_source_file
+ source_resource.run_action(:create)
+ Chef::Log.debug("#{new_resource} fetched source file to #{source_resource.path}")
+ end
+
+ def source_resource
+ @source_resource ||= Chef::Resource::RemoteFile.new(default_download_cache_path, run_context).tap do |r|
+ r.source(new_resource.source)
+ r.checksum(new_resource.checksum)
+ r.backup(false)
+
+ if new_resource.remote_file_attributes
+ new_resource.remote_file_attributes.each do |(k,v)|
+ r.send(k.to_sym, v)
+ end
+ end
+ end
+ end
+
+ def default_download_cache_path
+ uri = ::URI.parse(new_resource.source)
+ filename = ::File.basename(::URI.unescape(uri.path))
+ file_cache_dir = Chef::FileCache.create_cache_path("package/")
+ Chef::Util::PathHelper.cleanpath("#{file_cache_dir}/#{filename}")
+ end
+
+ def source_location
+ if new_resource.source.nil?
+ nil
+ elsif uri_scheme?(new_resource.source)
+ source_resource.path
+ else
+ new_source = Chef::Util::PathHelper.cleanpath(new_resource.source)
+ ::File.exist?(new_source) ? new_source : nil
+ end
+ end
+
+ def validate_content!
+ if new_resource.checksum
+ source_checksum = checksum(source_location)
+ if new_resource.checksum != source_checksum
+ raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(source_checksum))
+ end
+ end
+ end
+
end
end
end
diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb
new file mode 100644
index 0000000000..e510755281
--- /dev/null
+++ b/lib/chef/provider/package/windows/exe.rb
@@ -0,0 +1,117 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+# Author:: Matt Wrock <matt@mattwrock.com>
+# Copyright:: Copyright (c) 2011, 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/mixin/shell_out"
+
+class Chef
+ class Provider
+ class Package
+ class Windows
+ class Exe
+ include Chef::Mixin::ShellOut
+
+ def initialize(resource, installer_type, uninstall_entries)
+ @new_resource = resource
+ @installer_type = installer_type
+ @uninstall_entries = uninstall_entries
+ end
+
+ attr_reader :new_resource
+ attr_reader :installer_type
+ attr_reader :uninstall_entries
+
+ # From Chef::Provider::Package
+ def expand_options(options)
+ options ? " #{options}" : ""
+ end
+
+ # Returns a version if the package is installed or nil if it is not.
+ def installed_version
+ Chef::Log.debug("#{new_resource} checking package version")
+ current_installed_version
+ end
+
+ def package_version
+ new_resource.version
+ end
+
+ def install_package
+ Chef::Log.debug("#{new_resource} installing #{new_resource.installer_type} package '#{new_resource.source}'")
+ shell_out!(
+ [
+ "start",
+ "\"\"",
+ "/wait",
+ "\"#{new_resource.source}\"",
+ unattended_flags,
+ expand_options(new_resource.options),
+ "& exit %%%%ERRORLEVEL%%%%",
+ ].join(" "), timeout: new_resource.timeout, returns: new_resource.returns
+ )
+ end
+
+ def remove_package
+ uninstall_version = new_resource.version || current_installed_version
+ uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) }
+ .map { |version| version.uninstall_string }.uniq.each do |uninstall_string|
+ Chef::Log.debug("Registry provided uninstall string for #{new_resource} is '#{uninstall_string}'")
+ shell_out!(uninstall_command(uninstall_string), { returns: new_resource.returns })
+ end
+ end
+
+ private
+
+ def uninstall_command(uninstall_string)
+ uninstall_string.delete!('"')
+ uninstall_string = [
+ %q{/d"},
+ ::File.dirname(uninstall_string),
+ %q{" },
+ ::File.basename(uninstall_string),
+ expand_options(new_resource.options),
+ " ",
+ unattended_flags,
+ ].join
+ %Q{start "" /wait #{uninstall_string} & exit %%%%ERRORLEVEL%%%%}
+ end
+
+ def current_installed_version
+ @current_installed_version ||= uninstall_entries.count == 0 ? nil : begin
+ uninstall_entries.map { |entry| entry.display_version }.uniq
+ end
+ end
+
+ # http://unattended.sourceforge.net/installers.php
+ def unattended_flags
+ case installer_type
+ when :installshield
+ "/s /sms"
+ when :nsis
+ "/S /NCRC"
+ when :inno
+ "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART"
+ when :wise
+ "/s"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb
index 938452945e..106dc496b6 100644
--- a/lib/chef/provider/package/windows/msi.rb
+++ b/lib/chef/provider/package/windows/msi.rb
@@ -18,21 +18,25 @@
# TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install
-require 'chef/win32/api/installer' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
-require 'chef/mixin/shell_out'
+require "chef/win32/api/installer" if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
+require "chef/mixin/shell_out"
class Chef
class Provider
class Package
class Windows
class MSI
- include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ include Chef::ReservedNames::Win32::API::Installer if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
include Chef::Mixin::ShellOut
- def initialize(resource)
+ def initialize(resource, uninstall_entries)
@new_resource = resource
+ @uninstall_entries = uninstall_entries
end
+ attr_reader :new_resource
+ attr_reader :uninstall_entries
+
# From Chef::Provider::Package
def expand_options(options)
options ? " #{options}" : ""
@@ -40,27 +44,47 @@ class Chef
# Returns a version if the package is installed or nil if it is not.
def installed_version
- Chef::Log.debug("#{@new_resource} getting product code for package at #{@new_resource.source}")
- product_code = get_product_property(@new_resource.source, "ProductCode")
- Chef::Log.debug("#{@new_resource} checking package status and version for #{product_code}")
- get_installed_version(product_code)
+ if !new_resource.source.nil? && ::File.exist?(new_resource.source)
+ Chef::Log.debug("#{new_resource} getting product code for package at #{new_resource.source}")
+ product_code = get_product_property(new_resource.source, "ProductCode")
+ Chef::Log.debug("#{new_resource} checking package status and version for #{product_code}")
+ get_installed_version(product_code)
+ else
+ uninstall_entries.count == 0 ? nil : begin
+ uninstall_entries.map { |entry| entry.display_version }.uniq
+ end
+ end
end
def package_version
- Chef::Log.debug("#{@new_resource} getting product version for package at #{@new_resource.source}")
- get_product_property(@new_resource.source, "ProductVersion")
+ return new_resource.version if new_resource.version
+ if !new_resource.source.nil? && ::File.exist?(new_resource.source)
+ Chef::Log.debug("#{new_resource} getting product version for package at #{new_resource.source}")
+ get_product_property(new_resource.source, "ProductVersion")
+ end
end
- def install_package(name, version)
+ def install_package
# We could use MsiConfigureProduct here, but we'll start off with msiexec
- Chef::Log.debug("#{@new_resource} installing MSI package '#{@new_resource.source}'")
- shell_out!("msiexec /qn /i \"#{@new_resource.source}\" #{expand_options(@new_resource.options)}", {:timeout => @new_resource.timeout, :returns => @new_resource.returns})
+ Chef::Log.debug("#{new_resource} installing MSI package '#{new_resource.source}'")
+ shell_out!("msiexec /qn /i \"#{new_resource.source}\" #{expand_options(new_resource.options)}", {:timeout => new_resource.timeout, :returns => new_resource.returns})
end
-
- def remove_package(name, version)
+
+ def remove_package
# We could use MsiConfigureProduct here, but we'll start off with msiexec
- Chef::Log.debug("#{@new_resource} removing MSI package '#{@new_resource.source}'")
- shell_out!("msiexec /qn /x \"#{@new_resource.source}\" #{expand_options(@new_resource.options)}", {:timeout => @new_resource.timeout, :returns => @new_resource.returns})
+ if !new_resource.source.nil? && ::File.exist?(new_resource.source)
+ Chef::Log.debug("#{new_resource} removing MSI package '#{new_resource.source}'")
+ shell_out!("msiexec /qn /x \"#{new_resource.source}\" #{expand_options(new_resource.options)}", {:timeout => new_resource.timeout, :returns => new_resource.returns})
+ else
+ uninstall_version = new_resource.version || installed_version
+ uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) }
+ .map { |version| version.uninstall_string }.uniq.each do |uninstall_string|
+ Chef::Log.debug("#{new_resource} removing MSI package version using '#{uninstall_string}'")
+ uninstall_string += expand_options(new_resource.options)
+ uninstall_string += " /Q" unless uninstall_string =~ / \/Q\b/
+ shell_out!(uninstall_string, {:timeout => new_resource.timeout, :returns => new_resource.returns})
+ end
+ end
end
end
end
diff --git a/lib/chef/provider/package/windows/registry_uninstall_entry.rb b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
new file mode 100644
index 0000000000..145f799e05
--- /dev/null
+++ b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
@@ -0,0 +1,89 @@
+#
+# Author:: Seth Chisamore (<schisamo@chef.io>)
+# Author:: Matt Wrock <matt@mattwrock.com>
+# Copyright:: Copyright (c) 2011, 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "win32/registry" if (RUBY_PLATFORM =~ /mswin|mingw32|windows/)
+
+class Chef
+ class Provider
+ class Package
+ class Windows
+ class RegistryUninstallEntry
+
+ def self.find_entries(package_name)
+ Chef::Log.debug("Finding uninstall entries for #{package_name}")
+ entries = []
+ [
+ [::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0100)],
+ [::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0200)],
+ [::Win32::Registry::HKEY_CURRENT_USER],
+ ].each do |hkey|
+ desired = hkey.length > 1 ? hkey[1] : ::Win32::Registry::Constants::KEY_READ
+ begin
+ ::Win32::Registry.open(hkey[0], UNINSTALL_SUBKEY, desired) do |reg|
+ reg.each_key do |key, _wtime|
+ begin
+ entry = reg.open(key, desired)
+ display_name = read_registry_property(entry, "DisplayName")
+ if display_name == package_name
+ entries.push(RegistryUninstallEntry.new(hkey, key, entry))
+ end
+ rescue ::Win32::Registry::Error => ex
+ Chef::Log.debug("Registry error opening key '#{key}' on node #{desired}: #{ex}")
+ end
+ end
+ end
+ rescue ::Win32::Registry::Error => ex
+ Chef::Log.debug("Registry error opening hive '#{hkey[0]}' :: #{desired}: #{ex}")
+ end
+ end
+ entries
+ end
+
+ def self.read_registry_property(data, property)
+ data[property]
+ rescue ::Win32::Registry::Error => ex
+ Chef::Log.debug("Failure to read property '#{property}'")
+ nil
+ end
+
+ def initialize(hive, key, registry_data)
+ Chef::Log.debug("Creating uninstall entry for #{hive}::#{key}")
+ @hive = hive
+ @key = key
+ @data = registry_data
+ @display_name = RegistryUninstallEntry.read_registry_property(registry_data, "DisplayName")
+ @display_version = RegistryUninstallEntry.read_registry_property(registry_data, "DisplayVersion")
+ @uninstall_string = RegistryUninstallEntry.read_registry_property(registry_data, "UninstallString")
+ end
+
+ attr_reader :hive
+ attr_reader :key
+ attr_reader :display_name
+ attr_reader :display_version
+ attr_reader :uninstall_string
+ attr_reader :data
+
+ private
+
+ UNINSTALL_SUBKEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'.freeze
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 49c6f6beb5..c3fd3f69ec 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.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,18 +16,19 @@
# limitations under the License.
#
-require 'chef/config'
-require 'chef/provider/package'
-require 'chef/mixin/shell_out'
-require 'chef/resource/package'
-require 'singleton'
-require 'chef/mixin/get_source_from_package'
+require "chef/config"
+require "chef/provider/package"
+require "chef/mixin/which"
+require "chef/resource/package"
+require "singleton"
+require "chef/mixin/get_source_from_package"
class Chef
class Provider
class Package
class Yum < Chef::Provider::Package
+ provides :package, platform_family: %w{rhel fedora}
provides :yum_package, os: "linux"
class RPMUtils
@@ -646,10 +647,12 @@ class Chef
# Cache for our installed and available packages, pulled in from yum-dump.py
class YumCache
- include Chef::Mixin::Command
+ include Chef::Mixin::Which
include Chef::Mixin::ShellOut
include Singleton
+ attr_accessor :yum_binary
+
def initialize
@rpmdb = RPMDb.new
@@ -709,11 +712,11 @@ class Chef
one_line = false
error = nil
- helper = ::File.join(::File.dirname(__FILE__), 'yum-dump.py')
+ helper = ::File.join(::File.dirname(__FILE__), "yum-dump.py")
status = nil
begin
- status = shell_out!("/usr/bin/python #{helper}#{opts}", :timeout => Chef::Config[:yum_timeout])
+ status = shell_out!("#{python_bin} #{helper}#{opts}", :timeout => Chef::Config[:yum_timeout])
status.stdout.each_line do |line|
one_line = true
@@ -779,6 +782,42 @@ class Chef
@next_refresh = :none
end
+ def python_bin
+ yum_executable = which(yum_binary)
+ if yum_executable && shabang?(yum_executable)
+ shabang_or_fallback(extract_interpreter(yum_executable))
+ else
+ Chef::Log.warn("Yum executable not found or doesn't start with #!. Using default python.")
+ "/usr/bin/python"
+ end
+ rescue StandardError => e
+ Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.")
+ Chef::Log.debug(e)
+ "/usr/bin/python"
+ end
+
+ def extract_interpreter(file)
+ ::File.open(file, "r", &:readline)[2..-1].strip
+ end
+
+ # dnf based systems have a yum shim that has /bin/bash as the interpreter. Don't use this.
+ def shabang_or_fallback(interpreter)
+ if interpreter == "/bin/bash"
+ Chef::Log.warn("Yum executable interpreter is /bin/bash. Falling back to default python.")
+ "/usr/bin/python"
+ else
+ interpreter
+ end
+ end
+
+ def shabang?(file)
+ ::File.open(file, "r") do |f|
+ f.read(2) == '#!'
+ end
+ rescue Errno::ENOENT
+ false
+ end
+
def reload
@next_refresh = :all
end
@@ -953,11 +992,31 @@ class Chef
super
@yum = YumCache.instance
+ @yum.yum_binary = yum_binary
+ end
+
+ def yum_binary
+ @yum_binary ||=
+ begin
+ yum_binary = new_resource.yum_binary if new_resource.is_a?(Chef::Resource::YumPackage)
+ yum_binary ||= ::File.exist?("/usr/bin/yum-deprecated") ? "yum-deprecated" : "yum"
+ end
end
# Extra attributes
#
+ def arch_for_name(n)
+ if @new_resource.respond_to?("arch")
+ @new_resource.arch
+ elsif @arch
+ idx = package_name_array.index(n)
+ as_array(@arch)[idx]
+ else
+ nil
+ end
+ end
+
def arch
if @new_resource.respond_to?("arch")
@new_resource.arch
@@ -966,6 +1025,12 @@ class Chef
end
end
+ def set_arch(arch)
+ if @new_resource.respond_to?("arch")
+ @new_resource.arch(arch)
+ end
+ end
+
def flush_cache
if @new_resource.respond_to?("flush_cache")
@new_resource.flush_cache
@@ -977,12 +1042,14 @@ class Chef
# Helpers
#
- def yum_arch
+ def yum_arch(arch)
arch ? ".#{arch}" : nil
end
def yum_command(command)
- status = shell_out(command, {:timeout => Chef::Config[:yum_timeout]})
+ command = "#{yum_binary} #{command}"
+ Chef::Log.debug("#{@new_resource}: yum command: \"#{command}\"")
+ status = shell_out_with_timeout(command, {:timeout => Chef::Config[:yum_timeout]})
# This is fun: rpm can encounter errors in the %post/%postun scripts which aren't
# considered fatal - meaning the rpm is still successfully installed. These issue
@@ -999,7 +1066,7 @@ class Chef
if l =~ %r{^error: %(post|postun)\(.*\) scriptlet failed, exit status \d+$}
Chef::Log.warn("#{@new_resource} caught non-fatal scriptlet issue: \"#{l}\". Can't trust yum exit status " +
"so running install again to verify.")
- status = shell_out(command, {:timeout => Chef::Config[:yum_timeout]})
+ status = shell_out_with_timeout(command, {:timeout => Chef::Config[:yum_timeout]})
break
end
end
@@ -1059,23 +1126,20 @@ class Chef
end
end
- # Don't overwrite an existing arch
- unless arch
- parse_arch
- end
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
installed_version = []
@candidate_version = []
+ @arch = []
if @new_resource.source
unless ::File.exists?(@new_resource.source)
raise Chef::Exceptions::Package, "Package #{@new_resource.name} not found: #{@new_resource.source}"
end
Chef::Log.debug("#{@new_resource} checking rpm status")
- shell_out!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}", :timeout => Chef::Config[:yum_timeout]).stdout.each_line do |line|
+ shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}", :timeout => Chef::Config[:yum_timeout]).stdout.each_line do |line|
case line
when /([\w\d_.-]+)\s([\w\d_.-]+)/
@current_resource.package_name($1)
@@ -1085,24 +1149,43 @@ class Chef
@candidate_version << @new_resource.version
installed_version << @yum.installed_version(@current_resource.package_name, arch)
else
- if @new_resource.version
- new_resource = "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch}"
- else
- new_resource = "#{@new_resource.package_name}#{yum_arch}"
- end
- Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}")
+ package_name_array.each_with_index do |pkg, idx|
+ # Don't overwrite an existing arch
+ if arch
+ name, parch = pkg, arch
+ else
+ name, parch = parse_arch(pkg)
+ # if we parsed an arch from the name, update the name
+ # to be just the package name.
+ if parch
+ if @new_resource.package_name.is_a?(Array)
+ @new_resource.package_name[idx] = name
+ else
+ @new_resource.package_name(name)
+ # only set the arch if it's a single package
+ set_arch(parch)
+ end
+ end
+ end
- package_name_array.each do |pkg|
- installed_version << @yum.installed_version(pkg, arch)
- @candidate_version << @yum.candidate_version(pkg, arch)
+ if @new_resource.version
+ new_resource =
+ "#{@new_resource.package_name}-#{@new_resource.version}#{yum_arch(parch)}"
+ else
+ new_resource = "#{@new_resource.package_name}#{yum_arch(parch)}"
+ end
+ Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}")
+ installed_version << @yum.installed_version(name, parch)
+ @candidate_version << @yum.candidate_version(name, parch)
+ @arch << parch
end
-
end
if installed_version.size == 1
@current_resource.version(installed_version[0])
@candidate_version = @candidate_version[0]
+ @arch = @arch[0]
else
@current_resource.version(installed_version)
end
@@ -1117,7 +1200,7 @@ class Chef
# Work around yum not exiting with an error if a package doesn't exist
# for CHEF-2062
all_avail = as_array(name).zip(as_array(version)).any? do |n, v|
- @yum.version_available?(n, v, arch)
+ @yum.version_available?(n, v, arch_for_name(n))
end
method = log_method = nil
methods = []
@@ -1159,20 +1242,20 @@ class Chef
repos = []
pkg_string_bits = []
- index = 0
as_array(name).zip(as_array(version)).each do |n, v|
- s = ''
- unless v == current_version_array[index]
- s = "#{n}-#{v}#{yum_arch}"
- repo = @yum.package_repository(n, v, arch)
+ idx = package_name_array.index(n)
+ a = arch_for_name(n)
+ s = ""
+ unless v == current_version_array[idx]
+ s = "#{n}-#{v}#{yum_arch(a)}"
+ repo = @yum.package_repository(n, v, a)
repos << "#{s} from #{repo} repository"
pkg_string_bits << s
end
- index += 1
end
- pkg_string = pkg_string_bits.join(' ')
+ pkg_string = pkg_string_bits.join(" ")
Chef::Log.info("#{@new_resource} #{log_method} #{repos.join(' ')}")
- yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{pkg_string}")
+ yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{pkg_string}")
else
raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " +
"and release? (version-release, e.g. 1.84-10.fc6)"
@@ -1181,7 +1264,7 @@ class Chef
def install_package(name, version)
if @new_resource.source
- yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}")
+ yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}")
else
install_remote_package(name, version)
end
@@ -1219,13 +1302,17 @@ class Chef
def remove_package(name, version)
if version
- remove_str = as_array(name).zip(as_array(version)).map do |x|
- "#{x.join('-')}#{yum_arch}"
- end.join(' ')
+ remove_str = as_array(name).zip(as_array(version)).map do |n, v|
+ a = arch_for_name(n)
+ "#{[n, v].join('-')}#{yum_arch(a)}"
+ end.join(" ")
else
- remove_str = as_array(name).map { |n| "#{n}#{yum_arch}" }.join(' ')
+ remove_str = as_array(name).map do |n|
+ a = arch_for_name(n)
+ "#{n}#{yum_arch(a)}"
+ end.join(" ")
end
- yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{remove_str}")
+ yum_command("-d0 -e0 -y#{expand_options(@new_resource.options)} remove #{remove_str}")
if flush_cache[:after]
@yum.reload
@@ -1240,22 +1327,26 @@ class Chef
private
- def parse_arch
+ def parse_arch(package_name)
# Allow for foo.x86_64 style package_name like yum uses in it's output
#
- if @new_resource.package_name =~ %r{^(.*)\.(.*)$}
+ if package_name =~ %r{^(.*)\.(.*)$}
new_package_name = $1
new_arch = $2
# foo.i386 and foo.beta1 are both valid package names or expressions of an arch.
# Ensure we don't have an existing package matching package_name, then ensure we at
# least have a match for the new_package+new_arch before we overwrite. If neither
# then fall through to standard package handling.
- if (@yum.installed_version(@new_resource.package_name).nil? and @yum.candidate_version(@new_resource.package_name).nil?) and
- (@yum.installed_version(new_package_name, new_arch) or @yum.candidate_version(new_package_name, new_arch))
- @new_resource.package_name(new_package_name)
- @new_resource.arch(new_arch)
+ old_installed = @yum.installed_version(package_name)
+ old_candidate = @yum.candidate_version(package_name)
+ new_installed = @yum.installed_version(new_package_name, new_arch)
+ new_candidate = @yum.candidate_version(new_package_name, new_arch)
+ if (old_installed.nil? and old_candidate.nil?) and (new_installed or new_candidate)
+ Chef::Log.debug("Parsed out arch #{new_arch}, new package name is #{new_package_name}")
+ return new_package_name, new_arch
end
end
+ return package_name, nil
end
# If we don't have the package we could have been passed a 'whatprovides' feature
@@ -1300,7 +1391,7 @@ class Chef
new_package_name = packages.first.name
new_package_version = packages.first.version.to_s
debug_msg = "#{name}: Unable to match package '#{name}' but matched #{packages.size} "
- debug_msg << packages.size == 1 ? "package" : "packages"
+ debug_msg << (packages.size == 1 ? "package" : "packages")
debug_msg << ", selected '#{new_package_name}' version '#{new_package_version}'"
Chef::Log.debug(debug_msg)
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index 2cd321660b..9b0aaf322a 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -19,56 +19,58 @@
# limitations under the License.
#
-require 'chef/provider/package'
-require 'chef/mixin/command'
-require 'chef/resource/package'
-require 'singleton'
+require "chef/provider/package"
+require "chef/mixin/command"
+require "chef/resource/package"
+require "singleton"
class Chef
class Provider
class Package
class Zypper < Chef::Provider::Package
+ provides :package, platform_family: "suse"
+ provides :zypper_package, os: "linux"
+
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
+ @current_resource = Chef::Resource::ZypperPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
is_installed=false
is_out_of_date=false
- version=''
- oud_version=''
- Chef::Log.debug("#{@new_resource} checking zypper")
- status = shell_out("zypper --non-interactive info #{@new_resource.package_name}")
+ version=""
+ oud_version=""
+ Chef::Log.debug("#{new_resource} checking zypper")
+ status = shell_out_with_timeout("zypper --non-interactive info #{new_resource.package_name}")
status.stdout.each_line do |line|
case line
when /^Version: (.+)$/
version = $1
- Chef::Log.debug("#{@new_resource} version #{$1}")
+ Chef::Log.debug("#{new_resource} version #{$1}")
when /^Installed: Yes$/
is_installed=true
- Chef::Log.debug("#{@new_resource} is installed")
+ Chef::Log.debug("#{new_resource} is installed")
when /^Installed: No$/
is_installed=false
- Chef::Log.debug("#{@new_resource} is not installed")
+ Chef::Log.debug("#{new_resource} is not installed")
when /^Status: out-of-date \(version (.+) installed\)$/
is_out_of_date=true
oud_version=$1
- Chef::Log.debug("#{@new_resource} out of date version #{$1}")
+ Chef::Log.debug("#{new_resource} out of date version #{$1}")
end
end
if is_installed==false
@candidate_version=version
- @current_resource.version(nil)
end
if is_installed==true
if is_out_of_date==true
- @current_resource.version(oud_version)
+ current_resource.version(oud_version)
@candidate_version=version
else
- @current_resource.version(version)
+ current_resource.version(version)
@candidate_version=version
end
end
@@ -77,7 +79,7 @@ class Chef
raise Chef::Exceptions::Package, "zypper failed - #{status.inspect}!"
end
- @current_resource
+ current_resource
end
def zypper_version()
@@ -104,9 +106,9 @@ class Chef
def zypper_package(command, pkgname, version)
version = "=#{version}" unless version.nil? || version.empty?
if zypper_version < 1.0
- shell_out!("zypper#{gpg_checks} #{command} -y #{pkgname}")
+ shell_out_with_timeout!("zypper#{gpg_checks} #{command} -y #{pkgname}")
else
- shell_out!("zypper --non-interactive#{gpg_checks} "+
+ shell_out_with_timeout!("zypper --non-interactive#{gpg_checks} "+
"#{command} #{pkgname}#{version}")
end
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index f9dcd6d80c..d3b586e75d 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -16,7 +16,8 @@
# limitations under the License.
#
-require 'chef/provider/windows_script'
+require "chef/platform/query_helpers"
+require "chef/provider/windows_script"
class Chef
class Provider
@@ -24,71 +25,191 @@ class Chef
provides :powershell_script, os: "windows"
+ def initialize (new_resource, run_context)
+ super(new_resource, run_context, ".ps1")
+ add_exit_status_wrapper
+ end
+
+ def action_run
+ validate_script_syntax!
+ super
+ end
+
+ def command
+ basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+
+ # Powershell.exe is always in "v1.0" folder (for backwards compatibility)
+ interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter)
+
+ # Must use -File rather than -Command to launch the script
+ # file created by the base class that contains the script
+ # code -- otherwise, powershell.exe does not propagate the
+ # error status of a failed Windows process that ran at the
+ # end of the script, it gets changed to '1'.
+ #
+ # Nano only supports -Command
+ cmd = "\"#{interpreter_path}\" #{flags}"
+ if Chef::Platform.windows_nano_server?
+ cmd << " -Command \". '#{script_file.path}'\""
+ else
+ cmd << " -File \"#{script_file.path}\""
+ end
+ cmd
+ end
+
+ def flags
+ interpreter_flags = [*default_interpreter_flags].join(" ")
+
+ if ! (@new_resource.flags.nil?)
+ interpreter_flags = [@new_resource.flags, interpreter_flags].join(" ")
+ end
+
+ interpreter_flags
+ end
+
protected
- EXIT_STATUS_EXCEPTION_HANDLER = "\ntrap [Exception] {write-error -exception ($_.Exception.Message);exit 1}".freeze
- EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE ) {exit $LASTEXITCODE} else { exit 1 }}".freeze
- EXIT_STATUS_RESET_SCRIPT = "\n$global:LASTEXITCODE=$null".freeze
- # Process exit codes are strange with PowerShell. Unless you
- # explicitly call exit in Powershell, the powershell.exe
- # interpreter returns only 0 for success or 1 for failure. Since
- # we'd like to get specific exit codes from executable tools run
- # with Powershell, we do some work using the automatic variables
- # $? and $LASTEXITCODE to return the process exit code of the
- # last process run in the script if it is the last command
- # executed, otherwise 0 or 1 based on whether $? is set to true
- # (success, where we return 0) or false (where we return 1).
- def normalize_script_exit_status( code )
- target_code = ( EXIT_STATUS_EXCEPTION_HANDLER +
- EXIT_STATUS_RESET_SCRIPT +
- "\n" +
- code.to_s +
- EXIT_STATUS_NORMALIZATION_SCRIPT )
- convert_boolean_return = @new_resource.convert_boolean_return
- self.code = <<EOH
-new-variable -name interpolatedexitcode -visibility private -value $#{convert_boolean_return}
-new-variable -name chefscriptresult -visibility private
-$chefscriptresult = {
-#{target_code}
-}.invokereturnasis()
-if ($interpolatedexitcode -and $chefscriptresult.gettype().name -eq 'boolean') { exit [int32](!$chefscriptresult) } else { exit 0 }
-EOH
- Chef::Log.debug("powershell_script provider called with script code:\n\n#{code}\n")
+ # Process exit codes are strange with PowerShell and require
+ # special handling to cover common use cases.
+ def add_exit_status_wrapper
+ self.code = wrapper_script
+ Chef::Log.debug("powershell_script provider called with script code:\n\n#{@new_resource.code}\n")
Chef::Log.debug("powershell_script provider will execute transformed code:\n\n#{self.code}\n")
end
- public
+ def validate_script_syntax!
+ interpreter_arguments = default_interpreter_flags.join(" ")
+ Tempfile.open(["chef_powershell_script-user-code", ".ps1"]) do | user_script_file |
+ # Wrap the user's code in a PowerShell script block so that
+ # it isn't executed. However, syntactically invalid script
+ # in that block will still trigger a syntax error which is
+ # exactly what we want here -- verify the syntax without
+ # actually running the script.
+ user_code_wrapped_in_powershell_script_block = <<-EOH
+{
+ #{@new_resource.code}
+}
+EOH
+ user_script_file.puts user_code_wrapped_in_powershell_script_block
- def initialize (new_resource, run_context)
- super(new_resource, run_context, '.ps1')
- normalize_script_exit_status(new_resource.code)
+ # A .close or explicit .flush required to ensure the file is
+ # written to the file system at this point, which is required since
+ # the intent is to execute the code just written to it.
+ user_script_file.close
+ validation_command = "\"#{interpreter}\" #{interpreter_arguments} -Command \". '#{user_script_file.path}'\""
+
+ # Note that other script providers like bash allow syntax errors
+ # to be suppressed by setting 'returns' to a value that the
+ # interpreter would return as a status code in the syntax
+ # error case. We explicitly don't do this here -- syntax
+ # errors will not be suppressed, since doing so could make
+ # it harder for users to detect / debug invalid scripts.
+
+ # Therefore, the only return value for a syntactically valid
+ # script is 0. If an exception is raised by shellout, this
+ # means a non-zero return and thus a syntactically invalid script.
+
+ with_os_architecture(node, architecture: new_resource.architecture) do
+ shell_out!(validation_command, {returns: [0]})
+ end
+ end
end
- def flags
- default_flags = [
+ def default_interpreter_flags
+ return [] if Chef::Platform.windows_nano_server?
+
+ # Execution policy 'Bypass' is preferable since it doesn't require
+ # user input confirmation for files such as PowerShell modules
+ # downloaded from the Internet. However, 'Bypass' is not supported
+ # prior to PowerShell 3.0, so the fallback is 'Unrestricted'
+ execution_policy = Chef::Platform.supports_powershell_execution_bypass?(run_context.node) ? "Bypass" : "Unrestricted"
+
+ [
"-NoLogo",
"-NonInteractive",
"-NoProfile",
- "-ExecutionPolicy Unrestricted",
+ "-ExecutionPolicy #{execution_policy}",
# Powershell will hang if STDIN is redirected
# http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
"-InputFormat None",
- # Must use -File rather than -Command to launch the script
- # file created by the base class that contains the script
- # code -- otherwise, powershell.exe does not propagate the
- # error status of a failed Windows process that ran at the
- # end of the script, it gets changed to '1'.
- "-File"
]
+ end
- interpreter_flags = default_flags.join(' ')
+ # A wrapper script is used to launch user-supplied script while
+ # still obtaining useful process exit codes. Unless you
+ # explicitly call exit in Powershell, the powershell.exe
+ # interpreter returns only 0 for success or 1 for failure. Since
+ # we'd like to get specific exit codes from executable tools run
+ # with Powershell, we do some work using the automatic variables
+ # $? and $LASTEXITCODE to return the process exit code of the
+ # last process run in the script if it is the last command
+ # executed, otherwise 0 or 1 based on whether $? is set to true
+ # (success, where we return 0) or false (where we return 1).
+ def wrapper_script
+<<-EOH
+# Chef Client wrapper for powershell_script resources
- if ! (@new_resource.flags.nil?)
- interpreter_flags = [@new_resource.flags, interpreter_flags].join(' ')
- end
+# LASTEXITCODE can be uninitialized -- make it explictly 0
+# to avoid incorrect detection of failure (non-zero) codes
+$global:LASTEXITCODE = 0
- interpreter_flags
+# Catch any exceptions -- without this, exceptions will result
+# In a zero return code instead of the desired non-zero code
+# that indicates a failure
+trap [Exception] {write-error ($_.Exception.Message);exit 1}
+
+# Variable state that should not be accessible to the user code
+new-variable -name interpolatedexitcode -visibility private -value $#{@new_resource.convert_boolean_return}
+new-variable -name chefscriptresult -visibility private
+
+# Initialize a variable we use to capture $? inside a block
+$global:lastcmdlet = $null
+
+# Execute the user's code in a script block --
+$chefscriptresult =
+{
+ #{@new_resource.code}
+
+ # This assignment doesn't affect the block's return value
+ $global:lastcmdlet = $?
+}.invokereturnasis()
+
+# Assume failure status of 1 -- success cases
+# will have to override this
+$exitstatus = 1
+
+# If convert_boolean_return is enabled, the block's return value
+# gets precedence in determining our exit status
+if ($interpolatedexitcode -and $chefscriptresult -ne $null -and $chefscriptresult.gettype().name -eq 'boolean')
+{
+ $exitstatus = [int32](!$chefscriptresult)
+}
+elseif ($lastcmdlet)
+{
+ # Otherwise, a successful cmdlet execution defines the status
+ $exitstatus = 0
+}
+elseif ( $LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0 )
+{
+ # If the cmdlet status is failed, allow the Win32 status
+ # in $LASTEXITCODE to define exit status. This handles the case
+ # where no cmdlets, only Win32 processes have run since $?
+ # will be set to $false whenever a Win32 process returns a non-zero
+ # status.
+ $exitstatus = $LASTEXITCODE
+}
+
+# Print STDOUT for the script execution
+Write-Output $chefscriptresult
+
+# If this script is launched with -File, the process exit
+# status of PowerShell.exe will be $exitstatus. If it was
+# launched with -Command, it will be 0 if $exitstatus was 0,
+# 1 (i.e. failed) otherwise.
+exit $exitstatus
+EOH
end
+
end
end
end
diff --git a/lib/chef/provider/reboot.rb b/lib/chef/provider/reboot.rb
index 8dde4653ec..3f64955d21 100644
--- a/lib/chef/provider/reboot.rb
+++ b/lib/chef/provider/reboot.rb
@@ -16,12 +16,13 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/provider'
+require "chef/log"
+require "chef/provider"
class Chef
class Provider
class Reboot < Chef::Provider
+ provides :reboot
def whyrun_supported?
true
@@ -39,7 +40,7 @@ class Chef
:delay_mins => @new_resource.delay_mins,
:reason => @new_resource.reason,
:timestamp => Time.now,
- :requested_by => @new_resource.name
+ :requested_by => @new_resource.name,
)
end
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index 94f4e2655b..c6a06e0974 100644
--- a/lib/chef/provider/registry_key.rb
+++ b/lib/chef/provider/registry_key.rb
@@ -17,20 +17,22 @@
# limitations under the License.
#
-require 'chef/config'
-require 'chef/log'
-require 'chef/resource/file'
-require 'chef/mixin/checksum'
-require 'chef/provider'
-require 'etc'
-require 'fileutils'
-require 'chef/scan_access_control'
-require 'chef/win32/registry'
+require "chef/config"
+require "chef/log"
+require "chef/resource/file"
+require "chef/mixin/checksum"
+require "chef/provider"
+require "etc"
+require "fileutils"
+require "chef/scan_access_control"
+require "chef/win32/registry"
class Chef
class Provider
class RegistryKey < Chef::Provider
+ provides :registry_key
+
include Chef::Mixin::Checksum
def whyrun_supported?
@@ -62,7 +64,7 @@ class Chef
def values_to_hash(values)
if values
- @name_hash = Hash[values.map { |val| [val[:name], val] }]
+ @name_hash = Hash[values.map { |val| [val[:name].downcase, val] }]
else
@name_hash = {}
end
@@ -98,8 +100,8 @@ class Chef
end
end
@new_resource.unscrubbed_values.each do |value|
- if @name_hash.has_key?(value[:name])
- current_value = @name_hash[value[:name]]
+ if @name_hash.has_key?(value[:name].downcase)
+ current_value = @name_hash[value[:name].downcase]
unless current_value[:type] == value[:type] && current_value[:data] == value[:data]
converge_by("set value #{value}") do
registry.set_value(@new_resource.key, value)
@@ -120,7 +122,7 @@ class Chef
end
end
@new_resource.unscrubbed_values.each do |value|
- unless @name_hash.has_key?(value[:name])
+ unless @name_hash.has_key?(value[:name].downcase)
converge_by("create value #{value}") do
registry.set_value(@new_resource.key, value)
end
@@ -131,7 +133,7 @@ class Chef
def action_delete
if registry.key_exists?(@new_resource.key)
@new_resource.unscrubbed_values.each do |value|
- if @name_hash.has_key?(value[:name])
+ if @name_hash.has_key?(value[:name].downcase)
converge_by("delete value #{value}") do
registry.delete_value(@new_resource.key, value)
end
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb
index eaccce46cf..02270201cb 100644
--- a/lib/chef/provider/remote_directory.rb
+++ b/lib/chef/provider/remote_directory.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,178 +16,266 @@
# limitations under the License.
#
-require 'chef/provider/file'
-require 'chef/provider/directory'
-require 'chef/resource/directory'
-require 'chef/resource/remote_file'
-require 'chef/mixin/file_class'
-require 'chef/platform'
-require 'uri'
-require 'tempfile'
-require 'net/https'
-require 'set'
-require 'chef/util/path_helper'
+require "chef/provider/directory"
+require "chef/resource/file"
+require "chef/resource/directory"
+require "chef/resource/cookbook_file"
+require "chef/mixin/file_class"
+require "chef/platform/query_helpers"
+require "chef/util/path_helper"
+require "chef/deprecation/warnings"
+require "chef/deprecation/provider/remote_directory"
+
+require "forwardable"
class Chef
class Provider
class RemoteDirectory < Chef::Provider::Directory
+ extend Forwardable
+ include Chef::Mixin::FileClass
provides :remote_directory
- include Chef::Mixin::FileClass
+ def_delegators :@new_resource, :purge, :path, :source, :cookbook, :cookbook_name
+ def_delegators :@new_resource, :files_rights, :files_mode, :files_group, :files_owner, :files_backup
+ def_delegators :@new_resource, :rights, :mode, :group, :owner
+
+ # The overwrite property on the resource. Delegates to new_resource but can be mutated.
+ #
+ # @return [Boolean] if we are overwriting
+ #
+ def overwrite?
+ @overwrite = new_resource.overwrite if @overwrite.nil?
+ !!@overwrite
+ end
+
+ attr_accessor :managed_files
+
+ # Hash containing keys of the paths for all the files that we sync, plus all their
+ # parent directories.
+ #
+ # @return [Set] Ruby Set of the files that we manage
+ #
+ def managed_files
+ @managed_files ||= Set.new
+ end
+ # Handle action :create.
+ #
def action_create
super
- # Mark all files as needing to be purged
- files_to_purge = Set.new(ls(@new_resource.path)) # Make sure each path is clean
# Transfer files
files_to_transfer.each do |cookbook_file_relative_path|
create_cookbook_file(cookbook_file_relative_path)
- # parent directories and file being transferred are removed from the purge list
- Pathname.new(Chef::Util::PathHelper.cleanpath(::File.join(@new_resource.path, cookbook_file_relative_path))).descend do |d|
- files_to_purge.delete(d.to_s)
- end
+ # parent directories and file being transferred need to not be removed in the purge
+ add_managed_file(cookbook_file_relative_path)
end
- purge_unmanaged_files(files_to_purge)
+ purge_unmanaged_files
end
+ # Handle action :create_if_missing.
+ #
def action_create_if_missing
# if this action is called, ignore the existing overwrite flag
- @new_resource.overwrite(false)
+ @overwrite = false
action_create
end
- protected
+ private
- # List all excluding . and ..
- def ls(path)
- files = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), '**', '*'),
- ::File::FNM_DOTMATCH)
-
- # Remove current directory and previous directory
- files.reject! do |name|
- basename = Pathname.new(name).basename().to_s
- ['.', '..'].include?(basename)
+ # Add a file and its parent directories to the managed_files Hash.
+ #
+ # @param [String] cookbook_file_relative_path relative path to the file
+ # @api private
+ #
+ def add_managed_file(cookbook_file_relative_path)
+ if purge
+ Pathname.new(Chef::Util::PathHelper.cleanpath(::File.join(path, cookbook_file_relative_path))).descend do |d|
+ managed_files.add(d.to_s)
+ end
end
-
- # Clean all the paths... this is required because of the join
- files.map {|f| Chef::Util::PathHelper.cleanpath(f)}
end
- def purge_unmanaged_files(unmanaged_files)
- if @new_resource.purge
- unmanaged_files.sort.reverse.each do |f|
- # file_class comes from Chef::Mixin::FileClass
- if ::File.directory?(f) && !Chef::Platform.windows? && !file_class.symlink?(f.dup)
- # Linux treats directory symlinks as files
- # Remove a directory as a directory when not on windows if it is not a symlink
- purge_directory(f)
- elsif ::File.directory?(f) && Chef::Platform.windows?
- # Windows treats directory symlinks as directories so we delete them here
- purge_directory(f)
- else
- converge_by("delete unmanaged file #{f}") do
- ::File.delete(f)
- Chef::Log.debug("#{@new_resource} deleted file #{f}")
+ # Remove all files not in the managed_files Set.
+ #
+ # @api private
+ #
+ def purge_unmanaged_files
+ if purge
+ Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), "**", "*"), ::File::FNM_DOTMATCH).sort!.reverse!.each do |file|
+ # skip '.' and '..'
+ next if [".",".."].include?(Pathname.new(file).basename().to_s)
+
+ # Clean the path. This is required because of the ::File.join
+ file = Chef::Util::PathHelper.cleanpath(file)
+
+ # Skip files that we've sync'd and their parent dirs
+ next if managed_files.include?(file)
+
+ if ::File.directory?(file)
+ if !Chef::Platform.windows? && file_class.symlink?(file.dup)
+ # Unix treats dir symlinks as files
+ purge_file(file)
+ else
+ # Unix dirs are dirs, Windows dirs and dir symlinks are dirs
+ purge_directory(file)
end
+ else
+ purge_file(file)
end
end
end
end
+ # Use a Chef directory sub-resource to remove a directory.
+ #
+ # @param [String] dir The path of the directory to remove
+ # @api private
+ #
def purge_directory(dir)
- converge_by("delete unmanaged directory #{dir}") do
- Dir::rmdir(dir)
- Chef::Log.debug("#{@new_resource} removed directory #{dir}")
- end
+ res = Chef::Resource::Directory.new(dir, run_context)
+ res.run_action(:delete)
+ new_resource.updated_by_last_action(true) if res.updated?
end
+ # Use a Chef file sub-resource to remove a file.
+ #
+ # @param [String] file The path of the file to remove
+ # @api private
+ #
+ def purge_file(file)
+ res = Chef::Resource::File.new(file, run_context)
+ res.run_action(:delete)
+ new_resource.updated_by_last_action(true) if res.updated?
+ end
+
+ # Get the files to tranfer. This returns files in lexicographical sort order.
+ #
+ # FIXME: it should do breadth-first, see CHEF-5080 (please use a performant sort)
+ #
+ # @return Array<String> The list of files to transfer
+ # @api private
+ #
def files_to_transfer
cookbook = run_context.cookbook_collection[resource_cookbook]
- files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source)
- files.sort.reverse
+ files = cookbook.relative_filenames_in_preferred_directory(node, :files, source)
+ files.sort_by! { |x| x.count(::File::SEPARATOR) }
end
- def directory_root_in_cookbook_cache
- @directory_root_in_cookbook_cache ||= begin
- cookbook = run_context.cookbook_collection[resource_cookbook]
- cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path)
- end
+ # Either the explicit cookbook that the user sets on the resource, or the implicit
+ # cookbook_name that the resource was declared in.
+ #
+ # @return [String] Cookbook to get file from.
+ # @api private
+ #
+ def resource_cookbook
+ cookbook || cookbook_name
end
- # Determine the cookbook to get the file from. If new resource sets an
- # explicit cookbook, use it, otherwise fall back to the implicit cookbook
- # i.e., the cookbook the resource was declared in.
- def resource_cookbook
- @new_resource.cookbook || @new_resource.cookbook_name
+ # If we are overwriting, then cookbook_file sub-resources should all be action :create,
+ # otherwise they should be :create_if_missing
+ #
+ # @return [Symbol] Action to take on cookbook_file sub-resources
+ # @api private
+ #
+ def action_for_cookbook_file
+ overwrite? ? :create : :create_if_missing
end
+ # This creates and uses a cookbook_file resource to sync a single file from the cookbook.
+ #
+ # @param [String] cookbook_file_relative_path The relative path to the cookbook file
+ # @api private
+ #
def create_cookbook_file(cookbook_file_relative_path)
- full_path = ::File.join(@new_resource.path, cookbook_file_relative_path)
+ full_path = ::File.join(path, cookbook_file_relative_path)
ensure_directory_exists(::File.dirname(full_path))
- file_to_fetch = cookbook_file_resource(full_path, cookbook_file_relative_path)
- if @new_resource.overwrite
- file_to_fetch.run_action(:create)
- else
- file_to_fetch.run_action(:create_if_missing)
- end
- @new_resource.updated_by_last_action(true) if file_to_fetch.updated?
+ res = cookbook_file_resource(full_path, cookbook_file_relative_path)
+ res.run_action(action_for_cookbook_file)
+ new_resource.updated_by_last_action(true) if res.updated?
end
+ # This creates the cookbook_file resource for use by create_cookbook_file.
+ #
+ # @param [String] target_path Path on the system to create
+ # @param [String] relative_source_path Relative path in the cookbook to the base source
+ # @return [Chef::Resource::CookbookFile] The built cookbook_file resource
+ # @api private
+ #
def cookbook_file_resource(target_path, relative_source_path)
- cookbook_file = Chef::Resource::CookbookFile.new(target_path, run_context)
- cookbook_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
- cookbook_file.source(::File.join(@new_resource.source, relative_source_path))
- if Chef::Platform.windows? && @new_resource.files_rights
- @new_resource.files_rights.each_pair do |permission, *args|
- cookbook_file.rights(permission, *args)
+ res = Chef::Resource::CookbookFile.new(target_path, run_context)
+ res.cookbook_name = resource_cookbook
+ res.source(::File.join(source, relative_source_path))
+ if Chef::Platform.windows? && files_rights
+ files_rights.each_pair do |permission, *args|
+ res.rights(permission, *args)
end
end
- cookbook_file.mode(@new_resource.files_mode) if @new_resource.files_mode
- cookbook_file.group(@new_resource.files_group) if @new_resource.files_group
- cookbook_file.owner(@new_resource.files_owner) if @new_resource.files_owner
- cookbook_file.backup(@new_resource.files_backup) if @new_resource.files_backup
+ res.mode(files_mode) if files_mode
+ res.group(files_group) if files_group
+ res.owner(files_owner) if files_owner
+ res.backup(files_backup) if files_backup
- cookbook_file
+ res
end
- def ensure_directory_exists(path)
- unless ::File.directory?(path)
- directory_to_create = resource_for_directory(path)
- directory_to_create.run_action(:create)
- @new_resource.updated_by_last_action(true) if directory_to_create.updated?
+ # This creates and uses a directory resource to create a directory if it is needed.
+ #
+ # @param [String] dir The path to the directory to create.
+ # @api private
+ #
+ def ensure_directory_exists(dir)
+ # doing the check here and skipping the resource should be more performant
+ unless ::File.directory?(dir)
+ res = directory_resource(dir)
+ res.run_action(:create)
+ new_resource.updated_by_last_action(true) if res.updated?
end
end
- def resource_for_directory(path)
- dir = Chef::Resource::Directory.new(path, run_context)
- dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
- if Chef::Platform.windows? && @new_resource.rights
+ # This creates the directory resource for ensure_directory_exists.
+ #
+ # @param [String] dir Directory path on the system
+ # @return [Chef::Resource::Directory] The built directory resource
+ # @api private
+ #
+ def directory_resource(dir)
+ res = Chef::Resource::Directory.new(dir, run_context)
+ res.cookbook_name = resource_cookbook
+ if Chef::Platform.windows? && rights
# rights are only meant to be applied to the toppest-level directory;
# Windows will handle inheritance.
- if path == @new_resource.path
- @new_resource.rights.each do |rights| #rights is a hash
- permissions = rights.delete(:permissions) #delete will return the value or nil if not found
- principals = rights.delete(:principals)
- dir.rights(permissions, principals, rights)
+ if dir == path
+ rights.each do |r|
+ r = r.dup # do not update the new_resource
+ permissions = r.delete(:permissions)
+ principals = r.delete(:principals)
+ res.rights(permissions, principals, r)
end
end
end
- dir.mode(@new_resource.mode) if @new_resource.mode
- dir.group(@new_resource.group)
- dir.owner(@new_resource.owner)
- dir.recursive(true)
- dir
- end
+ res.mode(mode) if mode
+ res.group(group) if group
+ res.owner(owner) if owner
+ res.recursive(true)
- def whyrun_supported?
- true
+ res
end
+ #
+ # Add back deprecated methods and aliases that are internally unused and should be removed in Chef-13
+ #
+ extend Chef::Deprecation::Warnings
+ include Chef::Deprecation::Provider::RemoteDirectory
+ add_deprecation_warnings_for(Chef::Deprecation::Provider::RemoteDirectory.instance_methods)
+
+ alias_method :resource_for_directory, :directory_resource
+ add_deprecation_warnings_for([:resource_for_directory])
+
end
end
end
diff --git a/lib/chef/provider/remote_file.rb b/lib/chef/provider/remote_file.rb
index da2573dacb..6548300c5d 100644
--- a/lib/chef/provider/remote_file.rb
+++ b/lib/chef/provider/remote_file.rb
@@ -17,13 +17,14 @@
# limitations under the License.
#
-require 'chef/provider/file'
-require 'chef/deprecation/provider/remote_file'
-require 'chef/deprecation/warnings'
+require "chef/provider/file"
+require "chef/deprecation/provider/remote_file"
+require "chef/deprecation/warnings"
class Chef
class Provider
class RemoteFile < Chef::Provider::File
+ provides :remote_file
extend Chef::Deprecation::Warnings
include Chef::Deprecation::Provider::RemoteFile
diff --git a/lib/chef/provider/remote_file/cache_control_data.rb b/lib/chef/provider/remote_file/cache_control_data.rb
index f9b729362c..85b723ece4 100644
--- a/lib/chef/provider/remote_file/cache_control_data.rb
+++ b/lib/chef/provider/remote_file/cache_control_data.rb
@@ -19,11 +19,11 @@
# limitations under the License.
#
-require 'stringio'
-require 'chef/file_cache'
-require 'chef/json_compat'
-require 'chef/digester'
-require 'chef/exceptions'
+require "stringio"
+require "chef/file_cache"
+require "chef/json_compat"
+require "chef/digester"
+require "chef/exceptions"
class Chef
class Provider
@@ -145,18 +145,51 @@ class Chef
end
def load_json_data
- Chef::FileCache.load("remote_file/#{sanitized_cache_file_basename}")
+ path = sanitized_cache_file_path(sanitized_cache_file_basename)
+ if Chef::FileCache.has_key?(path)
+ Chef::FileCache.load(path)
+ else
+ old_path = sanitized_cache_file_path(sanitized_cache_file_basename_md5)
+ if Chef::FileCache.has_key?(old_path)
+ # We found an old cache control data file. We started using sha256 instead of md5
+ # to name these. Upgrade the file to the new name.
+ Chef::Log.debug("Found old cache control data file at #{old_path}. Moving to #{path}.")
+ Chef::FileCache.load(old_path).tap do |data|
+ Chef::FileCache.store(path, data)
+ Chef::FileCache.delete(old_path)
+ end
+ else
+ raise Chef::Exceptions::FileNotFound
+ end
+ end
end
- def sanitized_cache_file_basename
+ def sanitized_cache_file_path(basename)
+ "remote_file/#{basename}"
+ end
+
+ def scrubbed_uri
# Scrub and truncate in accordance with the goals of keeping the name
# human-readable but within the bounds of local file system
# path length limits
- scrubbed_uri = uri.gsub(/\W/, '_')[0..63]
+ uri.gsub(/\W/, "_")[0..63]
+ end
+
+ def sanitized_cache_file_basename
+ uri_sha2 = Chef::Digester.instance.generate_checksum(StringIO.new(uri))
+ cache_file_basename(uri_sha2[0,32])
+ end
+
+
+ def sanitized_cache_file_basename_md5
+ # Old way of creating the file basename
uri_md5 = Chef::Digester.instance.generate_md5_checksum(StringIO.new(uri))
- "#{scrubbed_uri}-#{uri_md5}.json"
+ cache_file_basename(uri_md5)
end
+ def cache_file_basename(checksum)
+ "#{scrubbed_uri}-#{checksum}.json"
+ end
end
end
end
diff --git a/lib/chef/provider/remote_file/content.rb b/lib/chef/provider/remote_file/content.rb
index ef55dd77cd..02c0cff457 100644
--- a/lib/chef/provider/remote_file/content.rb
+++ b/lib/chef/provider/remote_file/content.rb
@@ -1,7 +1,7 @@
#
# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Author:: Lamont Granquist (<lamont@opscode.com>)
-# Copyright:: Copyright (c) 2013 Opscode, Inc.
+# Copyright:: Copyright (c) 2013-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,9 +17,10 @@
# limitations under the License.
#
-require 'uri'
-require 'tempfile'
-require 'chef/file_content_management/content_base'
+require "uri"
+require "tempfile"
+require "chef/file_content_management/content_base"
+require "chef/mixin/uris"
class Chef
class Provider
@@ -28,6 +29,8 @@ class Chef
private
+ include Chef::Mixin::Uris
+
def file_for_provider
Chef::Log.debug("#{@new_resource} checking for changes")
@@ -45,10 +48,14 @@ class Chef
sources = sources.dup
source = sources.shift
begin
- uri = URI.parse(source)
+ uri = if Chef::Provider::RemoteFile::Fetcher.network_share?(source)
+ source
+ else
+ as_uri(source)
+ end
raw_file = grab_file_from_uri(uri)
rescue SocketError, Errno::ECONNREFUSED, Errno::ENOENT, Errno::EACCES, Timeout::Error, Net::HTTPServerException, Net::HTTPFatalError, Net::FTPError => e
- Chef::Log.warn("#{@new_resource} cannot be downloaded from #{source}: #{e.to_s}")
+ Chef::Log.warn("#{@new_resource} cannot be downloaded from #{source}: #{e}")
if source = sources.shift
Chef::Log.info("#{@new_resource} trying to download from another mirror")
retry
diff --git a/lib/chef/provider/remote_file/fetcher.rb b/lib/chef/provider/remote_file/fetcher.rb
index 249b29186f..53bfe9935c 100644
--- a/lib/chef/provider/remote_file/fetcher.rb
+++ b/lib/chef/provider/remote_file/fetcher.rb
@@ -23,15 +23,29 @@ class Chef
class Fetcher
def self.for_resource(uri, new_resource, current_resource)
- case uri.scheme
- when "http", "https"
- Chef::Provider::RemoteFile::HTTP.new(uri, new_resource, current_resource)
- when "ftp"
- Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource)
- when "file"
- Chef::Provider::RemoteFile::LocalFile.new(uri, new_resource, current_resource)
+ if network_share?(uri)
+ Chef::Provider::RemoteFile::NetworkFile.new(uri, new_resource, current_resource)
else
- raise ArgumentError, "Invalid uri, Only http(s), ftp, and file are currently supported"
+ case uri.scheme
+ when "http", "https"
+ Chef::Provider::RemoteFile::HTTP.new(uri, new_resource, current_resource)
+ when "ftp"
+ Chef::Provider::RemoteFile::FTP.new(uri, new_resource, current_resource)
+ when "file"
+ Chef::Provider::RemoteFile::LocalFile.new(uri, new_resource, current_resource)
+ else
+ raise ArgumentError, "Invalid uri, Only http(s), ftp, and file are currently supported"
+ end
+ end
+ end
+
+ # Windows network share: \\computername\share\file
+ def self.network_share?(source)
+ case source
+ when String
+ !!(%r{\A\\\\[A-Za-z0-9+\-\.]+} =~ source)
+ else
+ false
end
end
diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb
index 3f78286aa3..a81126f27f 100644
--- a/lib/chef/provider/remote_file/ftp.rb
+++ b/lib/chef/provider/remote_file/ftp.rb
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require 'uri'
-require 'tempfile'
-require 'net/ftp'
-require 'chef/provider/remote_file'
-require 'chef/file_content_management/tempfile'
+require "uri"
+require "tempfile"
+require "net/ftp"
+require "chef/provider/remote_file"
+require "chef/file_content_management/tempfile"
class Chef
class Provider
@@ -59,7 +59,7 @@ class Chef
if uri.userinfo
URI.unescape(uri.user)
else
- 'anonymous'
+ "anonymous"
end
end
@@ -94,11 +94,11 @@ class Chef
private
def with_proxy_env
- saved_socks_env = ENV['SOCKS_SERVER']
- ENV['SOCKS_SERVER'] = proxy_uri(@uri).to_s
+ saved_socks_env = ENV["SOCKS_SERVER"]
+ ENV["SOCKS_SERVER"] = proxy_uri(@uri).to_s
yield
ensure
- ENV['SOCKS_SERVER'] = saved_socks_env
+ ENV["SOCKS_SERVER"] = saved_socks_env
end
def with_connection
@@ -162,7 +162,7 @@ class Chef
end
def parse_path
- path = uri.path.sub(%r{\A/}, '%2F') # re-encode the beginning slash because uri library decodes it.
+ path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it.
directories = path.split(%r{/}, -1)
directories.each {|d|
d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
diff --git a/lib/chef/provider/remote_file/http.rb b/lib/chef/provider/remote_file/http.rb
index f17ab5a56d..218d7ef223 100644
--- a/lib/chef/provider/remote_file/http.rb
+++ b/lib/chef/provider/remote_file/http.rb
@@ -17,10 +17,10 @@
# limitations under the License.
#
-require 'chef/http/simple'
-require 'chef/digester'
-require 'chef/provider/remote_file'
-require 'chef/provider/remote_file/cache_control_data'
+require "chef/http/simple"
+require "chef/digester"
+require "chef/provider/remote_file"
+require "chef/provider/remote_file/cache_control_data"
class Chef
class Provider
@@ -87,11 +87,11 @@ class Chef
end
def last_modified_time_from(response)
- response['last_modified'] || response['date']
+ response["last_modified"] || response["date"]
end
def etag_from(response)
- response['etag']
+ response["etag"]
end
def http_client_opts
@@ -105,7 +105,7 @@ class Chef
# case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz,
# which is not what you wanted.
if uri.to_s =~ /gz$/
- Chef::Log.debug("turning gzip compression off due to filename ending in gz")
+ Chef::Log.debug("Turning gzip compression off due to filename ending in gz")
opts[:disable_gzip] = true
end
opts
diff --git a/lib/chef/provider/remote_file/local_file.rb b/lib/chef/provider/remote_file/local_file.rb
index e78311f2c3..2e99886a00 100644
--- a/lib/chef/provider/remote_file/local_file.rb
+++ b/lib/chef/provider/remote_file/local_file.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'uri'
-require 'tempfile'
-require 'chef/provider/remote_file'
+require "uri"
+require "tempfile"
+require "chef/provider/remote_file"
class Chef
class Provider
@@ -32,15 +32,21 @@ class Chef
@new_resource = new_resource
@uri = uri
end
-
+
# CHEF-4472: Remove the leading slash from windows paths that we receive from a file:// URI
- def fix_windows_path(path)
- path.gsub(/^\/([a-zA-Z]:)/,'\1')
+ def fix_windows_path(path)
+ path.gsub(/^\/([a-zA-Z]:)/,'\1')
+ end
+
+ def source_path
+ @source_path ||= begin
+ path = URI.unescape(uri.path)
+ Chef::Platform.windows? ? fix_windows_path(path) : path
+ end
end
# Fetches the file at uri, returning a Tempfile-like File handle
def fetch
- source_path = Chef::Platform.windows? ? fix_windows_path(uri.path) : uri.path
tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
Chef::Log.debug("#{new_resource} staging #{source_path} to #{tempfile.path}")
FileUtils.cp(source_path, tempfile.path)
diff --git a/lib/chef/provider/remote_file/network_file.rb b/lib/chef/provider/remote_file/network_file.rb
new file mode 100644
index 0000000000..7c066cb052
--- /dev/null
+++ b/lib/chef/provider/remote_file/network_file.rb
@@ -0,0 +1,48 @@
+#
+# Author:: Jesse Campbell (<hikeit@gmail.com>)
+# Copyright:: Copyright (c) 2013 Jesse Campbell
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "uri"
+require "tempfile"
+require "chef/provider/remote_file"
+
+class Chef
+ class Provider
+ class RemoteFile
+ class NetworkFile
+
+ attr_reader :new_resource
+
+ def initialize(source, new_resource, current_resource)
+ @new_resource = new_resource
+ @source = source
+ end
+
+ # Fetches the file on a network share, returning a Tempfile-like File handle
+ # windows only
+ def fetch
+ tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
+ Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}")
+ FileUtils.cp(@source, tempfile.path)
+ tempfile.close if tempfile
+ tempfile
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/route.rb b/lib/chef/provider/route.rb
index 72a5029a94..4abfd806b9 100644
--- a/lib/chef/provider/route.rb
+++ b/lib/chef/provider/route.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require 'chef/log'
-require 'chef/mixin/command'
-require 'chef/provider'
-require 'ipaddr'
+require "chef/log"
+require "chef/mixin/command"
+require "chef/provider"
+require "ipaddr"
class Chef::Provider::Route < Chef::Provider
include Chef::Mixin::Command
@@ -28,52 +28,52 @@ class Chef::Provider::Route < Chef::Provider
attr_accessor :is_running
- MASK = {'0.0.0.0' => '0',
- '128.0.0.0' => '1',
- '192.0.0.0' => '2',
- '224.0.0.0' => '3',
- '240.0.0.0' => '4',
- '248.0.0.0' => '5',
- '252.0.0.0' => '6',
- '254.0.0.0' => '7',
- '255.0.0.0' => '8',
- '255.128.0.0' => '9',
- '255.192.0.0' => '10',
- '255.224.0.0' => '11',
- '255.240.0.0' => '12',
- '255.248.0.0' => '13',
- '255.252.0.0' => '14',
- '255.254.0.0' => '15',
- '255.255.0.0' => '16',
- '255.255.128.0' => '17',
- '255.255.192.0' => '18',
- '255.255.224.0' => '19',
- '255.255.240.0' => '20',
- '255.255.248.0' => '21',
- '255.255.252.0' => '22',
- '255.255.254.0' => '23',
- '255.255.255.0' => '24',
- '255.255.255.128' => '25',
- '255.255.255.192' => '26',
- '255.255.255.224' => '27',
- '255.255.255.240' => '28',
- '255.255.255.248' => '29',
- '255.255.255.252' => '30',
- '255.255.255.254' => '31',
- '255.255.255.255' => '32' }
+ MASK = {"0.0.0.0" => "0",
+ "128.0.0.0" => "1",
+ "192.0.0.0" => "2",
+ "224.0.0.0" => "3",
+ "240.0.0.0" => "4",
+ "248.0.0.0" => "5",
+ "252.0.0.0" => "6",
+ "254.0.0.0" => "7",
+ "255.0.0.0" => "8",
+ "255.128.0.0" => "9",
+ "255.192.0.0" => "10",
+ "255.224.0.0" => "11",
+ "255.240.0.0" => "12",
+ "255.248.0.0" => "13",
+ "255.252.0.0" => "14",
+ "255.254.0.0" => "15",
+ "255.255.0.0" => "16",
+ "255.255.128.0" => "17",
+ "255.255.192.0" => "18",
+ "255.255.224.0" => "19",
+ "255.255.240.0" => "20",
+ "255.255.248.0" => "21",
+ "255.255.252.0" => "22",
+ "255.255.254.0" => "23",
+ "255.255.255.0" => "24",
+ "255.255.255.128" => "25",
+ "255.255.255.192" => "26",
+ "255.255.255.224" => "27",
+ "255.255.255.240" => "28",
+ "255.255.255.248" => "29",
+ "255.255.255.252" => "30",
+ "255.255.255.254" => "31",
+ "255.255.255.255" => "32" }
def hex2ip(hex_data)
# Cleanup hex data
- hex_ip = hex_data.to_s.downcase.gsub(/[^0-9a-f]/, '')
+ hex_ip = hex_data.to_s.downcase.gsub(/[^0-9a-f]/, "")
# Check hex data format (IP is a 32bit integer, so should be 8 chars long)
return nil if hex_ip.length != hex_data.length || hex_ip.length != 8
# Extract octets from hex data
- octets = hex_ip.scan(/../).reverse.collect { |octet| [octet].pack('H2').unpack("C").first }
+ octets = hex_ip.scan(/../).reverse.collect { |octet| [octet].pack("H2").unpack("C").first }
# Validate IP
- ip = octets.join('.')
+ ip = octets.join(".")
begin
IPAddr.new(ip, Socket::AF_INET).to_s
rescue ArgumentError
@@ -197,7 +197,7 @@ class Chef::Provider::Route < Chef::Provider
end
def generate_command(action)
- common_route_items = ''
+ common_route_items = ""
common_route_items << "/#{MASK[@new_resource.netmask.to_s]}" if @new_resource.netmask
common_route_items << " via #{@new_resource.gateway} " if @new_resource.gateway
@@ -215,7 +215,7 @@ class Chef::Provider::Route < Chef::Provider
end
def config_file_contents(action, options={})
- content = ''
+ content = ""
case action
when :add
content << "#{options[:target]}"
diff --git a/lib/chef/provider/script.rb b/lib/chef/provider/script.rb
index e8b5235b7a..d95962a15b 100644
--- a/lib/chef/provider/script.rb
+++ b/lib/chef/provider/script.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'tempfile'
-require 'chef/provider/execute'
-require 'forwardable'
+require "tempfile"
+require "chef/provider/execute"
+require "forwardable"
class Chef
class Provider
@@ -27,6 +27,7 @@ class Chef
provides :bash
provides :csh
+ provides :ksh
provides :perl
provides :python
provides :ruby
diff --git a/lib/chef/provider/service.rb b/lib/chef/provider/service.rb
index 75da2ddb31..6e1d0b4064 100644
--- a/lib/chef/provider/service.rb
+++ b/lib/chef/provider/service.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.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,8 +16,8 @@
# limitations under the License.
#
-require 'chef/mixin/command'
-require 'chef/provider'
+require "chef/mixin/command"
+require "chef/provider"
class Chef
class Provider
@@ -25,6 +25,10 @@ class Chef
include Chef::Mixin::Command
+ def supports
+ @supports ||= new_resource.supports.dup
+ end
+
def initialize(new_resource, run_context)
super
@enabled = nil
@@ -34,24 +38,33 @@ class Chef
true
end
- def load_new_resource_state
- # If the user didn't specify a change in enabled state,
- # it will be the same as the old resource
- if ( @new_resource.enabled.nil? )
- @new_resource.enabled(@current_resource.enabled)
- end
- if ( @new_resource.running.nil? )
- @new_resource.running(@current_resource.running)
- end
- end
+ def load_current_resource
+ supports[:status] = false if supports[:status].nil?
+ supports[:reload] = false if supports[:reload].nil?
+ supports[:restart] = false if supports[:restart].nil?
+ end
+
+ # the new_resource#enabled and #running variables are not user input, but when we
+ # do (e.g.) action_enable we want to set new_resource.enabled so that the comparison
+ # between desired and current state produces the correct change in reporting.
+ # XXX?: the #nil? check below will likely fail if this is a cloned resource or if
+ # we just run multiple actions.
+ def load_new_resource_state
+ if ( @new_resource.enabled.nil? )
+ @new_resource.enabled(@current_resource.enabled)
+ end
+ if ( @new_resource.running.nil? )
+ @new_resource.running(@current_resource.running)
+ end
+ end
def shared_resource_requirements
end
def define_resource_requirements
requirements.assert(:reload) do |a|
- a.assertion { @new_resource.supports[:reload] || @new_resource.reload_command }
- a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload"
+ a.assertion { supports[:reload] || @new_resource.reload_command }
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self} does not support :reload"
# if a service is not declared to support reload, that won't
# typically change during the course of a run - so no whyrun
# alternative here.
@@ -130,27 +143,27 @@ class Chef
end
def enable_service
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :enable"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :enable"
end
def disable_service
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :disable"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :disable"
end
def start_service
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :start"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :start"
end
def stop_service
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :stop"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :stop"
end
def restart_service
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :restart"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :restart"
end
def reload_service
- raise Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload"
+ raise Chef::Exceptions::UnsupportedAction, "#{self} does not support :reload"
end
protected
@@ -168,6 +181,32 @@ class Chef
@new_resource.respond_to?(method_name) &&
!!@new_resource.send(method_name)
end
+
+ module ServicePriorityInit
+
+ #
+ # Platform-specific versions
+ #
+
+ #
+ # Linux
+ #
+
+ require "chef/chef_class"
+ require "chef/provider/service/systemd"
+ require "chef/provider/service/insserv"
+ require "chef/provider/service/redhat"
+ require "chef/provider/service/arch"
+ require "chef/provider/service/gentoo"
+ require "chef/provider/service/upstart"
+ require "chef/provider/service/debian"
+ require "chef/provider/service/invokercd"
+
+ Chef.set_provider_priority_array :service, [ Systemd, Arch ], platform_family: "arch"
+ Chef.set_provider_priority_array :service, [ Systemd, Gentoo ], platform_family: "gentoo"
+ Chef.set_provider_priority_array :service, [ Systemd, Upstart, Insserv, Debian, Invokercd ], platform_family: "debian"
+ Chef.set_provider_priority_array :service, [ Systemd, Insserv, Redhat ], platform_family: %w{rhel fedora suse}
+ end
end
end
end
diff --git a/lib/chef/provider/service/aix.rb b/lib/chef/provider/service/aix.rb
index 0aef62c62e..1968f8f3de 100644
--- a/lib/chef/provider/service/aix.rb
+++ b/lib/chef/provider/service/aix.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/provider/service'
+require "chef/provider/service"
class Chef
class Provider
@@ -91,15 +91,18 @@ class Chef
protected
def determine_current_status!
- Chef::Log.debug "#{@new_resource} using lssrc to check the status "
+ Chef::Log.debug "#{@new_resource} using lssrc to check the status"
begin
- services = shell_out!("lssrc -a | grep -w #{@new_resource.service_name}").stdout.split("\n")
- is_resource_group?(services)
-
- if services.length == 1 && services[0].split(' ').last == "active"
- @current_resource.running true
- else
+ if is_resource_group?
+ # Groups as a whole have no notion of whether they're running
@current_resource.running false
+ else
+ service = shell_out!("lssrc -s #{@new_resource.service_name}").stdout
+ if service.split(" ").last == "active"
+ @current_resource.running true
+ else
+ @current_resource.running false
+ end
end
Chef::Log.debug "#{@new_resource} running: #{@current_resource.running}"
# ShellOut sometimes throws different types of Exceptions than ShellCommandFailed.
@@ -112,11 +115,9 @@ class Chef
end
end
- def is_resource_group? (services)
- if services.length > 1
- Chef::Log.debug("#{@new_resource.service_name} is a group")
- @is_resource_group = true
- elsif services[0].split(' ')[1] == @new_resource.service_name
+ def is_resource_group?
+ so = shell_out("lssrc -g #{@new_resource.service_name}")
+ if so.exitstatus == 0
Chef::Log.debug("#{@new_resource.service_name} is a group")
@is_resource_group = true
end
diff --git a/lib/chef/provider/service/aixinit.rb b/lib/chef/provider/service/aixinit.rb
index 19beac79f0..66d85984fa 100644
--- a/lib/chef/provider/service/aixinit.rb
+++ b/lib/chef/provider/service/aixinit.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
+require "chef/provider/service/init"
class Chef
class Provider
@@ -60,14 +60,14 @@ class Chef
Dir.glob(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).each { |f| ::File.delete(f)}
if @new_resource.priority.is_a? Integer
- create_symlink(2, 'S', @new_resource.priority)
+ create_symlink(2, "S", @new_resource.priority)
elsif @new_resource.priority.is_a? Hash
@new_resource.priority.each do |level,o|
- create_symlink(level,(o[0] == :start ? 'S' : 'K'),o[1])
+ create_symlink(level,(o[0] == :start ? "S" : "K"),o[1])
end
else
- create_symlink(2, 'S', '')
+ create_symlink(2, "S", "")
end
end
@@ -75,13 +75,13 @@ class Chef
Dir.glob(["/etc/rc.d/rc2.d/[SK][0-9][0-9]#{@new_resource.service_name}", "/etc/rc.d/rc2.d/[SK]#{@new_resource.service_name}"]).each { |f| ::File.delete(f) }
if @new_resource.priority.is_a? Integer
- create_symlink(2, 'K',100 - @new_resource.priority)
+ create_symlink(2, "K",100 - @new_resource.priority)
elsif @new_resource.priority.is_a? Hash
@new_resource.priority.each do |level,o|
- create_symlink(level, 'K', 100 - o[1]) if o[0] == :stop
+ create_symlink(level, "K", 100 - o[1]) if o[0] == :stop
end
else
- create_symlink(2, 'K', '')
+ create_symlink(2, "K", "")
end
end
@@ -98,7 +98,7 @@ class Chef
files.each do |file|
if (RC_D_SCRIPT_NAME =~ file)
- priority[2] = [($1 == "S" ? :start : :stop), ($2.empty? ? '' : $2.to_i)]
+ priority[2] = [($1 == "S" ? :start : :stop), ($2.empty? ? "" : $2.to_i)]
if $1 == "S"
is_enabled = true
end
diff --git a/lib/chef/provider/service/arch.rb b/lib/chef/provider/service/arch.rb
index e7fbcc820c..3e7b9fcaca 100644
--- a/lib/chef/provider/service/arch.rb
+++ b/lib/chef/provider/service/arch.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
+require "chef/provider/service/init"
class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
@@ -50,7 +50,7 @@ class Chef::Provider::Service::Arch < Chef::Provider::Service::Init
def daemons
entries = []
if ::File.read("/etc/rc.conf").match(/DAEMONS=\((.*)\)/m)
- entries += $1.gsub(/\\?[\r\n]/, ' ').gsub(/# *[^ ]+/,' ').split(' ') if $1.length > 0
+ entries += $1.gsub(/\\?[\r\n]/, " ").gsub(/# *[^ ]+/," ").split(" ") if $1.length > 0
end
yield(entries) if block_given?
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index 01505924cb..559ad48418 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -16,21 +16,19 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
+require "chef/provider/service/init"
class Chef
class Provider
class Service
class Debian < Chef::Provider::Service::Init
+ provides :service, platform_family: "debian" do |node|
+ Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian)
+ end
+
UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i
UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i
- provides :service, platform_family: "debian"
-
- def self.provides?(node, resource)
- super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:debian)
- end
-
def self.supports?(resource, action)
Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
end
@@ -111,7 +109,7 @@ class Chef
priority.each { |runlevel, arguments|
Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
# if we are in a update-rc.d default startup runlevel && we start in this runlevel
- if %w[ 1 2 3 4 5 S ].include?(runlevel) && arguments[0] == :start
+ if %w{ 1 2 3 4 5 S }.include?(runlevel) && arguments[0] == :start
enabled = true
end
}
diff --git a/lib/chef/provider/service/freebsd.rb b/lib/chef/provider/service/freebsd.rb
index 9204e3ef92..4aa9eb09f6 100644
--- a/lib/chef/provider/service/freebsd.rb
+++ b/lib/chef/provider/service/freebsd.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/resource/service'
-require 'chef/provider/service/init'
-require 'chef/mixin/command'
+require "chef/resource/service"
+require "chef/provider/service/init"
+require "chef/mixin/command"
class Chef
class Provider
@@ -99,7 +99,7 @@ class Chef
def restart_service
if new_resource.restart_command
super
- elsif new_resource.supports[:restart]
+ elsif supports[:restart]
shell_out_with_systems_locale!("#{init_command} fastrestart")
else
stop_service
@@ -119,11 +119,11 @@ class Chef
private
def read_rc_conf
- ::File.open("/etc/rc.conf", 'r') { |file| file.readlines }
+ ::File.open("/etc/rc.conf", "r") { |file| file.readlines }
end
def write_rc_conf(lines)
- ::File.open("/etc/rc.conf", 'w') do |file|
+ ::File.open("/etc/rc.conf", "w") do |file|
lines.each { |line| file.puts(line) }
end
end
@@ -147,7 +147,7 @@ class Chef
# some scripts support multiple instances through symlinks such as openvpn.
# We should get the service name from rcvar.
Chef::Log.debug("name=\"service\" not found at #{init_command}. falling back to rcvar")
- sn = shell_out!("#{init_command} rcvar").stdout[/(\w+_enable)=/, 1]
+ shell_out!("#{init_command} rcvar").stdout[/(\w+_enable)=/, 1]
else
# for why-run mode when the rcd_script is not there yet
new_resource.service_name
diff --git a/lib/chef/provider/service/gentoo.rb b/lib/chef/provider/service/gentoo.rb
index 3dab920f06..28fd43a709 100644
--- a/lib/chef/provider/service/gentoo.rb
+++ b/lib/chef/provider/service/gentoo.rb
@@ -1,7 +1,7 @@
#
# Author:: Lee Jensen (<ljensen@engineyard.com>)
# Author:: AJ Christensen (<aj@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");
@@ -17,18 +17,18 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
-require 'chef/mixin/command'
-require 'chef/util/path_helper'
+require "chef/provider/service/init"
+require "chef/mixin/command"
+require "chef/util/path_helper"
class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init
provides :service, platform_family: "gentoo"
def load_current_resource
+ supports[:status] = true if supports[:status].nil?
+ supports[:restart] = true if supports[:restart].nil?
- @new_resource.supports[:status] = true
- @new_resource.supports[:restart] = true
@found_script = false
super
diff --git a/lib/chef/provider/service/init.rb b/lib/chef/provider/service/init.rb
index 0a219a69e1..90c0ec2b34 100644
--- a/lib/chef/provider/service/init.rb
+++ b/lib/chef/provider/service/init.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.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,8 +16,9 @@
# limitations under the License.
#
-require 'chef/provider/service/simple'
-require 'chef/mixin/command'
+require "chef/provider/service/simple"
+require "chef/mixin/command"
+require "chef/platform/service_helpers"
class Chef
class Provider
@@ -71,7 +72,7 @@ class Chef
def restart_service
if @new_resource.restart_command
super
- elsif @new_resource.supports[:restart]
+ elsif supports[:restart]
shell_out_with_systems_locale!("#{default_init_command} restart")
else
stop_service
@@ -83,7 +84,7 @@ class Chef
def reload_service
if @new_resource.reload_command
super
- elsif @new_resource.supports[:reload]
+ elsif supports[:reload]
shell_out_with_systems_locale!("#{default_init_command} reload")
end
end
diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb
index 31965a4bc6..1c4d294053 100644
--- a/lib/chef/provider/service/insserv.rb
+++ b/lib/chef/provider/service/insserv.rb
@@ -16,18 +16,16 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
-require 'chef/util/path_helper'
+require "chef/provider/service/init"
+require "chef/util/path_helper"
class Chef
class Provider
class Service
class Insserv < Chef::Provider::Service::Init
- provides :service, os: "linux"
-
- def self.provides?(node, resource)
- super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv)
+ provides :service, platform_family: %w{debian rhel fedora suse} do |node|
+ Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv)
end
def self.supports?(resource, action)
diff --git a/lib/chef/provider/service/invokercd.rb b/lib/chef/provider/service/invokercd.rb
index 5ff24e0dbb..a48e2ac815 100644
--- a/lib/chef/provider/service/invokercd.rb
+++ b/lib/chef/provider/service/invokercd.rb
@@ -16,17 +16,15 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
+require "chef/provider/service/init"
class Chef
class Provider
class Service
class Invokercd < Chef::Provider::Service::Init
- provides :service, platform_family: "debian"
-
- def self.provides?(node, resource)
- super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd)
+ provides :service, platform_family: "debian", override: true do |node|
+ Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd)
end
def self.supports?(resource, action)
diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb
index df5be54fda..b939f78b03 100644
--- a/lib/chef/provider/service/macosx.rb
+++ b/lib/chef/provider/service/macosx.rb
@@ -16,16 +16,19 @@
# limitations under the License.
#
-require 'rexml/document'
-require 'chef/resource/service'
-require 'chef/provider/service/simple'
-require 'chef/util/path_helper'
+require "etc"
+require "rexml/document"
+require "chef/resource/service"
+require "chef/resource/macosx_service"
+require "chef/provider/service/simple"
+require "chef/util/path_helper"
class Chef
class Provider
class Service
class Macosx < Chef::Provider::Service::Simple
+ provides :macosx_service, os: "darwin"
provides :service, os: "darwin"
def self.gather_plist_dirs
@@ -33,27 +36,45 @@ class Chef
/Library/LaunchDaemons
/System/Library/LaunchAgents
/System/Library/LaunchDaemons }
- Chef::Util::PathHelper.home('Library', 'LaunchAgents') { |p| locations << p }
+ Chef::Util::PathHelper.home("Library", "LaunchAgents") { |p| locations << p }
locations
end
PLIST_DIRS = gather_plist_dirs
+ def this_version_or_newer?(this_version)
+ Gem::Version.new(node["platform_version"]) >= Gem::Version.new(this_version)
+ end
+
def load_current_resource
- @current_resource = Chef::Resource::Service.new(@new_resource.name)
+ @current_resource = Chef::Resource::MacosxService.new(@new_resource.name)
@current_resource.service_name(@new_resource.service_name)
@plist_size = 0
- @plist = find_service_plist
+ @plist = @new_resource.plist ? @new_resource.plist : find_service_plist
@service_label = find_service_label
+ # LauchAgents should be loaded as the console user.
+ @console_user = @plist ? @plist.include?("LaunchAgents") : false
+ @session_type = @new_resource.session_type
+
+ if @console_user
+ @console_user = Etc.getlogin
+ Chef::Log.debug("#{new_resource} console_user: '#{@console_user}'")
+ cmd = "su "
+ param = this_version_or_newer?("10.10") ? "" : "-l "
+ @base_user_cmd = cmd + param + "#{@console_user} -c"
+ # Default LauchAgent session should be Aqua
+ @session_type = "Aqua" if @session_type.nil?
+ end
+
+ Chef::Log.debug("#{new_resource} Plist: '#{@plist}' service_label: '#{@service_label}'")
set_service_status
@current_resource
end
def define_resource_requirements
- #super
requirements.assert(:reload) do |a|
- a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} does not support :reload"
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self} does not support :reload"
end
requirements.assert(:all_actions) do |a|
@@ -61,6 +82,12 @@ class Chef
a.failure_message Chef::Exceptions::Service, "Several plist files match service name. Please use full service name."
end
+ requirements.assert(:all_actions) do |a|
+ a.assertion {::File.exists?(@plist.to_s) }
+ a.failure_message Chef::Exceptions::Service,
+ "Could not find plist for #{@new_resource}"
+ end
+
requirements.assert(:enable, :disable) do |a|
a.assertion { !@service_label.to_s.empty? }
a.failure_message Chef::Exceptions::Service,
@@ -69,7 +96,7 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { @plist_size > 0 }
- # No failrue here in original code - so we also will not
+ # No failure here in original code - so we also will not
# fail. Instead warn that the service is potentially missing
a.whyrun "Assuming that the service would have been previously installed and is currently disabled." do
@current_resource.enabled(false)
@@ -85,7 +112,7 @@ class Chef
if @new_resource.start_command
super
else
- shell_out_with_systems_locale!("launchctl load -w '#{@plist}'", :user => @owner_uid, :group => @owner_gid)
+ load_service
end
end
end
@@ -97,7 +124,7 @@ class Chef
if @new_resource.stop_command
super
else
- shell_out_with_systems_locale!("launchctl unload '#{@plist}'", :user => @owner_uid, :group => @owner_gid)
+ unload_service
end
end
end
@@ -106,9 +133,9 @@ class Chef
if @new_resource.restart_command
super
else
- stop_service
+ unload_service
sleep 1
- start_service
+ load_service
end
end
@@ -121,10 +148,7 @@ class Chef
if @current_resource.enabled
Chef::Log.debug("#{@new_resource} already enabled, not enabling")
else
- shell_out!(
- "launchctl load -w '#{@plist}'",
- :user => @owner_uid, :group => @owner_gid
- )
+ load_service
end
end
@@ -132,38 +156,49 @@ class Chef
unless @current_resource.enabled
Chef::Log.debug("#{@new_resource} not enabled, not disabling")
else
- shell_out!(
- "launchctl unload -w '#{@plist}'",
- :user => @owner_uid, :group => @owner_gid
- )
+ unload_service
+ end
+ end
+
+ def load_service
+ session = @session_type ? "-S #{@session_type} " : ""
+ cmd = "launchctl load -w " + session + @plist
+ shell_out_as_user(cmd)
+ end
+
+ def unload_service
+ cmd = "launchctl unload -w " + @plist
+ shell_out_as_user(cmd)
+ end
+
+ def shell_out_as_user(cmd)
+ if @console_user
+ shell_out_with_systems_locale("#{@base_user_cmd} '#{cmd}'")
+ else
+ shell_out_with_systems_locale(cmd)
+
end
end
def set_service_status
return if @plist == nil or @service_label.to_s.empty?
- cmd = shell_out(
- "launchctl list #{@service_label}",
- :user => @owner_uid, :group => @owner_gid
- )
+ cmd = "launchctl list #{@service_label}"
+ res = shell_out_as_user(cmd)
- if cmd.exitstatus == 0
+ if res.exitstatus == 0
@current_resource.enabled(true)
else
@current_resource.enabled(false)
end
if @current_resource.enabled
- @owner_uid = ::File.stat(@plist).uid
- @owner_gid = ::File.stat(@plist).gid
-
- shell_out!(
- "launchctl list", :user => @owner_uid, :group => @owner_gid
- ).stdout.each_line do |line|
- case line
- when /(\d+|-)\s+(?:\d+|-)\s+(.*\.?)#{@service_label}/
+ res.stdout.each_line do |line|
+ case line.downcase
+ when /\s+\"pid\"\s+=\s+(\d+).*/
pid = $1
@current_resource.running(!pid.to_i.zero?)
+ Chef::Log.debug("Current PID for #{@service_label} is #{pid}")
end
end
else
@@ -171,13 +206,16 @@ class Chef
end
end
- private
+ private
def find_service_label
# CHEF-5223 "you can't glob for a file that hasn't been converged
# onto the node yet."
return nil if @plist.nil?
+ # Plist must exist by this point
+ raise Chef::Exceptions::FileNotFound, "Cannot find #{@plist}!" unless ::File.exists?(@plist)
+
# Most services have the same internal label as the name of the
# plist file. However, there is no rule saying that *has* to be
# the case, and some core services (notably, ssh) do not follow
@@ -185,7 +223,9 @@ class Chef
# plist files can come in XML or Binary formats. this command
# will make sure we get XML every time.
- plist_xml = shell_out!("plutil -convert xml1 -o - #{@plist}").stdout
+ plist_xml = shell_out_with_systems_locale!(
+ "plutil -convert xml1 -o - #{@plist}"
+ ).stdout
plist_doc = REXML::Document.new(plist_xml)
plist_doc.elements[
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
index d509ee10ff..10d7b21953 100644
--- a/lib/chef/provider/service/openbsd.rb
+++ b/lib/chef/provider/service/openbsd.rb
@@ -16,35 +16,36 @@
# limitations under the License.
#
-require 'chef/mixin/command'
-require 'chef/mixin/shell_out'
-require 'chef/provider/service/init'
-require 'chef/resource/service'
+require "chef/mixin/command"
+require "chef/mixin/shell_out"
+require "chef/provider/service/init"
+require "chef/resource/service"
class Chef
class Provider
class Service
class Openbsd < Chef::Provider::Service::Init
- provides :service, os: [ "openbsd" ]
+ provides :service, os: "openbsd"
include Chef::Mixin::ShellOut
attr_reader :init_command, :rc_conf, :rc_conf_local, :enabled_state_found
- RC_CONF_PATH = '/etc/rc.conf'
- RC_CONF_LOCAL_PATH = '/etc/rc.conf.local'
+ RC_CONF_PATH = "/etc/rc.conf"
+ RC_CONF_LOCAL_PATH = "/etc/rc.conf.local"
def initialize(new_resource, run_context)
super
- @rc_conf = ::File.read(RC_CONF_PATH) rescue ''
- @rc_conf_local = ::File.read(RC_CONF_LOCAL_PATH) rescue ''
+ @rc_conf = ::File.read(RC_CONF_PATH) rescue ""
+ @rc_conf_local = ::File.read(RC_CONF_LOCAL_PATH) rescue ""
@init_command = ::File.exist?(rcd_script_path) ? rcd_script_path : nil
- new_resource.supports[:status] = true
new_resource.status_command("#{default_init_command} check")
end
def load_current_resource
+ supports[:status] = true if supports[:status].nil?
+
@current_resource = Chef::Resource::Service.new(new_resource.name)
current_resource.service_name(new_resource.service_name)
@@ -81,7 +82,7 @@ class Chef
if !is_enabled?
if is_builtin?
if is_enabled_by_default?
- update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, '')
+ update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, "")
else
# add line with blank string, which means enable
update_rcl rc_conf_local + "\n" + "#{builtin_service_enable_variable_name}=\"\"\n"
@@ -89,7 +90,7 @@ class Chef
else
# add to pkg_scripts, most recent addition goes last
old_services_list = rc_conf_local.match(/^pkg_scripts="(.*)"/)
- old_services_list = old_services_list ? old_services_list[1].split(' ') : []
+ old_services_list = old_services_list ? old_services_list[1].split(" ") : []
new_services_list = old_services_list + [new_resource.service_name]
if rc_conf_local.match(/^pkg_scripts="(.*)"/)
new_rcl = rc_conf_local.sub(/^pkg_scripts="(.*)"/, "pkg_scripts=\"#{new_services_list.join(' ')}\"")
@@ -109,12 +110,12 @@ class Chef
update_rcl rc_conf_local + "\n" + "#{builtin_service_enable_variable_name}=\"NO\"\n"
else
# remove line to disable
- update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, '')
+ update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, "")
end
else
# remove from pkg_scripts
old_list = rc_conf_local.match(/^pkg_scripts="(.*)"/)
- old_list = old_list ? old_list[1].split(' ') : []
+ old_list = old_list ? old_list[1].split(" ") : []
new_list = old_list - [new_resource.service_name]
update_rcl rc_conf_local.sub(/^pkg_scripts="(.*)"/, pkg_scripts="#{new_list.join(' ')}")
end
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index 850953125e..9cc4258b70 100644
--- a/lib/chef/provider/service/redhat.rb
+++ b/lib/chef/provider/service/redhat.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.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,31 +16,39 @@
# limitations under the License.
#
-require 'chef/provider/service/init'
+require "chef/provider/service/init"
class Chef
class Provider
class Service
class Redhat < Chef::Provider::Service::Init
- CHKCONFIG_ON = /\d:on/
- CHKCONFIG_MISSING = /No such/
-
- provides :service, platform_family: [ "rhel", "fedora", "suse" ]
+ # @api private
+ attr_accessor :service_missing
+ # @api private
+ attr_accessor :current_run_levels
- def self.provides?(node, resource)
- super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat)
+ provides :service, platform_family: %w{rhel fedora suse} do |node|
+ Chef::Platform::ServiceHelpers.service_resource_providers.include?(:redhat)
end
+ CHKCONFIG_ON = /\d:on/
+ CHKCONFIG_MISSING = /No such/
+
def self.supports?(resource, action)
Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:initd)
end
def initialize(new_resource, run_context)
super
- @init_command = "/sbin/service #{@new_resource.service_name}"
- @new_resource.supports[:status] = true
+ @init_command = "/sbin/service #{new_resource.service_name}"
@service_missing = false
+ @current_run_levels = []
+ end
+
+ # @api private
+ def run_levels
+ new_resource.run_levels
end
def define_resource_requirements
@@ -49,34 +57,62 @@ class Chef
requirements.assert(:all_actions) do |a|
chkconfig_file = "/sbin/chkconfig"
a.assertion { ::File.exists? chkconfig_file }
- a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} does not exist!"
+ a.failure_message Chef::Exceptions::Service, "#{chkconfig_file} dbleoes not exist!"
end
requirements.assert(:start, :enable, :reload, :restart) do |a|
- a.assertion { !@service_missing }
- a.failure_message Chef::Exceptions::Service, "#{@new_resource}: unable to locate the init.d script!"
+ a.assertion do
+ custom_command_for_action?(action) || !@service_missing
+ end
+ a.failure_message Chef::Exceptions::Service, "#{new_resource}: No custom command for #{action} specified and unable to locate the init.d script!"
a.whyrun "Assuming service would be disabled. The init script is not presently installed."
end
end
def load_current_resource
+ supports[:status] = true if supports[:status].nil?
+
super
if ::File.exists?("/sbin/chkconfig")
- chkconfig = shell_out!("/sbin/chkconfig --list #{@current_resource.service_name}", :returns => [0,1])
- @current_resource.enabled(!!(chkconfig.stdout =~ CHKCONFIG_ON))
+ chkconfig = shell_out!("/sbin/chkconfig --list #{current_resource.service_name}", :returns => [0,1])
+ unless run_levels.nil? or run_levels.empty?
+ all_levels_match = true
+ chkconfig.stdout.split(/\s+/)[1..-1].each do |level|
+ index = level.split(":").first
+ status = level.split(":").last
+ if level =~ CHKCONFIG_ON
+ @current_run_levels << index.to_i
+ all_levels_match = false unless run_levels.include?(index.to_i)
+ else
+ all_levels_match = false if run_levels.include?(index.to_i)
+ end
+ end
+ current_resource.enabled(all_levels_match)
+ else
+ current_resource.enabled(!!(chkconfig.stdout =~ CHKCONFIG_ON))
+ end
@service_missing = !!(chkconfig.stderr =~ CHKCONFIG_MISSING)
end
- @current_resource
+ current_resource
+ end
+
+ # @api private
+ def levels
+ (run_levels.nil? or run_levels.empty?) ? "" : "--level #{run_levels.join('')} "
end
def enable_service()
- shell_out! "/sbin/chkconfig #{@new_resource.service_name} on"
+ unless run_levels.nil? or run_levels.empty?
+ disable_levels = current_run_levels - run_levels
+ shell_out! "/sbin/chkconfig --level #{disable_levels.join('')} #{new_resource.service_name} off" unless disable_levels.empty?
+ end
+ shell_out! "/sbin/chkconfig #{levels}#{new_resource.service_name} on"
end
def disable_service()
- shell_out! "/sbin/chkconfig #{@new_resource.service_name} off"
+ shell_out! "/sbin/chkconfig #{levels}#{new_resource.service_name} off"
end
end
end
diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb
index ee403ee163..a096c9dfc0 100644
--- a/lib/chef/provider/service/simple.rb
+++ b/lib/chef/provider/service/simple.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/provider/service'
-require 'chef/resource/service'
-require 'chef/mixin/command'
+require "chef/provider/service"
+require "chef/resource/service"
+require "chef/mixin/command"
class Chef
class Provider
@@ -58,25 +58,25 @@ class Chef
shared_resource_requirements
requirements.assert(:start) do |a|
a.assertion { @new_resource.start_command }
- a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires that start_command be set"
+ a.failure_message Chef::Exceptions::Service, "#{self} requires that start_command be set"
end
requirements.assert(:stop) do |a|
a.assertion { @new_resource.stop_command }
- a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires that stop_command be set"
+ a.failure_message Chef::Exceptions::Service, "#{self} requires that stop_command be set"
end
requirements.assert(:restart) do |a|
a.assertion { @new_resource.restart_command || ( @new_resource.start_command && @new_resource.stop_command ) }
- a.failure_message Chef::Exceptions::Service, "#{self.to_s} requires a restart_command or both start_command and stop_command be set in order to perform a restart"
+ a.failure_message Chef::Exceptions::Service, "#{self} requires a restart_command or both start_command and stop_command be set in order to perform a restart"
end
requirements.assert(:reload) do |a|
a.assertion { @new_resource.reload_command }
- a.failure_message Chef::Exceptions::UnsupportedAction, "#{self.to_s} requires a reload_command be set in order to perform a reload"
+ a.failure_message Chef::Exceptions::UnsupportedAction, "#{self} requires a reload_command be set in order to perform a reload"
end
requirements.assert(:all_actions) do |a|
- a.assertion { @new_resource.status_command or @new_resource.supports[:status] or
+ a.assertion { @new_resource.status_command or supports[:status] or
(!ps_cmd.nil? and !ps_cmd.empty?) }
a.failure_message Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this node's 'command.ps' attribute"
end
@@ -127,7 +127,7 @@ class Chef
nil
end
- elsif @new_resource.supports[:status]
+ elsif supports[:status]
Chef::Log.debug("#{@new_resource} supports status, running")
begin
if shell_out("#{default_init_command} status").exitstatus == 0
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
index eaea6bb1ab..c43a25258c 100644
--- a/lib/chef/provider/service/solaris.rb
+++ b/lib/chef/provider/service/solaris.rb
@@ -16,9 +16,9 @@
# limitations under the License.
#
-require 'chef/provider/service'
-require 'chef/resource/service'
-require 'chef/mixin/command'
+require "chef/provider/service"
+require "chef/resource/service"
+require "chef/mixin/command"
class Chef
class Provider
@@ -30,35 +30,39 @@ class Chef
def initialize(new_resource, run_context=nil)
super
- @init_command = "/usr/sbin/svcadm"
- @status_command = "/bin/svcs -l"
+ @init_command = "/usr/sbin/svcadm"
+ @status_command = "/bin/svcs"
@maintenace = false
end
def load_current_resource
@current_resource = Chef::Resource::Service.new(@new_resource.name)
@current_resource.service_name(@new_resource.service_name)
- unless ::File.exists? "/bin/svcs"
- raise Chef::Exceptions::Service, "/bin/svcs does not exist!"
+
+ [@init_command, @status_command].each do |cmd|
+ unless ::File.executable? cmd then
+ raise Chef::Exceptions::Service, "#{cmd} not executable!"
+ end
end
@status = service_status.enabled
+
@current_resource
end
def enable_service
- shell_out!("#{default_init_command} clear #{@new_resource.service_name}") if @maintenance
- shell_out!("#{default_init_command} enable -s #{@new_resource.service_name}")
+ shell_out!(default_init_command, "clear", @new_resource.service_name) if @maintenance
+ shell_out!(default_init_command, "enable", "-s", @new_resource.service_name)
end
def disable_service
- shell_out!("#{default_init_command} disable -s #{@new_resource.service_name}")
+ shell_out!(default_init_command, "disable", "-s", @new_resource.service_name)
end
alias_method :stop_service, :disable_service
alias_method :start_service, :enable_service
def reload_service
- shell_out_with_systems_locale!("#{default_init_command} refresh #{@new_resource.service_name}")
+ shell_out!(default_init_command, "refresh", @new_resource.service_name)
end
def restart_service
@@ -68,16 +72,38 @@ class Chef
end
def service_status
- status = shell_out!("#{@status_command} #{@current_resource.service_name}", :returns => [0, 1])
- status.stdout.each_line do |line|
- case line
- when /state\s+online/
- @current_resource.enabled(true)
- @current_resource.running(true)
- when /state\s+maintenance/
- @maintenance = true
- end
+ cmd = shell_out!(@status_command, "-l", @current_resource.service_name, :returns => [0, 1])
+ # Example output
+ # $ svcs -l rsyslog
+ # fmri svc:/application/rsyslog:default
+ # name rsyslog logging utility
+ # enabled true
+ # state online
+ # next_state none
+ # state_time April 2, 2015 04:25:19 PM EDT
+ # logfile /var/svc/log/application-rsyslog:default.log
+ # restarter svc:/system/svc/restarter:default
+ # contract_id 1115271
+ # dependency require_all/error svc:/milestone/multi-user:default (online)
+ # $
+
+ # load output into hash
+ status = {}
+ cmd.stdout.each_line do |line|
+ key, value = line.strip.split(/\s+/, 2)
+ status[key] = value
+ end
+
+ # check service state
+ @maintenance = false
+ case status["state"]
+ when "online"
+ @current_resource.enabled(true)
+ @current_resource.running(true)
+ when "maintenance"
+ @maintenance = true
end
+
unless @current_resource.enabled
@current_resource.enabled(false)
@current_resource.running(false)
diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb
index 9085ffde2e..bf4b324dce 100644
--- a/lib/chef/provider/service/systemd.rb
+++ b/lib/chef/provider/service/systemd.rb
@@ -16,22 +16,20 @@
# limitations under the License.
#
-require 'chef/resource/service'
-require 'chef/provider/service/simple'
-require 'chef/mixin/which'
+require "chef/resource/service"
+require "chef/provider/service/simple"
+require "chef/mixin/which"
class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
include Chef::Mixin::Which
- provides :service, os: "linux"
+ provides :service, os: "linux" do |node|
+ Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd)
+ end
attr_accessor :status_check_success
- def self.provides?(node, resource)
- super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:systemd)
- end
-
def self.supports?(resource, action)
Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:systemd)
end
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
index 8d4aa41035..c0f7e020f6 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -16,23 +16,22 @@
# limitations under the License.
#
-require 'chef/resource/service'
-require 'chef/provider/service/simple'
-require 'chef/mixin/command'
-require 'chef/util/file_edit'
+require "chef/resource/service"
+require "chef/provider/service/simple"
+require "chef/mixin/command"
+require "chef/util/file_edit"
class Chef
class Provider
class Service
class Upstart < Chef::Provider::Service::Simple
- UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/
- provides :service, os: "linux"
-
- def self.provides?(node, resource)
- super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart)
+ provides :service, platform_family: "debian", override: true do |node|
+ Chef::Platform::ServiceHelpers.service_resource_providers.include?(:upstart)
end
+ UPSTART_STATE_FORMAT = /\S+ \(?(start|stop)?\)? ?[\/ ](\w+)/
+
def self.supports?(resource, action)
Chef::Platform::ServiceHelpers.config_for_service(resource.service_name).include?(:upstart)
end
@@ -107,7 +106,7 @@ class Chef
Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
begin
- if shell_out!(@new_resource.status_command) == 0
+ if shell_out!(@new_resource.status_command).exitstatus == 0
@current_resource.running true
end
rescue
@@ -131,7 +130,7 @@ class Chef
# Get enabled/disabled state by reading job configuration file
if ::File.exists?("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
Chef::Log.debug("#{@new_resource} found #{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}")
- ::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}",'r') do |file|
+ ::File.open("#{@upstart_job_dir}/#{@new_resource.service_name}#{@upstart_conf_suffix}","r") do |file|
while line = file.gets
case line
when /^start on/
@@ -225,10 +224,10 @@ class Chef
command = "/sbin/status #{@job}"
status = popen4(command) do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
- # rsyslog stop/waiting
# service goal/state
# OR
- # rsyslog (stop) waiting
+ # service (instance) goal/state
+ # OR
# service (goal) state
line =~ UPSTART_STATE_FORMAT
data = Regexp.last_match
diff --git a/lib/chef/provider/service/windows.rb b/lib/chef/provider/service/windows.rb
index ba53f0a3c3..0ae3c5b09f 100644
--- a/lib/chef/provider/service/windows.rb
+++ b/lib/chef/provider/service/windows.rb
@@ -18,14 +18,13 @@
# limitations under the License.
#
-require 'chef/provider/service/simple'
+require "chef/provider/service/simple"
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require 'chef/win32/error'
- require 'win32/service'
+ require "chef/win32/error"
+ require "win32/service"
end
class Chef::Provider::Service::Windows < Chef::Provider::Service
-
provides :service, os: "windows"
provides :windows_service, os: "windows"
@@ -33,21 +32,23 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
include Chef::ReservedNames::Win32::API::Error rescue LoadError
#Win32::Service.get_start_type
- AUTO_START = 'auto start'
- MANUAL = 'demand start'
- DISABLED = 'disabled'
+ AUTO_START = "auto start"
+ MANUAL = "demand start"
+ DISABLED = "disabled"
#Win32::Service.get_current_state
- RUNNING = 'running'
- STOPPED = 'stopped'
- CONTINUE_PENDING = 'continue pending'
- PAUSE_PENDING = 'pause pending'
- PAUSED = 'paused'
- START_PENDING = 'start pending'
- STOP_PENDING = 'stop pending'
+ RUNNING = "running"
+ STOPPED = "stopped"
+ CONTINUE_PENDING = "continue pending"
+ PAUSE_PENDING = "pause pending"
+ PAUSED = "paused"
+ START_PENDING = "start pending"
+ STOP_PENDING = "stop pending"
TIMEOUT = 60
+ SERVICE_RIGHT = "SeServiceLogonRight"
+
def whyrun_supported?
false
end
@@ -79,10 +80,10 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
Win32::Service.configure(new_config)
Chef::Log.info "#{@new_resource} configured with #{new_config.inspect}"
- # it would be nice to check if the user already has the logon privilege, but that turns out to be
- # nontrivial.
if new_config.has_key?(:service_start_name)
- grant_service_logon(new_config[:service_start_name])
+ unless Chef::ReservedNames::Win32::Security.get_account_right(canonicalize_username(new_config[:service_start_name])).include?(SERVICE_RIGHT)
+ grant_service_logon(new_config[:service_start_name])
+ end
end
state = current_state
@@ -237,74 +238,25 @@ class Chef::Provider::Service::Windows < Chef::Provider::Service
end
private
- def make_policy_text(username)
- text = <<-EOS
-[Unicode]
-Unicode=yes
-[Privilege Rights]
-SeServiceLogonRight = \\\\#{canonicalize_username(username)},*S-1-5-80-0
-[Version]
-signature="$CHICAGO$"
-Revision=1
-EOS
- end
-
- def grant_logfile_name(username)
- Chef::Util::PathHelper.canonical_path("#{Dir.tmpdir}/logon_grant-#{clean_username_for_path(username)}-#{$$}.log", prefix=false)
- end
-
- def grant_policyfile_name(username)
- Chef::Util::PathHelper.canonical_path("#{Dir.tmpdir}/service_logon_policy-#{clean_username_for_path(username)}-#{$$}.inf", prefix=false)
- end
-
- def grant_dbfile_name(username)
- "#{ENV['TEMP']}\\secedit.sdb"
- end
-
def grant_service_logon(username)
- logfile = grant_logfile_name(username)
- policy_file = ::File.new(grant_policyfile_name(username), 'w')
- policy_text = make_policy_text(username)
- dbfile = grant_dbfile_name(username) # this is just an audit file.
-
begin
- Chef::Log.debug "Policy file text:\n#{policy_text}"
- policy_file.puts(policy_text)
- policy_file.close # need to flush the buffer.
-
- # it would be nice to do this with APIs instead, but the LSA_* APIs are
- # particularly onerous and life is short.
- cmd = %Q{secedit.exe /configure /db "#{dbfile}" /cfg "#{policy_file.path}" /areas USER_RIGHTS SECURITYPOLICY SERVICES /log "#{logfile}"}
- Chef::Log.debug "Granting logon-as-service privilege with: #{cmd}"
- runner = shell_out(cmd)
-
- if runner.exitstatus != 0
- Chef::Log.fatal "Logon-as-service grant failed with output: #{runner.stdout}"
- raise Chef::Exceptions::Service, <<-EOS
-Logon-as-service grant failed with policy file #{policy_file.path}.
-You can look at #{logfile} for details, or do `secedit /analyze #{dbfile}`.
-The failed command was `#{cmd}`.
-EOS
- end
-
- Chef::Log.info "Grant logon-as-service to user '#{username}' successful."
-
- ::File.delete(dbfile) rescue nil
- ::File.delete(policy_file)
- ::File.delete(logfile) rescue nil # logfile is not always present at end.
+ Chef::ReservedNames::Win32::Security.add_account_right(canonicalize_username(username), SERVICE_RIGHT)
+ rescue Chef::Exceptions::Win32APIError => err
+ Chef::Log.fatal "Logon-as-service grant failed with output: #{err}"
+ raise Chef::Exceptions::Service, "Logon-as-service grant failed for #{username}: #{err}"
end
+
+ Chef::Log.info "Grant logon-as-service to user '#{username}' successful."
true
end
# remove characters that make for broken or wonky filenames.
def clean_username_for_path(username)
- username.gsub(/[\/\\. ]+/, '_')
+ username.gsub(/[\/\\. ]+/, "_")
end
- # the security policy file only seems to accept \\username, so fix .\username or .\\username.
- # TODO: this probably has to be fixed to handle various valid Windows names correctly.
def canonicalize_username(username)
- username.sub(/^\.?\\+/, '')
+ username.sub(/^\.?\\+/, "")
end
def current_state
@@ -353,7 +305,7 @@ EOS
Chef::Log.debug "#{@new_resource.name} setting start_type to #{type}"
Win32::Service.configure(
:service_name => @new_resource.service_name,
- :start_type => allowed_types[type]
+ :start_type => allowed_types[type],
)
@new_resource.updated_by_last_action(true)
end
diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb
index 5f36483c32..2cc0da65a0 100644
--- a/lib/chef/provider/subversion.rb
+++ b/lib/chef/provider/subversion.rb
@@ -18,10 +18,10 @@
#TODO subversion and git should both extend from a base SCM provider.
-require 'chef/log'
-require 'chef/provider'
-require 'chef/mixin/command'
-require 'fileutils'
+require "chef/log"
+require "chef/provider"
+require "chef/mixin/command"
+require "fileutils"
class Chef
class Provider
@@ -130,8 +130,8 @@ class Chef
@new_resource.revision
else
command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}")
- status, svn_info, error_message = output_of_command(command, run_options)
- handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
+ svn_info = shell_out!(command, run_options(:cwd => cwd, :returns => [0,1])).stdout
+
extract_revision_info(svn_info)
end
end
@@ -142,11 +142,8 @@ class Chef
def find_current_revision
return nil unless ::File.exist?(::File.join(@new_resource.destination, ".svn"))
command = scm(:info)
- status, svn_info, error_message = output_of_command(command, run_options(:cwd => cwd))
+ svn_info = shell_out!(command, run_options(:cwd => cwd, :returns => [0,1])).stdout
- unless [0,1].include?(status.exitstatus)
- handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
- end
extract_revision_info(svn_info)
end
@@ -179,7 +176,8 @@ class Chef
end
attrs
end
- rev = (repo_attrs['Last Changed Rev'] || repo_attrs['Revision'])
+ rev = (repo_attrs["Last Changed Rev"] || repo_attrs["Revision"])
+ rev.strip! if rev
raise "Could not parse `svn info` data: #{svn_info}" if repo_attrs.empty?
Chef::Log.debug "#{@new_resource} resolved revision #{@new_resource.revision} to #{rev}"
rev
@@ -197,12 +195,20 @@ class Chef
end
def scm(*args)
- ['svn', *args].compact.join(" ")
+ binary = svn_binary
+ binary = "\"#{binary}\"" if binary =~ /\s/
+ [binary, *args].compact.join(" ")
end
def target_dir_non_existent_or_empty?
- !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
+ !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == [".",".."]
end
+
+ def svn_binary
+ @new_resource.svn_binary ||
+ (Chef::Platform.windows? ? "svn.exe" : "svn")
+ end
+
def assert_target_directory_valid!
target_parent_directory = ::File.dirname(@new_resource.destination)
unless ::File.directory?(target_parent_directory)
diff --git a/lib/chef/provider/template.rb b/lib/chef/provider/template.rb
index 1e759074b9..7cbfca025f 100644
--- a/lib/chef/provider/template.rb
+++ b/lib/chef/provider/template.rb
@@ -17,10 +17,10 @@
# limitations under the License.
#
-require 'chef/provider/template_finder'
-require 'chef/provider/file'
-require 'chef/deprecation/provider/template'
-require 'chef/deprecation/warnings'
+require "chef/provider/template_finder"
+require "chef/provider/file"
+require "chef/deprecation/provider/template"
+require "chef/deprecation/warnings"
class Chef
class Provider
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index 7fc680ec85..891861de8b 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -16,8 +16,8 @@
# limitations under the License.
#
-require 'chef/mixin/template'
-require 'chef/file_content_management/content_base'
+require "chef/mixin/template"
+require "chef/file_content_management/content_base"
class Chef
class Provider
@@ -29,20 +29,30 @@ class Chef
def template_location
@template_file_cache_location ||= begin
- template_finder.find(@new_resource.source, :local => @new_resource.local, :cookbook => @new_resource.cookbook)
+ template_finder.find(new_resource.source, :local => new_resource.local, :cookbook => new_resource.cookbook)
end
end
private
def file_for_provider
- context = TemplateContext.new(@new_resource.variables)
- context[:node] = @run_context.node
+ context = TemplateContext.new(new_resource.variables)
+ context[:node] = run_context.node
context[:template_finder] = template_finder
- context._extend_modules(@new_resource.helper_modules)
+
+ # helper variables
+ context[:cookbook_name] = new_resource.cookbook_name unless context.keys.include?(:coookbook_name)
+ context[:recipe_name] = new_resource.recipe_name unless context.keys.include?(:recipe_name)
+ context[:recipe_line_string] = new_resource.source_line unless context.keys.include?(:recipe_line_string)
+ context[:recipe_path] = new_resource.source_line_file unless context.keys.include?(:recipe_path)
+ context[:recipe_line] = new_resource.source_line_number unless context.keys.include?(:recipe_line)
+ context[:template_name] = new_resource.source unless context.keys.include?(:template_name)
+ context[:template_path] = template_location unless context.keys.include?(:template_path)
+
+ context._extend_modules(new_resource.helper_modules)
output = context.render_template(template_location)
- tempfile = Tempfile.open("chef-rendered-template")
+ tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
tempfile.binmode
tempfile.write(output)
tempfile.close
@@ -51,7 +61,7 @@ class Chef
def template_finder
@template_finder ||= begin
- TemplateFinder.new(run_context, @new_resource.cookbook_name, @run_context.node)
+ TemplateFinder.new(run_context, new_resource.cookbook_name, run_context.node)
end
end
end
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index f6ac72448e..b819401731 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -16,14 +16,13 @@
# limitations under the License.
#
-require 'chef/provider'
-require 'chef/mixin/command'
-require 'etc'
+require "chef/provider"
+require "chef/mixin/command"
+require "etc"
class Chef
class Provider
class User < Chef::Provider
-
include Chef::Mixin::Command
attr_accessor :user_exists, :locked
@@ -72,9 +71,9 @@ class Chef
end
@current_resource.comment(user_info.gecos)
- if @new_resource.password && @current_resource.password == 'x'
+ if @new_resource.password && @current_resource.password == "x"
begin
- require 'shadow'
+ require "shadow"
rescue LoadError
@shadow_lib_ok = false
else
@@ -90,7 +89,7 @@ class Chef
end
def define_resource_requirements
- requirements.assert(:all_actions) do |a|
+ requirements.assert(:create, :modify, :manage, :lock, :unlock) do |a|
a.assertion { @group_name_resolved }
a.failure_message Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{@new_resource.gid}"
a.whyrun "group name #{@new_resource.gid} does not exist. This will cause group assignment to fail. Assuming this group will have been created previously."
@@ -208,7 +207,6 @@ class Chef
def unlock_user
raise NotImplementedError
end
-
end
end
end
diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb
index af08ab4364..83bd900f79 100644
--- a/lib/chef/provider/user/aix.rb
+++ b/lib/chef/provider/user/aix.rb
@@ -18,9 +18,10 @@ class Chef
class Provider
class User
class Aix < Chef::Provider::User::Useradd
+ provides :user, platform: %w{aix}
UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
-
+
def create_user
super
add_password
@@ -88,7 +89,7 @@ class Chef
end
end
end
-
+
end
end
end
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index 0c0c85e18b..563d56dce7 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -16,11 +16,11 @@
# limitations under the License.
#
-require 'mixlib/shellout'
-require 'chef/provider/user'
-require 'openssl'
-require 'plist'
-require 'chef/util/path_helper'
+require "mixlib/shellout"
+require "chef/provider/user"
+require "openssl"
+require "plist"
+require "chef/util/path_helper"
class Chef
class Provider
@@ -44,6 +44,10 @@ class Chef
# This provider only supports Mac OSX versions 10.7 and above
class Dscl < Chef::Provider::User
+ attr_accessor :user_info
+ attr_accessor :authentication_authority
+ attr_accessor :password_shadow_conversion_algorithm
+
provides :user, os: "darwin"
def define_resource_requirements
@@ -56,19 +60,19 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { ::File.exists?("/usr/bin/dscl") }
- a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{@new_resource}!")
+ a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{new_resource}!")
end
requirements.assert(:all_actions) do |a|
a.assertion { ::File.exists?("/usr/bin/plutil") }
- a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{@new_resource}!")
+ a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{new_resource}!")
end
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if @new_resource.password && mac_osx_version_greater_than_10_7?
+ if new_resource.password && mac_osx_version_greater_than_10_7?
# SALTED-SHA512 password shadow hashes are not supported on 10.8 and above.
- !salted_sha512?(@new_resource.password)
+ !salted_sha512?(new_resource.password)
else
true
end
@@ -80,10 +84,10 @@ in 'password', with the associated 'salt' and 'iterations'.")
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if @new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(@new_resource.password)
+ if new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(new_resource.password)
# salt and iterations should be specified when
# SALTED-SHA512-PBKDF2 password shadow hash is given
- !@new_resource.salt.nil? && !@new_resource.iterations.nil?
+ !new_resource.salt.nil? && !new_resource.iterations.nil?
else
true
end
@@ -94,9 +98,9 @@ in 'password', with the associated 'salt' and 'iterations'.")
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if @new_resource.password && !mac_osx_version_greater_than_10_7?
+ if new_resource.password && !mac_osx_version_greater_than_10_7?
# On 10.7 SALTED-SHA512-PBKDF2 is not supported
- !salted_sha512_pbkdf2?(@new_resource.password)
+ !salted_sha512_pbkdf2?(new_resource.password)
else
true
end
@@ -109,21 +113,21 @@ user password using shadow hash.")
end
def load_current_resource
- @current_resource = Chef::Resource::User.new(@new_resource.username)
- @current_resource.username(@new_resource.username)
+ @current_resource = Chef::Resource::User.new(new_resource.username)
+ current_resource.username(new_resource.username)
@user_info = read_user_info
- if @user_info
- @current_resource.uid(dscl_get(@user_info, :uid))
- @current_resource.gid(dscl_get(@user_info, :gid))
- @current_resource.home(dscl_get(@user_info, :home))
- @current_resource.shell(dscl_get(@user_info, :shell))
- @current_resource.comment(dscl_get(@user_info, :comment))
- @authentication_authority = dscl_get(@user_info, :auth_authority)
-
- if @new_resource.password && dscl_get(@user_info, :password) == "********"
+ if user_info
+ current_resource.uid(dscl_get(user_info, :uid))
+ current_resource.gid(dscl_get(user_info, :gid))
+ current_resource.home(dscl_get(user_info, :home))
+ current_resource.shell(dscl_get(user_info, :shell))
+ current_resource.comment(dscl_get(user_info, :comment))
+ @authentication_authority = dscl_get(user_info, :auth_authority)
+
+ if new_resource.password && dscl_get(user_info, :password) == "********"
# A password is set. Let's get the password information from shadow file
- shadow_hash_binary = dscl_get(@user_info, :shadow_hash)
+ shadow_hash_binary = dscl_get(user_info, :shadow_hash)
# Calling shell_out directly since we want to give an input stream
shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string)
@@ -132,26 +136,26 @@ user password using shadow hash.")
if shadow_hash["SALTED-SHA512"]
# Convert the shadow value from Base64 encoding to hex before consuming them
@password_shadow_conversion_algorithm = "SALTED-SHA512"
- @current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack('H*').first)
+ current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack("H*").first)
elsif shadow_hash["SALTED-SHA512-PBKDF2"]
@password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2"
# Convert the entropy from Base64 encoding to hex before consuming them
- @current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first)
- @current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"])
+ current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack("H*").first)
+ current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"])
# Convert the salt from Base64 encoding to hex before consuming them
- @current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack('H*').first)
+ current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack("H*").first)
else
raise(Chef::Exceptions::User,"Unknown shadow_hash format: #{shadow_hash.keys.join(' ')}")
end
end
- convert_group_name if @new_resource.gid
+ convert_group_name if new_resource.gid
else
@user_exists = false
- Chef::Log.debug("#{@new_resource} user does not exist")
+ Chef::Log.debug("#{new_resource} user does not exist")
end
- @current_resource
+ current_resource
end
#
@@ -190,15 +194,16 @@ user password using shadow hash.")
# Create a user using dscl
#
def dscl_create_user
- run_dscl("create /Users/#{@new_resource.username}")
+ run_dscl("create /Users/#{new_resource.username}")
end
#
# Saves the specified Chef user `comment` into RealName attribute
- # of Mac user.
+ # of Mac user. If `comment` is not specified, it takes `username` value.
#
def dscl_create_comment
- run_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
+ comment = new_resource.comment || new_resource.username
+ run_dscl("create /Users/#{new_resource.username} RealName '#{comment}'")
end
#
@@ -207,13 +212,14 @@ user password using shadow hash.")
# from 200 if `system` is set, 500 otherwise.
#
def dscl_set_uid
- @new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '')
+ # XXX: mutates the new resource
+ new_resource.uid(get_free_uid) if (new_resource.uid.nil? || new_resource.uid == "")
- if uid_used?(@new_resource.uid)
- raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use")
+ if uid_used?(new_resource.uid)
+ raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{new_resource.uid} is already in use")
end
- run_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
+ run_dscl("create /Users/#{new_resource.username} UniqueID #{new_resource.uid}")
end
#
@@ -222,7 +228,7 @@ user password using shadow hash.")
#
def get_free_uid(search_limit=1000)
uid = nil
- base_uid = @new_resource.system ? 200 : 500
+ base_uid = new_resource.system ? 200 : 500
next_uid_guess = base_uid
users_uids = run_dscl("list /Users uid")
while(next_uid_guess < search_limit + base_uid)
@@ -248,7 +254,7 @@ user password using shadow hash.")
tmap
end
if uid_map[uid.to_s]
- unless uid_map[uid.to_s] == @new_resource.username.to_s
+ unless uid_map[uid.to_s] == new_resource.username.to_s
return true
end
end
@@ -257,18 +263,23 @@ user password using shadow hash.")
#
# Sets the group id for the user using dscl. Fails if a group doesn't
- # exist on the system with given group id.
+ # exist on the system with given group id. If `gid` is not specified, it
+ # sets a default Mac user group "staff", with id 20.
#
def dscl_set_gid
- unless @new_resource.gid && @new_resource.gid.to_s.match(/^\d+$/)
+ if new_resource.gid.nil?
+ # XXX: mutates the new resource
+ new_resource.gid(20)
+ elsif !new_resource.gid.to_s.match(/^\d+$/)
begin
- possible_gid = run_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last
+ possible_gid = run_dscl("read /Groups/#{new_resource.gid} PrimaryGroupID").split(" ").last
rescue Chef::Exceptions::DsclCommandFailed => e
- raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}")
+ raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{new_resource.gid} when creating user #{new_resource.username}")
end
- @new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
+ # XXX: mutates the new resource
+ new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
end
- run_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
+ run_dscl("create /Users/#{new_resource.username} PrimaryGroupID '#{new_resource.gid}'")
end
#
@@ -276,15 +287,15 @@ user password using shadow hash.")
# directory is managed (moved / created) for the user.
#
def dscl_set_home
- if @new_resource.home.nil? || @new_resource.home.empty?
- run_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory")
+ if new_resource.home.nil? || new_resource.home.empty?
+ run_dscl("delete /Users/#{new_resource.username} NFSHomeDirectory")
return
end
- if @new_resource.supports[:manage_home]
+ if new_resource.supports[:manage_home]
validate_home_dir_specification!
- if (@current_resource.home == @new_resource.home) && !new_home_exists?
+ if (current_resource.home == new_resource.home) && !new_home_exists?
ditto_home
elsif !current_home_exists? && !new_home_exists?
ditto_home
@@ -292,49 +303,49 @@ user password using shadow hash.")
move_home
end
end
- run_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
+ run_dscl("create /Users/#{new_resource.username} NFSHomeDirectory '#{new_resource.home}'")
end
def validate_home_dir_specification!
- unless @new_resource.home =~ /^\//
- raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'")
+ unless new_resource.home =~ /^\//
+ raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{new_resource.username}', home directory: '#{new_resource.home}'")
end
end
def current_home_exists?
- ::File.exist?("#{@current_resource.home}")
+ ::File.exist?("#{current_resource.home}")
end
def new_home_exists?
- ::File.exist?("#{@new_resource.home}")
+ ::File.exist?("#{new_resource.home}")
end
def ditto_home
skel = "/System/Library/User Template/English.lproj"
raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
- shell_out! "ditto '#{skel}' '#{@new_resource.home}'"
- ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
+ shell_out! "ditto '#{skel}' '#{new_resource.home}'"
+ ::FileUtils.chown_R(new_resource.username,new_resource.gid.to_s,new_resource.home)
end
def move_home
- Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}")
+ Chef::Log.debug("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}")
- src = @current_resource.home
- FileUtils.mkdir_p(@new_resource.home)
+ src = current_resource.home
+ FileUtils.mkdir_p(new_resource.home)
files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
- ::FileUtils.mv(files,@new_resource.home, :force => true)
+ ::FileUtils.mv(files,new_resource.home, :force => true)
::FileUtils.rmdir(src)
- ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
+ ::FileUtils.chown_R(new_resource.username,new_resource.gid.to_s,new_resource.home)
end
#
# Sets the shell for the user using dscl.
#
def dscl_set_shell
- if @new_resource.shell || ::File.exists?("#{@new_resource.shell}")
- run_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
+ if new_resource.shell || ::File.exists?("#{new_resource.shell}")
+ run_dscl("create /Users/#{new_resource.username} UserShell '#{new_resource.shell}'")
else
- run_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
+ run_dscl("create /Users/#{new_resource.username} UserShell '/usr/bin/false'")
end
end
@@ -345,17 +356,17 @@ user password using shadow hash.")
#
def set_password
# Return if there is no password to set
- return if @new_resource.password.nil?
+ return if new_resource.password.nil?
shadow_info = prepare_password_shadow_info
# Shadow info is saved as binary plist. Convert the info to binary plist.
shadow_info_binary = StringIO.new
command = Mixlib::ShellOut.new("plutil -convert binary1 -o - -",
- :input => shadow_info.to_plist, :live_stream => shadow_info_binary)
+ :input => shadow_info.to_plist, :live_stream => shadow_info_binary)
command.run_command
- if @user_info.nil?
+ if user_info.nil?
# User is just created. read_user_info() will read the fresh information
# for the user with a cache flush. However with experimentation we've seen
# that dscl cache is not immediately updated after the creation of the user
@@ -365,8 +376,8 @@ user password using shadow hash.")
end
# Replace the shadow info in user's plist
- dscl_set(@user_info, :shadow_hash, shadow_info_binary)
- save_user_info(@user_info)
+ dscl_set(user_info, :shadow_hash, shadow_info_binary)
+ save_user_info(user_info)
end
#
@@ -379,33 +390,33 @@ user password using shadow hash.")
iterations = nil
if mac_osx_version_10_7?
- hash_value = if salted_sha512?(@new_resource.password)
- @new_resource.password
- else
- # Create a random 4 byte salt
- salt = OpenSSL::Random.random_bytes(4)
- encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + @new_resource.password)
- hash_value = salt.unpack('H*').first + encoded_password
- end
+ hash_value = if salted_sha512?(new_resource.password)
+ new_resource.password
+ else
+ # Create a random 4 byte salt
+ salt = OpenSSL::Random.random_bytes(4)
+ encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + new_resource.password)
+ hash_value = salt.unpack("H*").first + encoded_password
+ end
shadow_info["SALTED-SHA512"] = StringIO.new
shadow_info["SALTED-SHA512"].string = convert_to_binary(hash_value)
shadow_info
else
- if salted_sha512_pbkdf2?(@new_resource.password)
- entropy = convert_to_binary(@new_resource.password)
- salt = convert_to_binary(@new_resource.salt)
- iterations = @new_resource.iterations
+ if salted_sha512_pbkdf2?(new_resource.password)
+ entropy = convert_to_binary(new_resource.password)
+ salt = convert_to_binary(new_resource.salt)
+ iterations = new_resource.iterations
else
salt = OpenSSL::Random.random_bytes(32)
- iterations = @new_resource.iterations # Use the default if not specified by the user
+ iterations = new_resource.iterations # Use the default if not specified by the user
entropy = OpenSSL::PKCS5::pbkdf2_hmac(
- @new_resource.password,
+ new_resource.password,
salt,
iterations,
128,
- OpenSSL::Digest::SHA512.new
+ OpenSSL::Digest::SHA512.new,
)
end
@@ -427,43 +438,43 @@ user password using shadow hash.")
# and deleting home directory if needed.
#
def remove_user
- if @new_resource.supports[:manage_home]
+ if new_resource.supports[:manage_home]
# Remove home directory
- FileUtils.rm_rf(@current_resource.home)
+ FileUtils.rm_rf(current_resource.home)
end
# Remove the user from its groups
run_dscl("list /Groups").each_line do |group|
if member_of_group?(group.chomp)
- run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{@new_resource.username}'")
+ run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{new_resource.username}'")
end
end
# Remove user account
- run_dscl("delete /Users/#{@new_resource.username}")
+ run_dscl("delete /Users/#{new_resource.username}")
end
#
# Locks the user.
#
def lock_user
- run_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
+ run_dscl("append /Users/#{new_resource.username} AuthenticationAuthority ';DisabledUser;'")
end
#
# Unlocks the user
#
def unlock_user
- auth_string = @authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip
- run_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'")
+ auth_string = authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip
+ run_dscl("create /Users/#{new_resource.username} AuthenticationAuthority '#{auth_string}'")
end
#
# Returns true if the user is locked, false otherwise.
#
def locked?
- if @authentication_authority
- !!(@authentication_authority =~ /DisabledUser/ )
+ if authentication_authority
+ !!(authentication_authority =~ /DisabledUser/ )
else
false
end
@@ -485,11 +496,11 @@ user password using shadow hash.")
# given attribute.
#
def diverged?(parameter)
- parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?)
+ parameter_updated?(parameter) && (not new_resource.send(parameter).nil?)
end
def parameter_updated?(parameter)
- not (@new_resource.send(parameter) == @current_resource.send(parameter))
+ not (new_resource.send(parameter) == current_resource.send(parameter))
end
#
@@ -500,11 +511,11 @@ user password using shadow hash.")
# type of the password specified.
#
def diverged_password?
- return false if @new_resource.password.nil?
+ return false if new_resource.password.nil?
# Dscl provider supports both plain text passwords and shadow hashes.
if mac_osx_version_10_7?
- if salted_sha512?(@new_resource.password)
+ if salted_sha512?(new_resource.password)
diverged?(:password)
else
!salted_sha512_password_match?
@@ -514,14 +525,14 @@ user password using shadow hash.")
# will be updated when the user logs in. So it's possible that we will have
# SALTED-SHA512 password in the current_resource. In that case we will force
# password to be updated.
- return true if salted_sha512?(@current_resource.password)
+ return true if salted_sha512?(current_resource.password)
# Some system users don't have salts; this can happen if the system is
# upgraded and the user hasn't logged in yet. In this case, we will force
# the password to be updated.
- return true if @current_resource.salt.nil?
+ return true if current_resource.salt.nil?
- if salted_sha512_pbkdf2?(@new_resource.password)
+ if salted_sha512_pbkdf2?(new_resource.password)
diverged?(:password) || diverged?(:salt) || diverged?(:iterations)
else
!salted_sha512_pbkdf2_password_match?
@@ -543,7 +554,7 @@ user password using shadow hash.")
# GroupMembership: root admin etc
members = membership_info.split(" ")
members.shift # Get rid of GroupMembership: string
- members.include?(@new_resource.username)
+ members.include?(new_resource.username)
end
#
@@ -559,7 +570,7 @@ user password using shadow hash.")
:comment => "realname",
:password => "passwd",
:auth_authority => "authentication_authority",
- :shadow_hash => "ShadowHashData"
+ :shadow_hash => "ShadowHashData",
}.freeze
# Directory where the user plist files are stored for versions 10.7 and above
@@ -577,7 +588,7 @@ user password using shadow hash.")
shell_out("dscacheutil '-flushcache'")
begin
- user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
+ user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}")
user_info = Plist::parse_xml(user_plist_info)
rescue Chef::Exceptions::PlistUtilCommandFailed
@@ -591,7 +602,7 @@ user password using shadow hash.")
# in DSCL_PROPERTY_MAP to the disk.
#
def save_user_info(user_info)
- user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
+ user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
Plist::Emit.save_plist(user_info, user_plist_file)
run_plutil("convert binary1 #{user_plist_file}")
end
@@ -653,7 +664,7 @@ user password using shadow hash.")
result = shell_out("plutil -#{args.join(' ')}")
raise(Chef::Exceptions::PlistUtilCommandFailed,"plutil error: #{result.inspect}") unless result.exitstatus == 0
if result.stdout.encoding == Encoding::ASCII_8BIT
- result.stdout.encode("utf-8", "binary", :undef => :replace, :invalid => :replace, :replace => '?')
+ result.stdout.encode("utf-8", "binary", :undef => :replace, :invalid => :replace, :replace => "?")
else
result.stdout
end
@@ -664,7 +675,7 @@ user password using shadow hash.")
end
def convert_to_binary(string)
- string.unpack('a2'*(string.size/2)).collect { |i| i.hex.chr }.join
+ string.unpack("a2"*(string.size/2)).collect { |i| i.hex.chr }.join
end
def salted_sha512?(string)
@@ -673,9 +684,9 @@ user password using shadow hash.")
def salted_sha512_password_match?
# Salt is included in the first 4 bytes of shadow data
- salt = @current_resource.password.slice(0,8)
- shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + @new_resource.password)
- @current_resource.password == salt + shadow
+ salt = current_resource.password.slice(0,8)
+ shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + new_resource.password)
+ current_resource.password == salt + shadow
end
def salted_sha512_pbkdf2?(string)
@@ -683,15 +694,15 @@ user password using shadow hash.")
end
def salted_sha512_pbkdf2_password_match?
- salt = convert_to_binary(@current_resource.salt)
+ salt = convert_to_binary(current_resource.salt)
OpenSSL::PKCS5::pbkdf2_hmac(
- @new_resource.password,
+ new_resource.password,
salt,
- @current_resource.iterations,
+ current_resource.iterations,
128,
- OpenSSL::Digest::SHA512.new
- ).unpack('H*').first == @current_resource.password
+ OpenSSL::Digest::SHA512.new,
+ ).unpack("H*").first == current_resource.password
end
end
diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb
index fe71e93561..60df7d55f8 100644
--- a/lib/chef/provider/user/pw.rb
+++ b/lib/chef/provider/user/pw.rb
@@ -16,12 +16,13 @@
# limitations under the License.
#
-require 'chef/provider/user'
+require "chef/provider/user"
class Chef
class Provider
class User
class Pw < Chef::Provider::User
+ provides :user, platform: %w{freebsd}
def load_current_resource
super
@@ -70,11 +71,11 @@ class Chef
opts = " #{@new_resource.username}"
field_list = {
- 'comment' => "-c",
- 'home' => "-d",
- 'gid' => "-g",
- 'uid' => "-u",
- 'shell' => "-s"
+ "comment" => "-c",
+ "home" => "-d",
+ "gid" => "-g",
+ "uid" => "-u",
+ "shell" => "-s",
}
field_list.sort{ |a,b| a[0] <=> b[0] }.each do |field, option|
field_symbol = field.to_sym
diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb
index d480acaced..18f44523ff 100644
--- a/lib/chef/provider/user/solaris.rb
+++ b/lib/chef/provider/user/solaris.rb
@@ -1,7 +1,9 @@
#
# Author:: Stephen Nelson-Smith (<sns@opscode.com>)
# Author:: Jon Ramsey (<jonathon.ramsey@gmail.com>)
+# Author:: Dave Eddy (<dave@daveeddy.com>)
# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# Copyright:: Copyright 2015, Dave Eddy
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,12 +18,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-require 'chef/provider/user/useradd'
+require "chef/provider/user/useradd"
class Chef
class Provider
class User
class Solaris < Chef::Provider::User::Useradd
+ provides :user, platform: %w{omnios solaris2}
UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
attr_writer :password_file
@@ -41,6 +44,32 @@ class Chef
super
end
+ def check_lock
+ shadow_line = shell_out!("getent", "shadow", new_resource.username).stdout.strip rescue nil
+
+ # if the command fails we return nil, this can happen if the user
+ # in question doesn't exist
+ return nil if shadow_line.nil?
+
+ # convert "dave:NP:16507::::::\n" to "NP"
+ fields = shadow_line.split(":")
+
+ # '*LK*...' and 'LK' are both considered locked,
+ # so look for LK at the beginning of the shadow entry
+ # optionally surrounded by '*'
+ @locked = !!fields[1].match(/^\*?LK\*?/)
+
+ @locked
+ end
+
+ def lock_user
+ shell_out!("passwd", "-l", new_resource.username)
+ end
+
+ def unlock_user
+ shell_out!("passwd", "-u", new_resource.username)
+ end
+
private
def manage_password
@@ -65,9 +94,10 @@ class Chef
buffer.close
# FIXME: mostly duplicates code with file provider deploying a file
- mode = ::File.stat(@password_file).mode & 07777
- uid = ::File.stat(@password_file).uid
- gid = ::File.stat(@password_file).gid
+ s = ::File.stat(@password_file)
+ mode = s.mode & 07777
+ uid = s.uid
+ gid = s.gid
FileUtils.chown uid, gid, buffer.path
FileUtils.chmod mode, buffer.path
diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb
index cc770c0be2..da6606bc14 100644
--- a/lib/chef/provider/user/useradd.rb
+++ b/lib/chef/provider/user/useradd.rb
@@ -16,13 +16,14 @@
# limitations under the License.
#
-require 'pathname'
-require 'chef/provider/user'
+require "pathname"
+require "chef/provider/user"
class Chef
class Provider
class User
class Useradd < Chef::Provider::User
+ provides :user
UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]]
@@ -62,7 +63,7 @@ class Chef
raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if passwd_s.stdout.empty?
- status_line = passwd_s.stdout.split(' ')
+ status_line = passwd_s.stdout.split(" ")
case status_line[1]
when /^P/
@locked = false
@@ -74,11 +75,11 @@ class Chef
unless passwd_s.exitstatus == 0
raise_lock_error = false
- if ['redhat', 'centos'].include?(node[:platform])
- passwd_version_check = shell_out!('rpm -q passwd')
+ if ["redhat", "centos"].include?(node[:platform])
+ passwd_version_check = shell_out!("rpm -q passwd")
passwd_version = passwd_version_check.stdout.chomp
- unless passwd_version == 'passwd-0.73-1'
+ unless passwd_version == "passwd-0.73-1"
raise_lock_error = true
end
else
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index e282a11d45..0851c2272a 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -16,10 +16,10 @@
# limitations under the License.
#
-require 'chef/provider/user'
-require 'chef/exceptions'
+require "chef/provider/user"
+require "chef/exceptions"
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
- require 'chef/util/windows/net_user'
+ require "chef/util/windows/net_user"
end
class Chef
@@ -35,6 +35,10 @@ class Chef
end
def load_current_resource
+ if @new_resource.gid
+ Chef::Log.warn("The 'gid' attribute is not implemented by the Windows platform. Please use the 'group' resource to assign a user to a group.")
+ end
+
@current_resource = Chef::Resource::User.new(@new_resource.name)
@current_resource.username(@new_resource.username)
user_info = nil
@@ -42,7 +46,6 @@ class Chef
user_info = @net_user.get_info
@current_resource.uid(user_info[:user_id])
- @current_resource.gid(user_info[:primary_group_id])
@current_resource.comment(user_info[:full_name])
@current_resource.home(user_info[:home_dir])
@current_resource.shell(user_info[:script_path])
@@ -65,7 +68,7 @@ class Chef
Chef::Log.debug("#{@new_resource} password has changed")
return true
end
- [ :uid, :gid, :comment, :home, :shell ].any? do |user_attrib|
+ [ :uid, :comment, :home, :shell ].any? do |user_attrib|
!@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib)
end
end
@@ -98,12 +101,11 @@ class Chef
opts = {:name => @new_resource.username}
field_list = {
- 'comment' => 'full_name',
- 'home' => 'home_dir',
- 'gid' => 'primary_group_id',
- 'uid' => 'user_id',
- 'shell' => 'script_path',
- 'password' => 'password'
+ "comment" => "full_name",
+ "home" => "home_dir",
+ "uid" => "user_id",
+ "shell" => "script_path",
+ "password" => "password",
}
field_list.sort{ |a,b| a[0] <=> b[0] }.each do |field, option|
diff --git a/lib/chef/provider/windows_script.rb b/lib/chef/provider/windows_script.rb
index e600bb2837..342158fa06 100644
--- a/lib/chef/provider/windows_script.rb
+++ b/lib/chef/provider/windows_script.rb
@@ -16,18 +16,20 @@
# limitations under the License.
#
-require 'chef/provider/script'
-require 'chef/mixin/windows_architecture_helper'
+require "chef/provider/script"
+require "chef/mixin/windows_architecture_helper"
class Chef
class Provider
class WindowsScript < Chef::Provider::Script
+ attr_reader :is_forced_32bit
+
protected
include Chef::Mixin::WindowsArchitectureHelper
- def initialize( new_resource, run_context, script_extension='')
+ def initialize( new_resource, run_context, script_extension="")
super( new_resource, run_context )
@script_extension = script_extension
@@ -36,11 +38,7 @@ class Chef
@is_wow64 = wow64_architecture_override_required?(run_context.node, target_architecture)
- # if the user wants to run the script 32 bit && we are on a 64bit windows system && we are running a 64bit ruby ==> fail
- if ( target_architecture == :i386 ) && node_windows_architecture(run_context.node) == :x86_64 && !is_i386_process_on_x86_64_windows?
- raise Chef::Exceptions::Win32ArchitectureIncorrect,
- "Support for the i386 architecture from a 64-bit Ruby runtime is not yet implemented"
- end
+ @is_forced_32bit = forced_32bit_override_required?(run_context.node, target_architecture)
end
public