summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNathan Williams <nath.e.will@gmail.com>2015-10-23 22:19:25 -0700
committerNathan Williams <nath.e.will@gmail.com>2015-10-23 22:19:25 -0700
commita58c59460aa6b9cfe0824dabaf6cac0c445d0288 (patch)
treedaf8cfecdc01e45dd19bd888974b6a75913401c5 /lib
parente6b2a07634f8eef374e1edb84387d69bca3044c1 (diff)
parent8bf1304da739d4be94edb101ad9e46c96b1d4ccd (diff)
downloadchef-a58c59460aa6b9cfe0824dabaf6cac0c445d0288.tar.gz
re-sync with master
Diffstat (limited to 'lib')
-rw-r--r--lib/chef/application.rb2
-rw-r--r--lib/chef/application/apply.rb10
-rw-r--r--lib/chef/application/client.rb18
-rw-r--r--lib/chef/application/knife.rb4
-rw-r--r--lib/chef/application/solo.rb8
-rw-r--r--lib/chef/application/windows_service.rb6
-rw-r--r--lib/chef/chef_class.rb39
-rw-r--r--lib/chef/chef_fs/data_handler/client_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server_root_dir.rb3
-rw-r--r--lib/chef/chef_fs/file_system/file_system_entry.rb2
-rw-r--r--lib/chef/chef_fs/file_system/organization_members_entry.rb4
-rw-r--r--lib/chef/client.rb31
-rw-r--r--lib/chef/config.rb3
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb2
-rw-r--r--lib/chef/cookbook/remote_file_vendor.rb2
-rw-r--r--lib/chef/cookbook_site_streaming_uploader.rb2
-rw-r--r--lib/chef/cookbook_version.rb6
-rw-r--r--lib/chef/data_bag.rb2
-rw-r--r--lib/chef/data_bag_item.rb2
-rw-r--r--lib/chef/deprecation/mixin/template.rb3
-rw-r--r--lib/chef/deprecation/provider/cookbook_file.rb2
-rw-r--r--lib/chef/deprecation/provider/file.rb2
-rw-r--r--lib/chef/deprecation/provider/remote_directory.rb52
-rw-r--r--lib/chef/deprecation/provider/remote_file.rb3
-rw-r--r--lib/chef/deprecation/provider/template.rb2
-rw-r--r--lib/chef/deprecation/warnings.rb7
-rw-r--r--lib/chef/dsl/reboot_pending.rb3
-rw-r--r--lib/chef/dsl/recipe.rb5
-rw-r--r--lib/chef/dsl/resources.rb4
-rw-r--r--lib/chef/event_dispatch/base.rb18
-rw-r--r--lib/chef/event_dispatch/dispatcher.rb35
-rw-r--r--lib/chef/exceptions.rb4
-rw-r--r--lib/chef/file_access_control/unix.rb24
-rw-r--r--lib/chef/file_content_management/deploy/cp.rb4
-rw-r--r--lib/chef/file_content_management/deploy/mv_unix.rb8
-rw-r--r--lib/chef/file_content_management/deploy/mv_windows.rb2
-rw-r--r--lib/chef/file_content_management/tempfile.rb2
-rw-r--r--lib/chef/formatters/base.rb3
-rw-r--r--lib/chef/formatters/doc.rb52
-rw-r--r--lib/chef/formatters/error_inspectors/compile_error_inspector.rb34
-rw-r--r--lib/chef/http/decompressor.rb4
-rw-r--r--lib/chef/json_compat.rb1
-rw-r--r--lib/chef/knife/bootstrap.rb41
-rw-r--r--lib/chef/knife/bootstrap/client_builder.rb15
-rw-r--r--lib/chef/knife/bootstrap/templates/README.md7
-rw-r--r--lib/chef/knife/cookbook_create.rb2
-rw-r--r--lib/chef/knife/cookbook_site_download.rb2
-rw-r--r--lib/chef/knife/cookbook_site_install.rb2
-rw-r--r--lib/chef/knife/cookbook_site_share.rb12
-rw-r--r--lib/chef/knife/cookbook_site_unshare.rb4
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb16
-rw-r--r--lib/chef/knife/core/node_presenter.rb25
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb6
-rw-r--r--lib/chef/knife/node_run_list_remove.rb13
-rw-r--r--lib/chef/knife/search.rb6
-rw-r--r--lib/chef/knife/ssh.rb84
-rw-r--r--lib/chef/local_mode.rb5
-rw-r--r--lib/chef/log.rb6
-rw-r--r--lib/chef/mixin/deprecation.rb16
-rw-r--r--lib/chef/mixin/properties.rb302
-rw-r--r--lib/chef/mixin/template.rb1
-rw-r--r--lib/chef/mixin/which.rb2
-rw-r--r--lib/chef/mixin/wide_string.rb72
-rw-r--r--lib/chef/mixin/windows_env_helper.rb5
-rw-r--r--lib/chef/mixin/wstring.rb31
-rw-r--r--lib/chef/monkey_patches/webrick-utils.rb51
-rw-r--r--lib/chef/monkey_patches/win32/registry.rb72
-rw-r--r--lib/chef/node.rb102
-rw-r--r--lib/chef/node_map.rb4
-rw-r--r--lib/chef/platform/query_helpers.rb45
-rw-r--r--lib/chef/platform/service_helpers.rb52
-rw-r--r--lib/chef/policy_builder.rb9
-rw-r--r--lib/chef/policy_builder/dynamic.rb186
-rw-r--r--lib/chef/policy_builder/expand_node_object.rb46
-rw-r--r--lib/chef/policy_builder/policyfile.rb173
-rw-r--r--lib/chef/property.rb47
-rw-r--r--lib/chef/provider.rb4
-rw-r--r--lib/chef/provider/deploy.rb4
-rw-r--r--lib/chef/provider/dsc_resource.rb14
-rw-r--r--lib/chef/provider/execute.rb2
-rw-r--r--lib/chef/provider/lwrp_base.rb1
-rw-r--r--lib/chef/provider/package/openbsd.rb2
-rw-r--r--lib/chef/provider/package/rpm.rb4
-rw-r--r--lib/chef/provider/package/windows/msi.rb4
-rw-r--r--lib/chef/provider/package/yum.rb2
-rw-r--r--lib/chef/provider/powershell_script.rb26
-rw-r--r--lib/chef/provider/remote_directory.rb292
-rw-r--r--lib/chef/provider/remote_file/http.rb2
-rw-r--r--lib/chef/provider/service/redhat.rb6
-rw-r--r--lib/chef/provider/service/solaris.rb60
-rw-r--r--lib/chef/provider/subversion.rb20
-rw-r--r--lib/chef/provider/template/content.rb24
-rw-r--r--lib/chef/provider/user.rb2
-rw-r--r--lib/chef/provider/user/dscl.rb7
-rw-r--r--lib/chef/provider/user/solaris.rb36
-rw-r--r--lib/chef/provider/user/windows.rb8
-rw-r--r--lib/chef/provider_resolver.rb4
-rw-r--r--lib/chef/resource.rb392
-rw-r--r--lib/chef/resource/action_class.rb (renamed from lib/chef/resource/action_provider.rb)16
-rw-r--r--lib/chef/resource/chef_gem.rb6
-rw-r--r--lib/chef/resource/execute.rb2
-rw-r--r--lib/chef/resource/file/verification.rb2
-rw-r--r--lib/chef/resource/lwrp_base.rb12
-rw-r--r--lib/chef/resource/script.rb2
-rw-r--r--lib/chef/resource/subversion.rb5
-rw-r--r--lib/chef/resource/windows_script.rb8
-rw-r--r--lib/chef/resource_reporter.rb6
-rw-r--r--lib/chef/resource_resolver.rb6
-rw-r--r--lib/chef/rest.rb2
-rw-r--r--lib/chef/run_context.rb18
-rw-r--r--lib/chef/run_list/run_list_expansion.rb47
-rw-r--r--lib/chef/run_list/versioned_recipe_list.rb15
-rw-r--r--lib/chef/run_lock.rb51
-rw-r--r--lib/chef/search/query.rb18
-rw-r--r--lib/chef/util/diff.rb4
-rw-r--r--lib/chef/util/windows.rb32
-rw-r--r--lib/chef/util/windows/net_use.rb106
-rw-r--r--lib/chef/util/windows/net_user.rb1
-rw-r--r--lib/chef/version.rb2
-rw-r--r--lib/chef/win32/api/file.rb1
-rw-r--r--lib/chef/win32/api/net.rb161
-rw-r--r--lib/chef/win32/api/registry.rb51
-rw-r--r--lib/chef/win32/api/unicode.rb43
-rw-r--r--lib/chef/win32/crypto.rb3
-rw-r--r--lib/chef/win32/file.rb9
-rw-r--r--lib/chef/win32/mutex.rb5
-rw-r--r--lib/chef/win32/net.rb57
-rw-r--r--lib/chef/win32/registry.rb53
-rw-r--r--lib/chef/win32/security.rb2
-rw-r--r--lib/chef/win32/security/token.rb2
-rw-r--r--lib/chef/win32/unicode.rb9
131 files changed, 2476 insertions, 1083 deletions
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index 0563822ede..970544c068 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -382,7 +382,7 @@ class Chef
def emit_warnings
if Chef::Config[:chef_gem_compile_time]
- Chef::Log.deprecation "setting chef_gem_compile_time to true is deprecated"
+ Chef.log_deprecation "setting chef_gem_compile_time to true is deprecated"
end
end
diff --git a/lib/chef/application/apply.rb b/lib/chef/application/apply.rb
index 243b441119..b21b838d72 100644
--- a/lib/chef/application/apply.rb
+++ b/lib/chef/application/apply.rb
@@ -29,7 +29,7 @@ require 'chef/resources'
class Chef::Application::Apply < Chef::Application
- banner "Usage: chef-apply [RECIPE_FILE] [-e RECIPE_TEXT] [-s]"
+ banner "Usage: chef-apply [RECIPE_FILE | -e RECIPE_TEXT | -s] [OPTIONS]"
option :execute,
:short => "-e RECIPE_TEXT",
@@ -97,10 +97,16 @@ class Chef::Application::Apply < Chef::Application
:description => 'Enable whyrun mode',
:boolean => true
+ option :profile_ruby,
+ :long => "--[no-]profile-ruby",
+ :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
+ :boolean => true,
+ :default => false
+
option :color,
:long => '--[no-]color',
:boolean => true,
- :default => !Chef::Platform.windows?,
+ :default => true,
:description => "Use colored output, defaults to enabled"
option :minimal_ohai,
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 73eda81343..5fac34196d 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -2,7 +2,7 @@
# Author:: AJ Christensen (<aj@opscode.com)
# Author:: Christopher Brown (<cb@opscode.com>)
# Author:: Mark Mzyk (mmzyk@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");
@@ -55,11 +55,17 @@ class Chef::Application::Client < Chef::Application
:boolean => true,
:default => false
+ option :profile_ruby,
+ :long => "--[no-]profile-ruby",
+ :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
+ :boolean => true,
+ :default => false
+
option :color,
:long => '--[no-]color',
:boolean => true,
- :default => !Chef::Platform.windows?,
- :description => "Use colored output, defaults to false on Windows, true otherwise"
+ :default => true,
+ :description => "Use colored output, defaults to enabled"
option :log_level,
:short => "-l LEVEL",
@@ -160,6 +166,12 @@ class Chef::Application::Client < Chef::Application
:description => "Set the client key file location",
:proc => nil
+ option :named_run_list,
+ :short => "-n NAMED_RUN_LIST",
+ :long => "--named-run-list NAMED_RUN_LIST",
+ :description => "Use a policyfile's named run list instead of the default run list",
+ :default => nil
+
option :environment,
:short => '-E ENVIRONMENT',
:long => '--environment ENVIRONMENT',
diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb
index af5216ae00..d169a5dab5 100644
--- a/lib/chef/application/knife.rb
+++ b/lib/chef/application/knife.rb
@@ -44,8 +44,8 @@ class Chef::Application::Knife < Chef::Application
option :color,
:long => '--[no-]color',
:boolean => true,
- :default => !Chef::Platform.windows?,
- :description => "Use colored output, defaults to false on Windows, true otherwise"
+ :default => true,
+ :description => "Use colored output, defaults to enabled"
option :environment,
:short => "-E ENVIRONMENT",
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index 5bb2a1ceb0..4b472e9662 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@opscode.com>)
# Author:: Mark Mzyk (mmzyk@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");
@@ -52,6 +52,12 @@ class Chef::Application::Solo < Chef::Application
:boolean => true,
:default => false
+ option :profile_ruby,
+ :long => "--[no-]profile-ruby",
+ :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
+ :boolean => true,
+ :default => false
+
option :color,
:long => '--[no-]color',
:boolean => true,
diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb
index b42a01cfdb..2551582c3a 100644
--- a/lib/chef/application/windows_service.rb
+++ b/lib/chef/application/windows_service.rb
@@ -189,7 +189,11 @@ class Chef
config_params += " -c #{Chef::Config[:config_file]}" unless Chef::Config[:config_file].nil?
config_params += " -L #{Chef::Config[:log_location]}" unless Chef::Config[:log_location] == STDOUT
# Starts a new process and waits till the process exits
- result = shell_out("chef-client #{config_params}", :timeout => Chef::Config[:windows_service][:watchdog_timeout])
+ result = shell_out(
+ "chef-client #{config_params}",
+ :timeout => Chef::Config[:windows_service][:watchdog_timeout],
+ :logger => Chef::Log
+ )
Chef::Log.debug "#{result.stdout}"
Chef::Log.debug "#{result.stderr}"
rescue Mixlib::ShellOut::CommandTimeout => e
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index 458ac82467..6a0d09ec96 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -190,6 +190,45 @@ class Chef
def resource_handler_map
@resource_handler_map ||= Chef::Platform::ResourceHandlerMap.instance
end
+
+ #
+ # Emit a deprecation message.
+ #
+ # @param message The message to send.
+ # @param location The location. Defaults to the caller who called you (since
+ # generally the person who triggered the check is the one that needs to be
+ # fixed).
+ #
+ # @example
+ # Chef.deprecation("Deprecated!")
+ #
+ # @api private this will likely be removed in favor of an as-yet unwritten
+ # `Chef.log`
+ def log_deprecation(message, location=nil)
+ if !location
+ # Pick the first caller that is *not* part of the Chef gem, that's the
+ # thing the user wrote.
+ chef_gem_path = File.expand_path("../..", __FILE__)
+ caller(0..10).each do |c|
+ if !c.start_with?(chef_gem_path)
+ location = c
+ break
+ end
+ end
+ end
+ # `run_context.events` is the primary deprecation target if we're in a
+ # run. If we are not yet in a run, print to `Chef::Log`.
+ if run_context && run_context.events
+ run_context.events.deprecation(message, location)
+ else
+ Chef::Log.deprecation(message, location)
+ end
+ end
+ end
+
+ # @api private Only for test dependency injection; not evenly implemented as yet.
+ def self.path_to(path)
+ path
end
reset!
diff --git a/lib/chef/chef_fs/data_handler/client_data_handler.rb b/lib/chef/chef_fs/data_handler/client_data_handler.rb
index d81f35e861..5bcbd4e373 100644
--- a/lib/chef/chef_fs/data_handler/client_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/client_data_handler.rb
@@ -13,11 +13,13 @@ class Chef
'validator' => false,
'chef_type' => 'client'
}
+ # Handle the fact that admin/validator have changed type from string -> boolean
+ client['admin'] = (client['admin'] == 'true') if client['admin'].is_a?(String)
+ client['validator'] = (client['validator'] == 'true') if client['validator'].is_a?(String)
if entry.respond_to?(:org) && entry.org
defaults['orgname'] = entry.org
end
result = normalize_hash(client, defaults)
- # You can NOT send json_class, or it will fail
result.delete('json_class')
result
end
diff --git a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
index e3ffd644ad..a243e0ae6b 100644
--- a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
@@ -120,7 +120,8 @@ class Chef
if File.dirname(path) == '/organizations'
File.basename(path)
else
- nil
+ # In Chef 12, everything is in an org.
+ 'chef'
end
end
end
diff --git a/lib/chef/chef_fs/file_system/file_system_entry.rb b/lib/chef/chef_fs/file_system/file_system_entry.rb
index 8611aa2e0f..478631eac2 100644
--- a/lib/chef/chef_fs/file_system/file_system_entry.rb
+++ b/lib/chef/chef_fs/file_system/file_system_entry.rb
@@ -83,7 +83,7 @@ class Chef
end
def exists?
- File.exists?(file_path) && parent.can_have_child?(name, dir?)
+ File.exists?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
end
def read
diff --git a/lib/chef/chef_fs/file_system/organization_members_entry.rb b/lib/chef/chef_fs/file_system/organization_members_entry.rb
index 94393b341f..40042a9cbc 100644
--- a/lib/chef/chef_fs/file_system/organization_members_entry.rb
+++ b/lib/chef/chef_fs/file_system/organization_members_entry.rb
@@ -39,9 +39,9 @@ class Chef
members = minimize_value(_read_json)
(desired_members - members).each do |member|
begin
- rest.post(File.join(api_path, member), {})
+ rest.post(api_path, 'username' => member)
rescue Net::HTTPServerException => e
- if e.response.code == '404'
+ if %w(404 405).include?(e.response.code)
raise "Chef server at #{api_path} does not allow you to directly add members. Please either upgrade your Chef server or move the users you want into invitations.json instead of members.json."
else
raise
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 621ce3d489..b2a00a7d01 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -232,6 +232,8 @@ class Chef
# @return Always returns true.
#
def run
+ start_profiling
+
run_error = nil
runlock = RunLock.new(Chef::Config.lockfile)
@@ -271,7 +273,7 @@ class Chef
if Chef::Config[:why_run] == true
# why_run should probably be renamed to why_converge
- Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes")
+ Chef::Log.debug("Not running controls in 'why-run' mode - this mode is used to see potential converge changes")
elsif Chef::Config[:audit_mode] != :disabled
audit_error = run_audits(run_context)
end
@@ -284,6 +286,9 @@ class Chef
run_completed_successfully
events.run_completed(node)
+ # keep this inside the main loop to get exception backtraces
+ end_profiling
+
# rebooting has to be the last thing we do, no exceptions.
Chef::Platform::Rebooter.reboot_if_needed!(node)
rescue Exception => run_error
@@ -496,7 +501,7 @@ class Chef
# @api private
#
def policy_builder
- @policy_builder ||= Chef::PolicyBuilder.strategy.new(node_name, ohai.data, json_attribs, override_runlist, events)
+ @policy_builder ||= Chef::PolicyBuilder::Dynamic.new(node_name, ohai.data, json_attribs, override_runlist, events)
end
#
@@ -891,6 +896,28 @@ class Chef
attr_reader :override_runlist
attr_reader :specific_recipes
+ def profiling_prereqs!
+ require 'ruby-prof'
+ rescue LoadError
+ raise "You must have the ruby-prof gem installed in order to use --profile-ruby"
+ end
+
+ def start_profiling
+ return unless Chef::Config[:profile_ruby]
+ profiling_prereqs!
+ RubyProf.start
+ end
+
+ def end_profiling
+ return unless Chef::Config[:profile_ruby]
+ profiling_prereqs!
+ path = Chef::FileCache.create_cache_path("graph_profile.out", false)
+ File.open(path, "w+") do |file|
+ RubyProf::GraphPrinter.new(RubyProf.stop).print(file, {})
+ end
+ Chef::Log.warn("Ruby execution profile dumped to #{path}")
+ end
+
def empty_directory?(path)
!File.exists?(path) || (Dir.entries(path).size <= 2)
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 6382af14c2..a43985f691 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -55,7 +55,8 @@ class Chef
default :event_loggers do
evt_loggers = []
- if ChefConfig.windows? and not Chef::Platform.windows_server_2003?
+ if ChefConfig.windows? && !(Chef::Platform.windows_server_2003? ||
+ Chef::Platform.windows_nano_server?)
evt_loggers << :win_evt
end
evt_loggers
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index bcbfcbeec8..dbccdbc0a8 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -91,7 +91,7 @@ class Chef
remove_ignored_files
if empty?
- Chef::Log.warn "found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
+ Chef::Log.warn "Found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
end
@cookbook_settings
end
diff --git a/lib/chef/cookbook/remote_file_vendor.rb b/lib/chef/cookbook/remote_file_vendor.rb
index 9d895b168e..7868430227 100644
--- a/lib/chef/cookbook/remote_file_vendor.rb
+++ b/lib/chef/cookbook/remote_file_vendor.rb
@@ -69,7 +69,7 @@ class Chef
Chef::FileCache.move_to(raw_file.path, cache_filename)
else
Chef::Log.debug("Not fetching #{cache_filename}, as the cache is up to date.")
- Chef::Log.debug("current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})")
+ Chef::Log.debug("Current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})")
end
full_path_cache_filename = Chef::FileCache.load(cache_filename, false)
diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb
index 0302a51165..2be189e942 100644
--- a/lib/chef/cookbook_site_streaming_uploader.rb
+++ b/lib/chef/cookbook_site_streaming_uploader.rb
@@ -26,7 +26,7 @@ require 'openssl'
class Chef
# == Chef::CookbookSiteStreamingUploader
# A streaming multipart HTTP upload implementation. Used to upload cookbooks
- # (in tarball form) to http://cookbooks.opscode.com
+ # (in tarball form) to https://supermarket.chef.io
#
# inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
class CookbookSiteStreamingUploader
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 8d302eeec2..3cdfd8c10b 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -51,12 +51,12 @@ class Chef
attr_accessor :metadata_filenames
def status=(new_status)
- Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+ Chef.log_deprecation("Deprecated method `status' called. This method will be removed.")
@status = new_status
end
def status
- Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+ Chef.log_deprecation("Deprecated method `status' called. This method will be removed.")
@status
end
@@ -480,7 +480,7 @@ class Chef
# @deprecated This method was used by the Ruby Chef Server and is no longer
# needed. There is no replacement.
def generate_manifest_with_urls(&url_generator)
- Chef::Log.deprecation("Deprecated method #generate_manifest_with_urls called from #{caller(1).first}")
+ Chef.log_deprecation("Deprecated method #generate_manifest_with_urls.")
rendered_manifest = manifest.dup
COOKBOOK_SEGMENTS.each do |segment|
diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb
index 8475774fa1..401ba6f63f 100644
--- a/lib/chef/data_bag.rb
+++ b/lib/chef/data_bag.rb
@@ -144,7 +144,7 @@ class Chef
def save
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In whyrun mode, so NOT performing data bag save.")
+ Chef::Log.warn("In why-run mode, so NOT performing data bag save.")
else
create
end
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index 9f92e26c50..31c9b69330 100644
--- a/lib/chef/data_bag_item.rb
+++ b/lib/chef/data_bag_item.rb
@@ -170,7 +170,7 @@ class Chef
r = chef_server_rest
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In whyrun mode, so NOT performing data bag item save.")
+ Chef::Log.warn("In why-run mode, so NOT performing data bag item save.")
else
r.put_rest("data/#{data_bag}/#{item_id}", self)
end
diff --git a/lib/chef/deprecation/mixin/template.rb b/lib/chef/deprecation/mixin/template.rb
index 36d18ad90d..58a661c4bd 100644
--- a/lib/chef/deprecation/mixin/template.rb
+++ b/lib/chef/deprecation/mixin/template.rb
@@ -25,7 +25,7 @@ class Chef
# == Deprecation::Provider::Mixin::Template
# This module contains the deprecated functions of
# Chef::Mixin::Template. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module Template
@@ -46,4 +46,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/deprecation/provider/cookbook_file.rb b/lib/chef/deprecation/provider/cookbook_file.rb
index dfbf4a39a4..92f5ce3623 100644
--- a/lib/chef/deprecation/provider/cookbook_file.rb
+++ b/lib/chef/deprecation/provider/cookbook_file.rb
@@ -24,7 +24,7 @@ class Chef
# == Deprecation::Provider::CookbookFile
# This module contains the deprecated functions of
# Chef::Provider::CookbookFile. These functions are refactored to
- # different components. They are frozen and will be removed in Chef 12.
+ # different components. They are frozen and will be removed in Chef 13.
#
module CookbookFile
diff --git a/lib/chef/deprecation/provider/file.rb b/lib/chef/deprecation/provider/file.rb
index 125f31fe10..31038ab3d8 100644
--- a/lib/chef/deprecation/provider/file.rb
+++ b/lib/chef/deprecation/provider/file.rb
@@ -25,7 +25,7 @@ class Chef
# == Deprecation::Provider::File
# This module contains the deprecated functions of
# Chef::Provider::File. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module File
diff --git a/lib/chef/deprecation/provider/remote_directory.rb b/lib/chef/deprecation/provider/remote_directory.rb
new file mode 100644
index 0000000000..cc8026ec55
--- /dev/null
+++ b/lib/chef/deprecation/provider/remote_directory.rb
@@ -0,0 +1,52 @@
+#
+# Author:: Serdar Sutay (<serdar@opscode.com>)
+# Copyright:: Copyright (c) 2013-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.
+#
+
+class Chef
+ module Deprecation
+ module Provider
+ module RemoteDirectory
+
+ def directory_root_in_cookbook_cache
+ Chef.log_deprecation "the Chef::Provider::RemoteDirectory#directory_root_in_cookbook_cache method is deprecated"
+
+ @directory_root_in_cookbook_cache ||=
+ begin
+ cookbook = run_context.cookbook_collection[resource_cookbook]
+ cookbook.preferred_filename_on_disk_location(node, :files, source, path)
+ end
+ end
+
+ # 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 = files.reject do |name|
+ basename = Pathname.new(name).basename().to_s
+ ['.', '..'].include?(basename)
+ end
+
+ # Clean all the paths... this is required because of the join
+ files.map {|f| Chef::Util::PathHelper.cleanpath(f)}
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/deprecation/provider/remote_file.rb b/lib/chef/deprecation/provider/remote_file.rb
index 4452de67cd..c06a5cc695 100644
--- a/lib/chef/deprecation/provider/remote_file.rb
+++ b/lib/chef/deprecation/provider/remote_file.rb
@@ -23,7 +23,7 @@ class Chef
# == Deprecation::Provider::RemoteFile
# This module contains the deprecated functions of
# Chef::Provider::RemoteFile. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module RemoteFile
@@ -83,4 +83,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/deprecation/provider/template.rb b/lib/chef/deprecation/provider/template.rb
index d7a228e97a..34e5f54b7e 100644
--- a/lib/chef/deprecation/provider/template.rb
+++ b/lib/chef/deprecation/provider/template.rb
@@ -25,7 +25,7 @@ class Chef
# == Deprecation::Provider::Template
# This module contains the deprecated functions of
# Chef::Provider::Template. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module Template
diff --git a/lib/chef/deprecation/warnings.rb b/lib/chef/deprecation/warnings.rb
index 34f468ff53..0b1ec2d5ed 100644
--- a/lib/chef/deprecation/warnings.rb
+++ b/lib/chef/deprecation/warnings.rb
@@ -25,10 +25,9 @@ class Chef
m = instance_method(name)
define_method(name) do |*args|
message = []
- message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 12."
- message << "Please update your cookbooks accordingly. Accessed from:"
- caller[0..3].each {|l| message << l}
- Chef::Log.deprecation message
+ message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 13."
+ message << "Please update your cookbooks accordingly."
+ Chef.log_deprecation(message)
super(*args)
end
end
diff --git a/lib/chef/dsl/reboot_pending.rb b/lib/chef/dsl/reboot_pending.rb
index c577118dd4..3d84b29ec5 100644
--- a/lib/chef/dsl/reboot_pending.rb
+++ b/lib/chef/dsl/reboot_pending.rb
@@ -49,7 +49,8 @@ class Chef
# The mere existence of the UpdateExeVolatile key should indicate a pending restart for certain updates
# http://support.microsoft.com/kb/832475
- (registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') &&
+ Chef::Platform.windows_server_2003? &&
+ (registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') &&
!registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0].nil? &&
[1,2,3].include?(registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0][:data]))
elsif platform?("ubuntu")
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb
index 29cfcd478c..26c0ec6768 100644
--- a/lib/chef/dsl/recipe.rb
+++ b/lib/chef/dsl/recipe.rb
@@ -140,8 +140,7 @@ class Chef
# method_missing manually. Not a fan. Not. A. Fan.
#
if respond_to?(method_symbol)
- Chef::Log.deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13.")
- Chef::Log.deprecation("Use public_send() or send() instead.")
+ Chef.log_deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13. Use public_send() or send() instead.")
return send(method_symbol, *args, &block)
end
@@ -150,7 +149,7 @@ class Chef
# never called. DEPRECATED.
#
if run_context.definitions.has_key?(method_symbol.to_sym)
- Chef::Log.deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
+ Chef.log_deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
Chef::DSL::Definitions.add_definition(method_symbol)
return send(method_symbol, *args, &block)
end
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index f15beaeab0..49588ed516 100644
--- a/lib/chef/dsl/resources.rb
+++ b/lib/chef/dsl/resources.rb
@@ -11,14 +11,14 @@ class Chef
begin
module_eval(<<-EOM, __FILE__, __LINE__+1)
def #{dsl_name}(*args, &block)
- Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
+ Chef.log_deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
declare_resource(#{dsl_name.inspect}, args[0], caller[0], &block)
end
EOM
rescue SyntaxError
# Handle the case where dsl_name has spaces, etc.
define_method(dsl_name.to_sym) do |*args, &block|
- Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
+ Chef.log_deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
declare_resource(dsl_name, args[0], caller[0], &block)
end
end
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 0ae5101029..585a3db174 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -47,14 +47,19 @@ class Chef
def ohai_completed(node)
end
- # Already have a client key, assuming this node has registered.
+ # Announce that we're not going to register the client. Generally because
+ # we already have the private key, or because we're deliberately not using
+ # a key.
def skipping_registration(node_name, config)
end
- # About to attempt to register as +node_name+
+ # About to attempt to create a private key registered to the server with
+ # client +node_name+.
def registration_start(node_name, config)
end
+ # Successfully created the private key and registered this client with the
+ # server.
def registration_completed
end
@@ -340,7 +345,6 @@ class Chef
def resource_completed(resource)
end
-
# A stream has opened.
def stream_opened(stream, options = {})
end
@@ -376,8 +380,12 @@ class Chef
def whyrun_assumption(action, resource, message)
end
- ## TODO: deprecation warning. this way we can queue them up and present
- # them all at once.
+ # Emit a message about something being deprecated.
+ def deprecation(message, location=caller(2..2)[0])
+ end
+
+ def run_list_expanded(run_list_expansion)
+ end
# An uncategorized message. This supports the case that a user needs to
# pass output that doesn't fit into one of the callbacks above. Note that
diff --git a/lib/chef/event_dispatch/dispatcher.rb b/lib/chef/event_dispatch/dispatcher.rb
index 9e17d78507..f3e55539a9 100644
--- a/lib/chef/event_dispatch/dispatcher.rb
+++ b/lib/chef/event_dispatch/dispatcher.rb
@@ -25,28 +25,33 @@ class Chef
# define the forwarding in one go:
#
- # Define a method that will be forwarded to all
- def self.def_forwarding_method(method_name)
- define_method(method_name) do |*args|
- @subscribers.each do |s|
- # Skip new/unsupported event names.
- if s.respond_to?(method_name)
- mth = s.method(method_name)
- # Anything with a *args is arity -1, so use all arguments.
- arity = mth.arity < 0 ? args.length : mth.arity
- # Trim arguments to match what the subscriber expects to allow
- # adding new arguments without breaking compat.
- mth.call(*args.take(arity))
- end
+ def call_subscribers(method_name, *args)
+ @subscribers.each do |s|
+ # Skip new/unsupported event names.
+ next if !s.respond_to?(method_name)
+ mth = s.method(method_name)
+ # Trim arguments to match what the subscriber expects to allow
+ # adding new arguments without breaking compat.
+ if mth.arity < args.size && mth.arity >= 0
+ mth.call(*args.take(mth.arity))
+ else
+ mth.call(*args)
end
end
end
(Base.instance_methods - Object.instance_methods).each do |method_name|
- def_forwarding_method(method_name)
+ class_eval <<-EOM
+ def #{method_name}(*args)
+ call_subscribers(#{method_name.inspect}, *args)
+ end
+ EOM
end
+ # Special case deprecation, since it needs to know its caller
+ def deprecation(message, location=caller(2..2)[0])
+ call_subscribers(:deprecation, message, location)
+ end
end
end
end
-
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index e3649c068b..6e7ff2e24a 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -105,7 +105,7 @@ class Chef
class VerificationNotFound < RuntimeError; end
class InvalidEventType < ArgumentError; end
class MultipleIdentityError < RuntimeError; end
- # Used in Resource::ActionProvider#load_current_resource to denote that
+ # Used in Resource::ActionClass#load_current_resource to denote that
# the resource doesn't actually exist (for example, the file does not exist)
class CurrentValueDoesNotExist < RuntimeError; end
@@ -116,6 +116,8 @@ class Chef
end
end
+ class InvalidPolicybuilderCall < ArgumentError; end
+
class InvalidResourceSpecification < ArgumentError; end
class SolrConnectionError < RuntimeError; end
class IllegalChecksumRevert < RuntimeError; end
diff --git a/lib/chef/file_access_control/unix.rb b/lib/chef/file_access_control/unix.rb
index c53d832414..8178d5fbf2 100644
--- a/lib/chef/file_access_control/unix.rb
+++ b/lib/chef/file_access_control/unix.rb
@@ -79,18 +79,18 @@ class Chef
def should_update_owner?
if target_uid.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("found target_uid == nil, so no owner was specified on resource, not managing owner")
+ Chef::Log.debug("Found target_uid == nil, so no owner was specified on resource, not managing owner")
return false
elsif current_uid.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("found current_uid == nil, so we are creating a new file, updating owner")
+ Chef::Log.debug("Found current_uid == nil, so we are creating a new file, updating owner")
return true
elsif target_uid != current_uid
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("found target_uid != current_uid, updating owner")
+ Chef::Log.debug("Found target_uid != current_uid, updating owner")
return true
else
- Chef::Log.debug("found target_uid == current_uid, not updating owner")
+ Chef::Log.debug("Found target_uid == current_uid, not updating owner")
# the user has specified a permission, but it matches the file, so behave idempotently
return false
end
@@ -138,18 +138,18 @@ class Chef
def should_update_group?
if target_gid.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("found target_gid == nil, so no group was specified on resource, not managing group")
+ Chef::Log.debug("Found target_gid == nil, so no group was specified on resource, not managing group")
return false
elsif current_gid.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("found current_gid == nil, so we are creating a new file, updating group")
+ Chef::Log.debug("Found current_gid == nil, so we are creating a new file, updating group")
return true
elsif target_gid != current_gid
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("found target_gid != current_gid, updating group")
+ Chef::Log.debug("Found target_gid != current_gid, updating group")
return true
else
- Chef::Log.debug("found target_gid == current_gid, not updating group")
+ Chef::Log.debug("Found target_gid == current_gid, not updating group")
# the user has specified a permission, but it matches the file, so behave idempotently
return false
end
@@ -187,20 +187,20 @@ class Chef
def should_update_mode?
if target_mode.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("found target_mode == nil, so no mode was specified on resource, not managing mode")
+ Chef::Log.debug("Found target_mode == nil, so no mode was specified on resource, not managing mode")
return false
elsif current_mode.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("found current_mode == nil, so we are creating a new file, updating mode")
+ Chef::Log.debug("Found current_mode == nil, so we are creating a new file, updating mode")
return true
elsif target_mode != current_mode
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("found target_mode != current_mode, updating mode")
+ Chef::Log.debug("Found target_mode != current_mode, updating mode")
return true
elsif suid_bit_set? and (should_update_group? or should_update_owner?)
return true
else
- Chef::Log.debug("found target_mode == current_mode, not updating mode")
+ Chef::Log.debug("Found target_mode == current_mode, not updating mode")
# the user has specified a permission, but it matches the file, so behave idempotently
return false
end
diff --git a/lib/chef/file_content_management/deploy/cp.rb b/lib/chef/file_content_management/deploy/cp.rb
index c6b1d6cd11..ea378c2e5d 100644
--- a/lib/chef/file_content_management/deploy/cp.rb
+++ b/lib/chef/file_content_management/deploy/cp.rb
@@ -34,12 +34,12 @@ class Chef
#
class Cp
def create(file)
- Chef::Log.debug("touching #{file} to create it")
+ Chef::Log.debug("Touching #{file} to create it")
FileUtils.touch(file)
end
def deploy(src, dst)
- Chef::Log.debug("copying temporary file #{src} into place at #{dst}")
+ Chef::Log.debug("Copying temporary file #{src} into place at #{dst}")
FileUtils.cp(src, dst)
end
end
diff --git a/lib/chef/file_content_management/deploy/mv_unix.rb b/lib/chef/file_content_management/deploy/mv_unix.rb
index 758c594e50..9712486424 100644
--- a/lib/chef/file_content_management/deploy/mv_unix.rb
+++ b/lib/chef/file_content_management/deploy/mv_unix.rb
@@ -30,19 +30,19 @@ class Chef
def create(file)
# this is very simple, but it ensures that ownership and file modes take
# good defaults, in particular mode needs to obey umask on create
- Chef::Log.debug("touching #{file} to create it")
+ Chef::Log.debug("Touching #{file} to create it")
FileUtils.touch(file)
end
def deploy(src, dst)
# we are only responsible for content so restore the dst files perms
- Chef::Log.debug("reading modes from #{dst} file")
+ Chef::Log.debug("Reading modes from #{dst} file")
stat = ::File.stat(dst)
mode = stat.mode & 07777
uid = stat.uid
gid = stat.gid
- Chef::Log.debug("applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
+ Chef::Log.debug("Applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
# i own the inode, so should be able to at least chmod it
::File.chmod(mode, src)
@@ -67,7 +67,7 @@ class Chef
Chef::Log.warn("Could not set gid = #{gid} on #{src}, file modes not preserved")
end
- Chef::Log.debug("moving temporary file #{src} into place at #{dst}")
+ Chef::Log.debug("Moving temporary file #{src} into place at #{dst}")
FileUtils.mv(src, dst)
end
end
diff --git a/lib/chef/file_content_management/deploy/mv_windows.rb b/lib/chef/file_content_management/deploy/mv_windows.rb
index 0d16da9717..e2951dba4c 100644
--- a/lib/chef/file_content_management/deploy/mv_windows.rb
+++ b/lib/chef/file_content_management/deploy/mv_windows.rb
@@ -35,7 +35,7 @@ class Chef
ACL = Security::ACL
def create(file)
- Chef::Log.debug("touching #{file} to create it")
+ Chef::Log.debug("Touching #{file} to create it")
FileUtils.touch(file)
end
diff --git a/lib/chef/file_content_management/tempfile.rb b/lib/chef/file_content_management/tempfile.rb
index 2dde0ce21b..6e1624f9a4 100644
--- a/lib/chef/file_content_management/tempfile.rb
+++ b/lib/chef/file_content_management/tempfile.rb
@@ -49,7 +49,7 @@ class Chef
end
end
- raise Chef::Exceptions::FileContentStagingError(errors) if tf.nil?
+ raise Chef::Exceptions::FileContentStagingError, errors if tf.nil?
# We always process the tempfile in binmode so that we
# preserve the line endings of the content.
diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb
index c901068aa0..d3756ef00c 100644
--- a/lib/chef/formatters/base.rb
+++ b/lib/chef/formatters/base.rb
@@ -212,6 +212,9 @@ class Chef
file_load_failed(path, exception)
end
+ def deprecation(message, location=caller(2..2)[0])
+ Chef::Log.deprecation("#{message} at #{location}")
+ end
end
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index a5d7e210c5..70108f547b 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -29,6 +29,18 @@ class Chef
end_time - start_time
end
+ def pretty_elapsed_time
+ time = elapsed_time
+ if time < 60 then
+ message = Time.at(time).utc.strftime("%S seconds")
+ elsif time < 3600 then
+ message = Time.at(time).utc.strftime("%M minutes %S seconds")
+ else
+ message = Time.at(time).utc.strftime("%H hours %M minutes %S seconds")
+ end
+ message
+ end
+
def run_start(version)
puts_line "Starting Chef Client, version #{version}"
end
@@ -43,10 +55,30 @@ class Chef
def run_completed(node)
@end_time = Time.now
+ # Print out deprecations.
+ if !deprecations.empty?
+ puts_line ""
+ puts_line "Deprecated features used!"
+ deprecations.each do |message, locations|
+ if locations.size == 1
+ puts_line " #{message} at #{locations.size} location:"
+ else
+ puts_line " #{message} at #{locations.size} locations:"
+ end
+ locations.each do |location|
+ prefix = " - "
+ Array(location).each do |line|
+ puts_line "#{prefix}#{line}"
+ prefix = " "
+ end
+ end
+ end
+ puts_line ""
+ end
if Chef::Config[:why_run]
puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated"
else
- puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{elapsed_time} seconds"
+ puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{pretty_elapsed_time}"
if total_audits > 0
puts_line " #{successful_audits}/#{total_audits} controls succeeded"
end
@@ -58,7 +90,7 @@ class Chef
if Chef::Config[:why_run]
puts_line "Chef Client failed. #{@updated_resources} resources would have been updated"
else
- puts_line "Chef Client failed. #{@updated_resources} resources updated in #{elapsed_time} seconds"
+ puts_line "Chef Client failed. #{@updated_resources} resources updated in #{pretty_elapsed_time}"
if total_audits > 0
puts_line " #{successful_audits} controls succeeded"
end
@@ -336,6 +368,16 @@ class Chef
end
end
+ def deprecation(message, location=caller(2..2)[0])
+ if Chef::Config[:treat_deprecation_warnings_as_errors]
+ super
+ end
+
+ # Save deprecations to the screen until the end
+ deprecations[message] ||= Set.new
+ deprecations[message] << location
+ end
+
def indent
indent_by(2)
end
@@ -343,6 +385,12 @@ class Chef
def unindent
indent_by(-2)
end
+
+ protected
+
+ def deprecations
+ @deprecations ||= {}
+ end
end
end
end
diff --git a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
index fe418ed485..621fadce40 100644
--- a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
@@ -47,11 +47,31 @@ class Chef
if exception_message_modifying_frozen?
msg = <<-MESSAGE
- Chef calls the freeze method on certain ruby objects to prevent
- pollution across multiple instances. Specifically, resource
- properties have frozen default values to avoid modifying the
- property for all instances of a resource. Try modifying the
- particular instance variable or using an instance accessor instead.
+ Ruby objects are often frozen to prevent further modifications
+ when they would negatively impact the process (e.g. values inside
+ Ruby's ENV class) or to prevent polluting other objects when default
+ values are passed by reference to many instances of an object (e.g.
+ the empty Array as a Chef resource default, passed by reference
+ to every instance of the resource).
+
+ Chef uses Object#freeze to ensure the default values of properties
+ inside Chef resources are not modified, so that when a new instance
+ of a Chef resource is created, and Object#dup copies values by
+ reference, the new resource is not receiving a default value that
+ has been by a previous instance of that resource.
+
+ Instead of modifying an object that contains a default value for all
+ instances of a Chef resource, create a new object and assign it to
+ the resource's parameter, e.g.:
+
+ fruit_basket = resource(:fruit_basket, 'default')
+
+ # BAD: modifies 'contents' object for all new fruit_basket instances
+ fruit_basket.contents << 'apple'
+
+ # GOOD: allocates new array only owned by this fruit_basket instance
+ fruit_basket.contents %w(apple)
+
MESSAGE
error_description.section("Additional information:", msg.gsub(/^ {6}/, ''))
@@ -88,7 +108,7 @@ class Chef
def culprit_backtrace_entry
@culprit_backtrace_entry ||= begin
bt_entry = filtered_bt.first
- Chef::Log.debug("backtrace entry for compile error: '#{bt_entry}'")
+ Chef::Log.debug("Backtrace entry for compile error: '#{bt_entry}'")
bt_entry
end
end
@@ -118,7 +138,7 @@ class Chef
begin
filters = Array(Chef::Config.cookbook_path).map {|p| /^#{Regexp.escape(p)}/i }
r = exception.backtrace.select {|line| filters.any? {|filter| line =~ filter }}
- Chef::Log.debug("filtered backtrace of compile error: #{r.join(",")}")
+ Chef::Log.debug("Filtered backtrace of compile error: #{r.join(",")}")
r
end
end
diff --git a/lib/chef/http/decompressor.rb b/lib/chef/http/decompressor.rb
index e1d776da60..a61f510e7d 100644
--- a/lib/chef/http/decompressor.rb
+++ b/lib/chef/http/decompressor.rb
@@ -79,10 +79,10 @@ class Chef
else
case response[CONTENT_ENCODING]
when GZIP
- Chef::Log.debug "decompressing gzip response"
+ Chef::Log.debug "Decompressing gzip response"
Zlib::Inflate.new(Zlib::MAX_WBITS + 16).inflate(response.body)
when DEFLATE
- Chef::Log.debug "decompressing deflate response"
+ Chef::Log.debug "Decompressing deflate response"
Zlib::Inflate.inflate(response.body)
else
response.body
diff --git a/lib/chef/json_compat.rb b/lib/chef/json_compat.rb
index d0b3b4c7f8..5e9f29a60b 100644
--- a/lib/chef/json_compat.rb
+++ b/lib/chef/json_compat.rb
@@ -41,6 +41,7 @@ class Chef
CHEF_RESOURCECOLLECTION = "Chef::ResourceCollection".freeze
CHEF_RESOURCESET = "Chef::ResourceCollection::ResourceSet".freeze
CHEF_RESOURCELIST = "Chef::ResourceCollection::ResourceList".freeze
+ CHEF_RUNLISTEXPANSION = "Chef::RunListExpansion".freeze
class <<self
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index 5b29591fcc..93236225a2 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -143,6 +143,22 @@ class Chef
:proc => lambda { |o| o.split(/[\s,]+/) },
:default => []
+ option :policy_name,
+ :long => "--policy-name POLICY_NAME",
+ :description => "Policyfile name to use (--policy-group must also be given)",
+ :default => nil
+
+ option :policy_group,
+ :long => "--policy-group POLICY_GROUP",
+ :description => "Policy group name to use (--policy-name must also be given)",
+ :default => nil
+
+ option :tags,
+ :long => "--tags TAGS",
+ :description => "Comma separated list of tags to apply to the node",
+ :proc => lambda { |o| o.split(/[\s,]+/) },
+ :default => []
+
option :first_boot_attributes,
:short => "-j JSON_ATTRIBS",
:long => "--json-attributes",
@@ -309,6 +325,7 @@ class Chef
def run
validate_name_args!
+ validate_options!
$stdout.sync = true
@@ -357,6 +374,17 @@ class Chef
end
end
+ def validate_options!
+ if incomplete_policyfile_options?
+ ui.error("--policy-name and --policy-group must be specified together")
+ exit 1
+ elsif policyfile_and_run_list_given?
+ ui.error("Policyfile options and --run-list are exclusive")
+ exit 1
+ end
+ true
+ end
+
def knife_ssh
ssh = Chef::Knife::Ssh.new
ssh.ui = ui
@@ -389,6 +417,19 @@ class Chef
command
end
+
+ private
+
+ # True if policy_name and run_list are both given
+ def policyfile_and_run_list_given?
+ !config[:run_list].empty? && !!config[:policy_name]
+ end
+
+ # True if one of policy_name or policy_group was given, but not both
+ def incomplete_policyfile_options?
+ (!!config[:policy_name] ^ config[:policy_group])
+ end
+
end
end
end
diff --git a/lib/chef/knife/bootstrap/client_builder.rb b/lib/chef/knife/bootstrap/client_builder.rb
index 304b06b8b7..7eb1e22628 100644
--- a/lib/chef/knife/bootstrap/client_builder.rb
+++ b/lib/chef/knife/bootstrap/client_builder.rb
@@ -91,6 +91,16 @@ class Chef
knife_config[:run_list]
end
+ # @return [String] policy_name from the knife_config
+ def policy_name
+ knife_config[:policy_name]
+ end
+
+ # @return [String] policy_group from the knife_config
+ def policy_group
+ knife_config[:policy_group]
+ end
+
# @return [Hash,Array] Object representation of json first-boot attributes from the knife_config
def first_boot_attributes
knife_config[:first_boot_attributes]
@@ -141,6 +151,11 @@ class Chef
node.run_list(normalized_run_list)
node.normal_attrs = first_boot_attributes if first_boot_attributes
node.environment(environment) if environment
+ node.policy_name = policy_name if policy_name
+ node.policy_group = policy_group if policy_group
+ (knife_config[:tags] || []).each do |tag|
+ node.tags << tag
+ end
node
end
end
diff --git a/lib/chef/knife/bootstrap/templates/README.md b/lib/chef/knife/bootstrap/templates/README.md
index 13a0fe7ada..b5bca25d8e 100644
--- a/lib/chef/knife/bootstrap/templates/README.md
+++ b/lib/chef/knife/bootstrap/templates/README.md
@@ -1,12 +1,11 @@
This directory contains bootstrap templates which can be used with the -d flag
to 'knife bootstrap' to install Chef in different ways. To simplify installation,
and reduce the matrix of common installation patterns to support, we have
-standardized on the [Omnibus](https://github.com/opscode/omnibus-ruby) built installation
+standardized on the [Omnibus](https://github.com/chef/omnibus) built installation
packages.
The 'chef-full' template downloads a script which is used to determine the correct
-Omnibus package for this system from the [Omnitruck](https://github.com/opscode/opscode-omnitruck) API. All other templates in this directory are deprecated and will be removed
-in the future.
+Omnibus package for this system from the [Omnitruck](https://docs.chef.io/api_omnitruck.html) API.
You can still utilize custom bootstrap templates on your system if your installation
-needs are unique. Additional information can be found on the [docs site](http://docs.opscode.com/knife_bootstrap.html#custom-templates). \ No newline at end of file
+needs are unique. Additional information can be found on the [docs site](https://docs.chef.io/knife_bootstrap.html#custom-templates).
diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb
index e17a54079f..97f6e65d3c 100644
--- a/lib/chef/knife/cookbook_create.rb
+++ b/lib/chef/knife/cookbook_create.rb
@@ -48,7 +48,7 @@ class Chef
option :cookbook_copyright,
:short => "-C COPYRIGHT",
:long => "--copyright COPYRIGHT",
- :description => "Name of Copyright holder"
+ :description => "Name of copyright holder"
option :cookbook_email,
:short => "-m EMAIL",
diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb
index c2d72ef8da..3e586e6542 100644
--- a/lib/chef/knife/cookbook_site_download.rb
+++ b/lib/chef/knife/cookbook_site_download.rb
@@ -84,7 +84,7 @@ class Chef
end
def download_cookbook
- ui.info "Downloading #{@name_args[0]} from the cookbooks site at version #{version} to #{download_location}"
+ ui.info "Downloading #{@name_args[0]} from Supermarket at version #{version} to #{download_location}"
noauth_rest.sign_on_redirect = false
tf = noauth_rest.get_rest desired_cookbook_data["file"], true
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index d0ab6da3ef..aee8b7fa94 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -93,7 +93,7 @@ class Chef
# TODO: it'd be better to store these outside the cookbook repo and
# keep them around, e.g., in ~/Library/Caches on OS X.
- ui.info("removing downloaded tarball")
+ ui.info("Removing downloaded tarball")
File.unlink(upstream_file)
if @repo.finalize_updates_to(@cookbook_name, downloader.version)
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index efd2e7f129..beb98b71b8 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -48,7 +48,7 @@ class Chef
:short => '-n',
:boolean => true,
:default => false,
- :description => "Don't take action, only print what files will be upload to SuperMarket."
+ :description => "Don't take action, only print what files will be uploaded to Supermarket."
def run
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
@@ -94,7 +94,7 @@ class Chef
Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}")
FileUtils.rm_rf tmp_cookbook_dir
rescue => e
- ui.error("Error uploading cookbook #{cookbook_name} to the Opscode Cookbook Site: #{e.message}. Increase log verbosity (-VV) for more information.")
+ ui.error("Error uploading cookbook #{cookbook_name} to Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
Chef::Log.debug("\n#{e.backtrace.join("\n")}")
exit(1)
end
@@ -108,15 +108,15 @@ class Chef
def get_category(cookbook_name)
begin
- data = noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}")
+ data = noauth_rest.get_rest("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
if !data["category"] && data["error_code"]
- ui.fatal("Received an error from the Opscode Cookbook site: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.")
+ ui.fatal("Received an error from Supermarket: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.")
exit(1)
else
data['category']
end
rescue => e
- ui.fatal("Unable to reach Opscode Cookbook Site: #{e.message}. Increase log verbosity (-VV) for more information.")
+ ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
Chef::Log.debug("\n#{e.backtrace.join("\n")}")
exit(1)
end
@@ -136,7 +136,7 @@ class Chef
if http_resp.code.to_i != 201
if res['error_messages']
if res['error_messages'][0] =~ /Version already exists/
- ui.error "The same version of this cookbook already exists on the Opscode Cookbook Site."
+ ui.error "The same version of this cookbook already exists on Supermarket."
exit(1)
else
ui.error "#{res['error_messages'][0]}"
diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb
index b34282ec32..77bb18322c 100644
--- a/lib/chef/knife/cookbook_site_unshare.rb
+++ b/lib/chef/knife/cookbook_site_unshare.rb
@@ -38,7 +38,7 @@ class Chef
exit 1
end
- confirm "Do you really want to unshare the cookbook #{@cookbook_name}"
+ confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
begin
rest.delete_rest "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}"
@@ -48,7 +48,7 @@ class Chef
exit 1
end
- ui.info "Unshared cookbook #{@cookbook_name}"
+ ui.info "Unshared all versions of the cookbook #{@cookbook_name}"
end
end
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index 7197653489..d210b9418f 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -40,7 +40,7 @@ class Chef
end
def bootstrap_environment
- @chef_config[:environment] || '_default'
+ @chef_config[:environment]
end
def validation_key
@@ -128,7 +128,7 @@ CONFIG
client_path = @chef_config[:chef_client_path] || 'chef-client'
s = "#{client_path} -j /etc/chef/first-boot.json"
s << ' -l debug' if @config[:verbosity] and @config[:verbosity] >= 2
- s << " -E #{bootstrap_environment}"
+ s << " -E #{bootstrap_environment}" unless bootstrap_environment.nil?
s
end
@@ -163,11 +163,19 @@ CONFIG
end
def first_boot
- (@config[:first_boot_attributes] || {}).merge(:run_list => @run_list)
+ (@config[:first_boot_attributes] || {}).tap do |attributes|
+ if @config[:policy_name] && @config[:policy_group]
+ attributes.merge!(:policy_name => @config[:policy_name], :policy_group => @config[:policy_group])
+ else
+ attributes.merge!(:run_list => @run_list)
+ end
+
+ attributes.merge!(:tags => @config[:tags]) if @config[:tags] && !@config[:tags].empty?
+ end
end
private
-
+
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
# This string should contain both the commands necessary to both create the files, as well as their content
def trusted_certs_content
diff --git a/lib/chef/knife/core/node_presenter.rb b/lib/chef/knife/core/node_presenter.rb
index d1aab592ef..d9ea8c7669 100644
--- a/lib/chef/knife/core/node_presenter.rb
+++ b/lib/chef/knife/core/node_presenter.rb
@@ -67,7 +67,12 @@ class Chef
result = {}
result["name"] = node.name
- result["chef_environment"] = node.chef_environment
+ if node.policy_name.nil? && node.policy_group.nil?
+ result["chef_environment"] = node.chef_environment
+ else
+ result["policy_name"] = node.policy_name
+ result["policy_group"] = node.policy_group
+ end
result["run_list"] = node.run_list
result["normal"] = node.normal_attrs
@@ -95,11 +100,29 @@ class Chef
summarized=<<-SUMMARY
#{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)}
+SUMMARY
+ show_policy = !(node.policy_name.nil? && node.policy_group.nil?)
+ if show_policy
+ summarized << <<-POLICY
+#{key('Policy Name:')} #{node.policy_name}
+#{key('Policy Group:')} #{node.policy_group}
+POLICY
+ else
+ summarized << <<-ENV
#{key('Environment:')} #{node.chef_environment}
+ENV
+ end
+ summarized << <<-SUMMARY
#{key('FQDN:')} #{node[:fqdn]}
#{key('IP:')} #{ip}
#{key('Run List:')} #{node.run_list}
+SUMMARY
+ unless show_policy
+ summarized << <<-ROLES
#{key('Roles:')} #{Array(node[:roles]).join(', ')}
+ROLES
+ end
+ summarized << <<-SUMMARY
#{key('Recipes:')} #{Array(node[:recipes]).join(', ')}
#{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
#{key('Tags:')} #{Array(node[:tags]).join(', ')}
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
index 1d359ffd53..808e053c40 100644
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ b/lib/chef/knife/core/subcommand_loader.rb
@@ -51,7 +51,7 @@ class Chef
Chef::Log.debug("Using autogenerated hashed command manifest #{plugin_manifest_path}")
Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
elsif custom_manifest?
- Chef::Log.deprecation("Using custom manifest #{plugin_manifest_path} is deprecated. Please use a `knife rehash` autogenerated manifest instead.")
+ Chef.log_deprecation("Using custom manifest #{plugin_manifest_path} is deprecated. Please use a `knife rehash` autogenerated manifest instead.")
Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, plugin_manifest)
else
Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
@@ -84,7 +84,7 @@ class Chef
# Deprecated and un-used instance variable.
@env = env
unless env.nil?
- Chef::Log.deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
+ Chef.log_deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
end
end
@@ -149,7 +149,7 @@ class Chef
# to get in the past.
#
def subcommand_files
- Chef::Log.deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
+ Chef.log_deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
Please use Chef::Knife::SubcommandLoader.for_config(chef_config_dir, env)"
@subcommand_files ||= if Chef::Knife::SubcommandLoader.plugin_manifest?
Chef::Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, env).subcommand_files
diff --git a/lib/chef/knife/node_run_list_remove.rb b/lib/chef/knife/node_run_list_remove.rb
index 4b8953a264..ef03c176b8 100644
--- a/lib/chef/knife/node_run_list_remove.rb
+++ b/lib/chef/knife/node_run_list_remove.rb
@@ -42,7 +42,18 @@ class Chef
entries = @name_args[1].split(',').map { |e| e.strip }
end
- entries.each { |e| node.run_list.remove(e) }
+ # iterate over the list of things to remove,
+ # warning if one of them was not found
+ entries.each do |e|
+ if node.run_list.find { |rli| e == rli.to_s }
+ node.run_list.remove(e)
+ else
+ ui.warn "#{e} is not in the run list"
+ unless e =~ /^(recipe|role)\[/
+ ui.warn '(did you forget recipe[] or role[] around it?)'
+ end
+ end
+ end
node.save
diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb
index 9319d30e7d..014fc8dd87 100644
--- a/lib/chef/knife/search.rb
+++ b/lib/chef/knife/search.rb
@@ -136,7 +136,7 @@ class Chef
def read_cli_args
if config[:query]
if @name_args[1]
- ui.error "please specify query as an argument or an option via -q, not both"
+ ui.error "Please specify query as an argument or an option via -q, not both"
ui.msg opt_parser
exit 1
end
@@ -145,7 +145,7 @@ class Chef
else
case name_args.size
when 0
- ui.error "no query specified"
+ ui.error "No query specified"
ui.msg opt_parser
exit 1
when 1
@@ -160,7 +160,7 @@ class Chef
def fuzzify_query
if @query !~ /:/
- @query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*"
+ @query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}* OR policy_name:*#{@query}* OR policy_group:*#{@query}*"
end
end
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 68e01cf94f..996f40c91d 100644
--- a/lib/chef/knife/ssh.rb
+++ b/lib/chef/knife/ssh.rb
@@ -132,15 +132,19 @@ class Chef
if config[:ssh_gateway]
gw_host, gw_user = config[:ssh_gateway].split('@').reverse
gw_host, gw_port = gw_host.split(':')
- gw_opts = gw_port ? { :port => gw_port } : {}
+ gw_opts = session_options(gw_host, gw_port, gw_user)
+ user = gw_opts.delete(:user)
- session.via(gw_host, gw_user || config[:ssh_user], gw_opts)
+ begin
+ # Try to connect with a key.
+ session.via(gw_host, user, gw_opts)
+ rescue Net::SSH::AuthenticationFailed
+ prompt = "Enter the password for #{user}@#{gw_host}: "
+ gw_opts[:password] = prompt_for_password(prompt)
+ # Try again with a password.
+ session.via(gw_host, user, gw_opts)
+ end
end
- rescue Net::SSH::AuthenticationFailed
- user = gw_user || config[:ssh_user]
- prompt = "Enter the password for #{user}@#{gw_host}: "
- gw_opts.merge!(:password => prompt_for_password(prompt))
- session.via(gw_host, user, gw_opts)
end
def configure_session
@@ -204,32 +208,50 @@ class Chef
list
end
- def session_from_list(list)
- list.each do |item|
- host, ssh_port = item
- Chef::Log.debug("Adding #{host}")
- session_opts = {}
-
- ssh_config = Net::SSH.configuration_for(host)
-
+ # Net::SSH session options hash for global options. These should be
+ # options that will apply to the gateway connection in addition to the
+ # main one.
+ #
+ # @since 12.5.0
+ # @param host [String] Hostname for this session.
+ # @param port [String] SSH port for this session.
+ # @param user [String] Optional username for this session.
+ # @return [Hash<Symbol, Object>]
+ def session_options(host, port, user=nil)
+ ssh_config = Net::SSH.configuration_for(host)
+ {}.tap do |opts|
# Chef::Config[:knife][:ssh_user] is parsed in #configure_user and written to config[:ssh_user]
- user = config[:ssh_user] || ssh_config[:user]
- hostspec = user ? "#{user}@#{host}" : host
- session_opts[:keys] = File.expand_path(config[:identity_file]) if config[:identity_file]
- session_opts[:keys_only] = true if config[:identity_file]
- session_opts[:password] = config[:ssh_password] if config[:ssh_password]
- session_opts[:forward_agent] = config[:forward_agent]
- session_opts[:port] = config[:ssh_port] ||
- ssh_port || # Use cloud port if available
- Chef::Config[:knife][:ssh_port] ||
- ssh_config[:port]
- session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
-
+ opts[:user] = user || config[:ssh_user] || ssh_config[:user]
+ if config[:identity_file]
+ opts[:keys] = File.expand_path(config[:identity_file])
+ opts[:keys_only] = true
+ elsif config[:ssh_password]
+ opts[:password] = config[:ssh_password]
+ end
+ # Don't set the keys to nil if we don't have them.
+ forward_agent = config[:forward_agent] || ssh_config[:forward_agent]
+ opts[:forward_agent] = forward_agent unless forward_agent.nil?
+ port ||= ssh_config[:port]
+ opts[:port] = port unless port.nil?
+ opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
if !config[:host_key_verify]
- session_opts[:paranoid] = false
- session_opts[:user_known_hosts_file] = "/dev/null"
+ opts[:paranoid] = false
+ opts[:user_known_hosts_file] = '/dev/null'
end
+ end
+ end
+ def session_from_list(list)
+ list.each do |item|
+ host, ssh_port = item
+ Chef::Log.debug("Adding #{host}")
+ session_opts = session_options(host, ssh_port)
+ # Handle port overrides for the main connection.
+ session_opts[:port] = Chef::Config[:knife][:ssh_port] if Chef::Config[:knife][:ssh_port]
+ session_opts[:port] = config[:ssh_port] if config[:ssh_port]
+ # Create the hostspec.
+ hostspec = session_opts[:user] ? "#{session_opts.delete(:user)}@#{host}" : host
+ # Connect a new session on the multi.
session.use(hostspec, session_opts)
@longest = host.length if host.length > @longest
@@ -405,7 +427,7 @@ class Chef
begin
require 'appscript'
rescue LoadError
- STDERR.puts "you need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install"
+ STDERR.puts "You need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install"
raise
end
@@ -445,7 +467,7 @@ class Chef
session.servers_for.each do |server|
cssh_cmd << " #{server.user ? "#{server.user}@#{server.host}" : server.host}"
end
- Chef::Log.debug("starting cssh session with command: #{cssh_cmd}")
+ Chef::Log.debug("Starting cssh session with command: #{cssh_cmd}")
exec(cssh_cmd)
end
diff --git a/lib/chef/local_mode.rb b/lib/chef/local_mode.rb
index 79fb750dd8..fbb72cd6cb 100644
--- a/lib/chef/local_mode.rb
+++ b/lib/chef/local_mode.rb
@@ -15,6 +15,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
require 'chef/config'
+if Chef::Platform.windows?
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.1')
+ require 'chef/monkey_patches/webrick-utils'
+ end
+end
class Chef
module LocalMode
diff --git a/lib/chef/log.rb b/lib/chef/log.rb
index 9b27778a40..bf846c2072 100644
--- a/lib/chef/log.rb
+++ b/lib/chef/log.rb
@@ -37,7 +37,11 @@ class Chef
end
end
- def self.deprecation(msg=nil, &block)
+ def self.deprecation(msg=nil, location=caller(2..2)[0], &block)
+ if msg
+ msg << " at #{Array(location).join("\n")}"
+ msg = msg.join("") if msg.respond_to?(:join)
+ end
if Chef::Config[:treat_deprecation_warnings_as_errors]
error(msg, &block)
raise Chef::Exceptions::DeprecatedFeatureError.new(msg)
diff --git a/lib/chef/mixin/deprecation.rb b/lib/chef/mixin/deprecation.rb
index a3eacf75cb..562af541bd 100644
--- a/lib/chef/mixin/deprecation.rb
+++ b/lib/chef/mixin/deprecation.rb
@@ -102,20 +102,20 @@ class Chef
def deprecated_attr_reader(name, alternative, level=:warn)
define_method(name) do
- Chef::Log.deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
- Chef::Log.deprecation(alternative)
- Chef::Log.deprecation("Called from:")
- caller[0..3].each {|c| Chef::Log.deprecation(c)}
+ Chef.log_deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
+ Chef.log_deprecation(alternative)
+ Chef.log_deprecation("Called from:")
+ caller[0..3].each {|c| Chef.log_deprecation(c)}
instance_variable_get("@#{name}")
end
end
def deprecated_attr_writer(name, alternative, level=:warn)
define_method("#{name}=") do |value|
- Chef::Log.deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
- Chef::Log.deprecation(alternative)
- Chef::Log.deprecation("Called from:")
- caller[0..3].each {|c| Chef::Log.deprecation(c)}
+ Chef.log_deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
+ Chef.log_deprecation(alternative)
+ Chef.log_deprecation("Called from:")
+ caller[0..3].each {|c| Chef.log_deprecation(c)}
instance_variable_set("@#{name}", value)
end
end
diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb
new file mode 100644
index 0000000000..85abe4427e
--- /dev/null
+++ b/lib/chef/mixin/properties.rb
@@ -0,0 +1,302 @@
+require 'chef/delayed_evaluator'
+require 'chef/mixin/params_validate'
+require 'chef/property'
+
+class Chef
+ module Mixin
+ module Properties
+ module ClassMethods
+ #
+ # The list of properties defined on this resource.
+ #
+ # Everything defined with `property` is in this list.
+ #
+ # @param include_superclass [Boolean] `true` to include properties defined
+ # on superclasses; `false` or `nil` to return the list of properties
+ # directly on this class.
+ #
+ # @return [Hash<Symbol,Property>] The list of property names and types.
+ #
+ def properties(include_superclass=true)
+ if include_superclass
+ result = {}
+ ancestors.reverse_each { |c| result.merge!(c.properties(false)) if c.respond_to?(:properties) }
+ result
+ else
+ @properties ||= {}
+ end
+ end
+
+ #
+ # Create a property on this resource class.
+ #
+ # If a superclass has this property, or if this property has already been
+ # defined by this resource, this will *override* the previous value.
+ #
+ # @param name [Symbol] The name of the property.
+ # @param type [Object,Array<Object>] The type(s) of this property.
+ # If present, this is prepended to the `is` validation option.
+ # @param options [Hash<Symbol,Object>] Validation options.
+ # @option options [Object,Array] :is An object, or list of
+ # objects, that must match the value using Ruby's `===` operator
+ # (`options[:is].any? { |v| v === value }`).
+ # @option options [Object,Array] :equal_to An object, or list
+ # of objects, that must be equal to the value using Ruby's `==`
+ # operator (`options[:is].any? { |v| v == value }`)
+ # @option options [Regexp,Array<Regexp>] :regex An object, or
+ # list of objects, that must match the value with `regex.match(value)`.
+ # @option options [Class,Array<Class>] :kind_of A class, or
+ # list of classes, that the value must be an instance of.
+ # @option options [Hash<String,Proc>] :callbacks A hash of
+ # messages -> procs, all of which match the value. The proc must
+ # return a truthy or falsey value (true means it matches).
+ # @option options [Symbol,Array<Symbol>] :respond_to A method
+ # name, or list of method names, the value must respond to.
+ # @option options [Symbol,Array<Symbol>] :cannot_be A property,
+ # or a list of properties, that the value cannot have (such as `:nil` or
+ # `:empty`). The method with a questionmark at the end is called on the
+ # value (e.g. `value.empty?`). If the value does not have this method,
+ # it is considered valid (i.e. if you don't respond to `empty?` we
+ # assume you are not empty).
+ # @option options [Proc] :coerce A proc which will be called to
+ # transform the user input to canonical form. The value is passed in,
+ # and the transformed value returned as output. Lazy values will *not*
+ # be passed to this method until after they are evaluated. Called in the
+ # context of the resource (meaning you can access other properties).
+ # @option options [Boolean] :required `true` if this property
+ # must be present; `false` otherwise. This is checked after the resource
+ # is fully initialized.
+ # @option options [Boolean] :name_property `true` if this
+ # property defaults to the same value as `name`. Equivalent to
+ # `default: lazy { name }`, except that #property_is_set? will
+ # return `true` if the property is set *or* if `name` is set.
+ # @option options [Boolean] :name_attribute Same as `name_property`.
+ # @option options [Object] :default The value this property
+ # will return if the user does not set one. If this is `lazy`, it will
+ # be run in the context of the instance (and able to access other
+ # properties).
+ # @option options [Boolean] :desired_state `true` if this property is
+ # part of desired state. Defaults to `true`.
+ # @option options [Boolean] :identity `true` if this property
+ # is part of object identity. Defaults to `false`.
+ #
+ # @example Bare property
+ # property :x
+ #
+ # @example With just a type
+ # property :x, String
+ #
+ # @example With just options
+ # property :x, default: 'hi'
+ #
+ # @example With type and options
+ # property :x, String, default: 'hi'
+ #
+ def property(name, type=NOT_PASSED, **options)
+ name = name.to_sym
+
+ options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
+
+ options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
+ options.merge!(name: name, declared_in: self)
+
+ if type == NOT_PASSED
+ # If a type is not passed, the property derives from the
+ # superclass property (if any)
+ if properties.has_key?(name)
+ property = properties[name].derive(**options)
+ else
+ property = property_type(**options)
+ end
+
+ # If a Property is specified, derive a new one from that.
+ elsif type.is_a?(Property) || (type.is_a?(Class) && type <= Property)
+ property = type.derive(**options)
+
+ # If a primitive type was passed, combine it with "is"
+ else
+ if options[:is]
+ options[:is] = ([ type ] + [ options[:is] ]).flatten(1)
+ else
+ options[:is] = type
+ end
+ property = property_type(**options)
+ end
+
+ local_properties = properties(false)
+ local_properties[name] = property
+
+ property.emit_dsl
+ end
+
+ #
+ # Create a reusable property type that can be used in multiple properties
+ # in different resources.
+ #
+ # @param options [Hash<Symbol,Object>] Validation options. see #property for
+ # the list of options.
+ #
+ # @example
+ # property_type(default: 'hi')
+ #
+ def property_type(**options)
+ Property.derive(**options)
+ end
+
+ #
+ # Create a lazy value for assignment to a default value.
+ #
+ # @param block The block to run when the value is retrieved.
+ #
+ # @return [Chef::DelayedEvaluator] The lazy value
+ #
+ def lazy(&block)
+ DelayedEvaluator.new(&block)
+ end
+
+ #
+ # Get or set the list of desired state properties for this resource.
+ #
+ # State properties are properties that describe the desired state
+ # of the system, such as file permissions or ownership.
+ # In general, state properties are properties that could be populated by
+ # examining the state of the system (e.g., File.stat can tell you the
+ # permissions on an existing file). Contrarily, properties that are not
+ # "state properties" usually modify the way Chef itself behaves, for example
+ # by providing additional options for a package manager to use when
+ # installing a package.
+ #
+ # This list is used by the Chef client auditing system to extract
+ # information from resources to describe changes made to the system.
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties are added to state_properties by default, and can be turned off
+ # with `desired_state: false`.
+ #
+ # ```ruby
+ # property :x # part of desired state
+ # property :y, desired_state: false # not part of desired state
+ # ```
+ #
+ # @param names [Array<Symbol>] A list of property names to set as desired
+ # state.
+ #
+ # @return [Array<Property>] All properties in desired state.
+ #
+ def state_properties(*names)
+ if !names.empty?
+ names = names.map { |name| name.to_sym }.uniq
+
+ local_properties = properties(false)
+ # Add new properties to the list.
+ names.each do |name|
+ property = properties[name]
+ if !property
+ self.property name, instance_variable_name: false, desired_state: true
+ elsif !property.desired_state?
+ self.property name, desired_state: true
+ end
+ end
+
+ # If state_attrs *excludes* something which is currently desired state,
+ # mark it as desired_state: false.
+ local_properties.each do |name,property|
+ if property.desired_state? && !names.include?(name)
+ self.property name, desired_state: false
+ end
+ end
+ end
+
+ properties.values.select { |property| property.desired_state? }
+ end
+
+ #
+ # Set the identity of this resource to a particular set of properties.
+ #
+ # This drives #identity, which returns data that uniquely refers to a given
+ # resource on the given node (in such a way that it can be correlated
+ # across Chef runs).
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties can be added to identity during declaration with
+ # `identity: true`.
+ #
+ # ```ruby
+ # property :x, identity: true # part of identity
+ # property :y # not part of identity
+ # ```
+ #
+ # If no properties are marked as identity, "name" is considered the identity.
+ #
+ # @param names [Array<Symbol>] A list of property names to set as the identity.
+ #
+ # @return [Array<Property>] All identity properties.
+ #
+ def identity_properties(*names)
+ if !names.empty?
+ names = names.map { |name| name.to_sym }
+
+ # Add or change properties that are not part of the identity.
+ names.each do |name|
+ property = properties[name]
+ if !property
+ self.property name, instance_variable_name: false, identity: true
+ elsif !property.identity?
+ self.property name, identity: true
+ end
+ end
+
+ # If identity_properties *excludes* something which is currently part of
+ # the identity, mark it as identity: false.
+ properties.each do |name,property|
+ if property.identity? && !names.include?(name)
+
+ self.property name, identity: false
+ end
+ end
+ end
+
+ result = properties.values.select { |property| property.identity? }
+ result = [ properties[:name] ] if result.empty?
+ result
+ end
+
+ def included(other)
+ other.extend ClassMethods
+ end
+ end
+
+ def self.included(other)
+ other.extend ClassMethods
+ end
+
+ include Chef::Mixin::ParamsValidate
+
+ #
+ # Whether this property has been set (or whether it has a default that has
+ # been retrieved).
+ #
+ # @param name [Symbol] The name of the property.
+ # @return [Boolean] `true` if the property has been set.
+ #
+ def property_is_set?(name)
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.is_set?(self)
+ end
+
+ #
+ # Clear this property as if it had never been set. It will thereafter return
+ # the default.
+ # been retrieved).
+ #
+ # @param name [Symbol] The name of the property.
+ #
+ def reset_property(name)
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.reset(self)
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index be83edc697..db9a2f6d91 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -136,6 +136,7 @@ class Chef
raise "You cannot render partials in this context" unless @template_finder
partial_variables = options.delete(:variables) || _public_instance_variables
+ partial_variables[:template_finder] = @template_finder
partial_context = self.class.new(partial_variables)
partial_context._extend_modules(@_extension_modules)
diff --git a/lib/chef/mixin/which.rb b/lib/chef/mixin/which.rb
index 4179c97b62..4e1c516386 100644
--- a/lib/chef/mixin/which.rb
+++ b/lib/chef/mixin/which.rb
@@ -28,7 +28,7 @@ class Chef
paths = ENV['PATH'].split(File::PATH_SEPARATOR) + extra_path
paths.each do |path|
filename = File.join(path, cmd)
- return filename if File.executable?(filename)
+ return filename if File.executable?(Chef.path_to(filename))
end
false
end
diff --git a/lib/chef/mixin/wide_string.rb b/lib/chef/mixin/wide_string.rb
new file mode 100644
index 0000000000..0c32b76365
--- /dev/null
+++ b/lib/chef/mixin/wide_string.rb
@@ -0,0 +1,72 @@
+#
+# Author:: Jay Mundrawala(<jdm@chef.io>)
+# Copyright:: Copyright 2015 Chef Software
+# 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.
+#
+
+class Chef
+ module Mixin
+ module WideString
+
+ def wstring(str)
+ if str.nil? || str.encoding == Encoding::UTF_16LE
+ str
+ else
+ utf8_to_wide(str)
+ end
+ end
+
+ def utf8_to_wide(ustring)
+ # ensure it is actually UTF-8
+ # Ruby likes to mark binary data as ASCII-8BIT
+ ustring = (ustring + "").force_encoding('UTF-8') if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
+
+ # ensure we have the double-null termination Windows Wide likes
+ ustring = ustring + "\000\000" if ustring.length == 0 or ustring[-1].chr != "\000"
+
+ # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
+ ustring = begin
+ if ustring.respond_to?(:encode)
+ ustring.encode('UTF-16LE')
+ else
+ require 'iconv'
+ Iconv.conv("UTF-16LE", "UTF-8", ustring)
+ end
+ end
+ ustring
+ end
+
+ def wide_to_utf8(wstring)
+ # ensure it is actually UTF-16LE
+ # Ruby likes to mark binary data as ASCII-8BIT
+ wstring = wstring.force_encoding('UTF-16LE') if wstring.respond_to?(:force_encoding)
+
+ # encode it all as UTF-8
+ wstring = begin
+ if wstring.respond_to?(:encode)
+ wstring.encode('UTF-8')
+ else
+ require 'iconv'
+ Iconv.conv("UTF-8", "UTF-16LE", wstring)
+ end
+ end
+ # remove trailing CRLF and NULL characters
+ wstring.strip!
+ wstring
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/mixin/windows_env_helper.rb b/lib/chef/mixin/windows_env_helper.rb
index a126801a28..cd12b4254a 100644
--- a/lib/chef/mixin/windows_env_helper.rb
+++ b/lib/chef/mixin/windows_env_helper.rb
@@ -18,6 +18,7 @@
require 'chef/exceptions'
+require 'chef/mixin/wide_string'
require 'chef/platform/query_helpers'
require 'chef/win32/error' if Chef::Platform.windows?
require 'chef/win32/api/system' if Chef::Platform.windows?
@@ -26,6 +27,8 @@ require 'chef/win32/api/unicode' if Chef::Platform.windows?
class Chef
module Mixin
module WindowsEnvHelper
+ include Chef::Mixin::WideString
+
if Chef::Platform.windows?
include Chef::ReservedNames::Win32::API::System
end
@@ -45,7 +48,7 @@ class Chef
Chef::ReservedNames::Win32::Error.raise!
end
if ( SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, FFI::MemoryPointer.from_string(
- Chef::ReservedNames::Win32::Unicode.utf8_to_wide('Environment')
+ utf8_to_wide('Environment')
).address, flags, 5000, nil) == 0 )
Chef::ReservedNames::Win32::Error.raise!
end
diff --git a/lib/chef/mixin/wstring.rb b/lib/chef/mixin/wstring.rb
deleted file mode 100644
index bb6fdf4884..0000000000
--- a/lib/chef/mixin/wstring.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Author:: Jay Mundrawala(<jdm@chef.io>)
-# Copyright:: Copyright 2015 Chef Software
-# 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.
-#
-
-class Chef
- module Mixin
- module WideString
- def wstring(str)
- if str.nil? || str.encoding == Encoding::UTF_16LE
- str
- else
- str.to_wstring
- end
- end
- end
- end
-end
diff --git a/lib/chef/monkey_patches/webrick-utils.rb b/lib/chef/monkey_patches/webrick-utils.rb
new file mode 100644
index 0000000000..57f6db54ed
--- /dev/null
+++ b/lib/chef/monkey_patches/webrick-utils.rb
@@ -0,0 +1,51 @@
+require 'webrick/utils'
+
+module WEBrick
+ module Utils
+ ##
+ # Creates TCP server sockets bound to +address+:+port+ and returns them.
+ #
+ # It will create IPV4 and IPV6 sockets on all interfaces.
+ #
+ # NOTE: We need to monkey patch this method because
+ # create_listeners on Windows with Ruby > 2.0.0 does not
+ # raise an error if we're already listening on a port.
+ #
+ def create_listeners(address, port, logger=nil)
+ #
+ # utils.rb -- Miscellaneous utilities
+ #
+ # Author: IPR -- Internet Programming with Ruby -- writers
+ # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+ # reserved.
+ #
+ # $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
+ unless port
+ raise ArgumentError, "must specify port"
+ end
+ res = Socket::getaddrinfo(address, port,
+ Socket::AF_UNSPEC, # address family
+ Socket::SOCK_STREAM, # socket type
+ 0, # protocol
+ Socket::AI_PASSIVE) # flag
+ last_error = nil
+ sockets = []
+ res.each{|ai|
+ begin
+ logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
+ sock = TCPServer.new(ai[3], port)
+ port = sock.addr[1] if port == 0
+ Utils::set_close_on_exec(sock)
+ sockets << sock
+ rescue => ex
+ logger.warn("TCPServer Error: #{ex}") if logger
+ last_error = ex
+ end
+ }
+ raise last_error if sockets.empty?
+ return sockets
+ end
+ module_function :create_listeners
+ end
+end
diff --git a/lib/chef/monkey_patches/win32/registry.rb b/lib/chef/monkey_patches/win32/registry.rb
new file mode 100644
index 0000000000..2ce2ac97f1
--- /dev/null
+++ b/lib/chef/monkey_patches/win32/registry.rb
@@ -0,0 +1,72 @@
+#
+# Copyright:: Copyright 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/win32/api/registry'
+require 'chef/win32/unicode'
+require 'win32/registry'
+
+module Win32
+ class Registry
+
+ module API
+
+ extend Chef::ReservedNames::Win32::API::Registry
+
+ module_function
+
+ if RUBY_VERSION =~ /^2\.1/
+ # ::Win32::Registry#delete_value is broken in Ruby 2.1 (up to Ruby 2.1.6).
+ # This should be resolved in a later release (see note #9 in link below).
+ # https://bugs.ruby-lang.org/issues/10820
+ def DeleteValue(hkey, name)
+ check RegDeleteValueW(hkey, name.to_wstring)
+ end
+ end
+
+ # ::Win32::Registry#delete_key uses RegDeleteKeyW. We need to use
+ # RegDeleteKeyExW to properly support WOW64 systems.
+ def DeleteKey(hkey, name)
+ check RegDeleteKeyExW(hkey, name.to_wstring, 0, 0)
+ end
+
+ end
+
+ if RUBY_VERSION =~ /^2.1/
+ # ::Win32::Registry#write does not correctly handle data in Ruby 2.1 (up to Ruby 2.1.6).
+ # https://bugs.ruby-lang.org/issues/11439
+ def write(name, type, data)
+ case type
+ when REG_SZ, REG_EXPAND_SZ
+ data = data.to_s.encode(WCHAR) + WCHAR_NUL
+ when REG_MULTI_SZ
+ data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL << WCHAR_NUL
+ when REG_BINARY
+ data = data.to_s
+ when REG_DWORD
+ data = API.packdw(data.to_i)
+ when REG_DWORD_BIG_ENDIAN
+ data = [data.to_i].pack('N')
+ when REG_QWORD
+ data = API.packqw(data.to_i)
+ else
+ raise TypeError, "Unsupported type #{type}"
+ end
+ API.SetValue(@hkey, name, type, data, data.bytesize)
+ end
+ end
+ end
+end
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 22c7d5bd8e..ad065cc02b 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -63,6 +63,8 @@ class Chef
include Chef::Mixin::ParamsValidate
+ NULL_ARG = Object.new
+
# Create a new Chef::Node object.
def initialize(chef_server_rest: nil)
@chef_server_rest = chef_server_rest
@@ -72,6 +74,9 @@ class Chef
@primary_runlist = Chef::RunList.new
@override_runlist = Chef::RunList.new
+ @policy_name = nil
+ @policy_group = nil
+
@attributes = Chef::Node::Attribute.new({}, {}, {}, {})
@run_state = {}
@@ -132,6 +137,50 @@ class Chef
alias :environment :chef_environment
+ # The `policy_name` for this node. Setting this to a non-nil value will
+ # enable policyfile mode when `chef-client` is run. If set in the config
+ # file or in node json, running `chef-client` will update this value.
+ #
+ # @see Chef::PolicyBuilder::Dynamic
+ # @see Chef::PolicyBuilder::Policyfile
+ #
+ # @param arg [String] the new policy_name value
+ # @return [String] the current policy_name, or the one you just set
+ def policy_name(arg=NULL_ARG)
+ return @policy_name if arg.equal?(NULL_ARG)
+ validate({policy_name: arg}, { policy_name: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } })
+ @policy_name = arg
+ end
+
+ # A "non-DSL-style" setter for `policy_name`
+ #
+ # @see #policy_name
+ def policy_name=(policy_name)
+ policy_name(policy_name)
+ end
+
+ # The `policy_group` for this node. Setting this to a non-nil value will
+ # enable policyfile mode when `chef-client` is run. If set in the config
+ # file or in node json, running `chef-client` will update this value.
+ #
+ # @see Chef::PolicyBuilder::Dynamic
+ # @see Chef::PolicyBuilder::Policyfile
+ #
+ # @param arg [String] the new policy_group value
+ # @return [String] the current policy_group, or the one you just set
+ def policy_group(arg=NULL_ARG)
+ return @policy_group if arg.equal?(NULL_ARG)
+ validate({policy_group: arg}, { policy_group: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } })
+ @policy_group = arg
+ end
+
+ # A "non-DSL-style" setter for `policy_group`
+ #
+ # @see #policy_group
+ def policy_group=(policy_group)
+ policy_group(policy_group)
+ end
+
def attributes
@attributes
end
@@ -391,7 +440,7 @@ class Chef
self.tags # make sure they're defined
- automatic_attrs[:recipes] = expansion.recipes.with_fully_qualified_names_and_version_constraints
+ automatic_attrs[:recipes] = expansion.recipes.with_duplicate_names
automatic_attrs[:expanded_run_list] = expansion.recipes.with_fully_qualified_names_and_version_constraints
automatic_attrs[:roles] = expansion.roles
@@ -461,6 +510,14 @@ class Chef
#Render correctly for run_list items so malformed json does not result
"run_list" => @primary_runlist.run_list.map { |item| item.to_s }
}
+ # Chef Server rejects node JSON with extra keys; prior to 12.3,
+ # "policy_name" and "policy_group" are unknown; after 12.3 they are
+ # optional, therefore only including them in the JSON if present
+ # maximizes compatibility for most people.
+ unless policy_group.nil? && policy_name.nil?
+ result["policy_name"] = policy_name
+ result["policy_group"] = policy_group
+ end
result
end
@@ -492,6 +549,10 @@ class Chef
else
o["recipes"].each { |r| node.recipes << r }
end
+
+ node.policy_name = o["policy_name"] if o.has_key?("policy_name")
+ node.policy_group = o["policy_group"] if o.has_key?("policy_group")
+
node
end
@@ -548,13 +609,21 @@ class Chef
# so then POST to create.
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In whyrun mode, so NOT performing node save.")
+ Chef::Log.warn("In why-run mode, so NOT performing node save.")
else
chef_server_rest.put_rest("nodes/#{name}", data_for_save)
end
rescue Net::HTTPServerException => e
- raise e unless e.response.code == "404"
- chef_server_rest.post_rest("nodes", data_for_save)
+ if e.response.code == "404"
+ chef_server_rest.post_rest("nodes", data_for_save)
+ # Chef Server before 12.3 rejects node JSON with 'policy_name' or
+ # 'policy_group' keys, but 'policy_name' will be detected first.
+ # Backcompat can be removed in 13.0
+ elsif e.response.code == "400" && e.response.body.include?("Invalid key policy_name")
+ save_without_policyfile_attrs
+ else
+ raise
+ end
end
self
end
@@ -563,6 +632,15 @@ class Chef
def create
chef_server_rest.post_rest("nodes", data_for_save)
self
+ rescue Net::HTTPServerException => e
+ # Chef Server before 12.3 rejects node JSON with 'policy_name' or
+ # 'policy_group' keys, but 'policy_name' will be detected first.
+ # Backcompat can be removed in 13.0
+ if e.response.code == "400" && e.response.body.include?("Invalid key policy_name")
+ chef_server_rest.post_rest("nodes", data_for_save_without_policyfile_attrs)
+ else
+ raise
+ end
end
def to_s
@@ -575,6 +653,22 @@ class Chef
private
+ def save_without_policyfile_attrs
+ trimmed_data = data_for_save_without_policyfile_attrs
+
+ chef_server_rest.put_rest("nodes/#{name}", trimmed_data)
+ rescue Net::HTTPServerException => e
+ raise e unless e.response.code == "404"
+ chef_server_rest.post_rest("nodes", trimmed_data)
+ end
+
+ def data_for_save_without_policyfile_attrs
+ data_for_save.tap do |trimmed_data|
+ trimmed_data.delete("policy_name")
+ trimmed_data.delete("policy_group")
+ end
+ end
+
def data_for_save
data = for_json
["automatic", "default", "normal", "override"].each do |level|
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index d905c8779e..751f9576f6 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -32,8 +32,8 @@ class Chef
# @return [NodeMap] Returns self for possible chaining
#
def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, on_platform: nil, on_platforms: nil, canonical: nil, override: nil, &block)
- Chef::Log.deprecation "The on_platform option to node_map has been deprecated" if on_platform
- Chef::Log.deprecation "The on_platforms option to node_map has been deprecated" if on_platforms
+ Chef.log_deprecation("The on_platform option to node_map has been deprecated") if on_platform
+ Chef.log_deprecation("The on_platforms option to node_map has been deprecated") if on_platforms
platform ||= on_platform || on_platforms
filters = {}
filters[:platform] = platform if platform
diff --git a/lib/chef/platform/query_helpers.rb b/lib/chef/platform/query_helpers.rb
index e64189fbd6..dfb99ed750 100644
--- a/lib/chef/platform/query_helpers.rb
+++ b/lib/chef/platform/query_helpers.rb
@@ -36,6 +36,44 @@ class Chef
is_server_2003
end
+ def windows_nano_server?
+ return false unless windows?
+ require 'win32/registry'
+
+ # This method may be called before ohai runs (e.g., it may be used to
+ # determine settings in config.rb). Chef::Win32::Registry.new uses
+ # node attributes to verify the machine architecture which aren't
+ # accessible before ohai runs.
+ nano = nil
+ key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels"
+ access = ::Win32::Registry::KEY_QUERY_VALUE | 0x0100 # nano is 64-bit only
+ begin
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, access) do |reg|
+ nano = reg["NanoServer"]
+ end
+ rescue ::Win32::Registry::Error
+ # If accessing the registry key failed, then we're probably not on
+ # nano. Fail through.
+ end
+ return nano == 1
+ end
+
+ def supports_msi?
+ return false unless windows?
+ require 'win32/registry'
+
+ key = "System\\CurrentControlSet\\Services\\msiserver"
+ access = ::Win32::Registry::KEY_QUERY_VALUE
+
+ begin
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, access) do |reg|
+ true
+ end
+ rescue ::Win32::Registry::Error
+ false
+ end
+ end
+
def supports_powershell_execution_bypass?(node)
node[:languages] && node[:languages][:powershell] &&
node[:languages][:powershell][:version].to_i >= 3
@@ -52,6 +90,13 @@ class Chef
Gem::Version.new(node[:languages][:powershell][:version]) >=
Gem::Version.new("5.0.10018.0")
end
+
+ def dsc_refresh_mode_disabled?(node)
+ require 'chef/util/powershell/cmdlet'
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
+ metadata = cmdlet.run!.return_value
+ metadata['RefreshMode'] == 'Disabled'
+ end
end
end
end
diff --git a/lib/chef/platform/service_helpers.rb b/lib/chef/platform/service_helpers.rb
index 92efe24da2..f97eed2581 100644
--- a/lib/chef/platform/service_helpers.rb
+++ b/lib/chef/platform/service_helpers.rb
@@ -16,6 +16,8 @@
# limitations under the License.
#
+require 'chef/chef_class'
+
class Chef
class Platform
class ServiceHelpers
@@ -34,55 +36,55 @@ class Chef
# different services is NOT a design concern of this module.
#
def service_resource_providers
- @service_resource_providers ||= [].tap do |service_resource_providers|
-
- if ::File.exist?("/usr/sbin/update-rc.d")
- service_resource_providers << :debian
- end
+ providers = []
- if ::File.exist?("/usr/sbin/invoke-rc.d")
- service_resource_providers << :invokercd
- end
+ if ::File.exist?(Chef.path_to("/usr/sbin/update-rc.d"))
+ providers << :debian
+ end
- if ::File.exist?("/sbin/insserv")
- service_resource_providers << :insserv
- end
+ if ::File.exist?(Chef.path_to("/usr/sbin/invoke-rc.d"))
+ providers << :invokercd
+ end
- if ::File.exist?("/sbin/initctl")
- service_resource_providers << :upstart
- end
+ if ::File.exist?(Chef.path_to("/sbin/initctl"))
+ service_resource_providers << :upstart
+ end
- if ::File.exist?("/sbin/chkconfig")
- service_resource_providers << :redhat
- end
+ if ::File.exist?(Chef.path_to("/sbin/insserv"))
+ providers << :insserv
+ end
- if systemd_is_init?
- service_resource_providers << :systemd
- end
+ if systemd_is_init?
+ providers << :systemd
+ end
+ if ::File.exist?(Chef.path_to("/sbin/chkconfig"))
+ providers << :redhat
end
+
+ providers
end
def config_for_service(service_name)
configs = []
- if ::File.exist?("/etc/init.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/etc/init.d/#{service_name}"))
configs << :initd
end
- if ::File.exist?("/etc/init/#{service_name}.conf")
+ if ::File.exist?(Chef.path_to("/etc/init/#{service_name}.conf"))
configs << :upstart
end
- if ::File.exist?("/etc/xinetd.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/etc/xinetd.d/#{service_name}"))
configs << :xinetd
end
- if ::File.exist?("/etc/rc.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/etc/rc.d/#{service_name}"))
configs << :etc_rcd
end
- if ::File.exist?("/usr/local/etc/rc.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/usr/local/etc/rc.d/#{service_name}"))
configs << :usr_local_etc_rcd
end
diff --git a/lib/chef/policy_builder.rb b/lib/chef/policy_builder.rb
index 136b2853b0..56415dbbd0 100644
--- a/lib/chef/policy_builder.rb
+++ b/lib/chef/policy_builder.rb
@@ -18,6 +18,7 @@
require 'chef/policy_builder/expand_node_object'
require 'chef/policy_builder/policyfile'
+require 'chef/policy_builder/dynamic'
class Chef
@@ -37,13 +38,5 @@ class Chef
# * cookbook_hash is stored in run_context
module PolicyBuilder
- def self.strategy
- if Chef::Config[:use_policyfile]
- Policyfile
- else
- ExpandNodeObject
- end
- end
-
end
end
diff --git a/lib/chef/policy_builder/dynamic.rb b/lib/chef/policy_builder/dynamic.rb
new file mode 100644
index 0000000000..c9842ba532
--- /dev/null
+++ b/lib/chef/policy_builder/dynamic.rb
@@ -0,0 +1,186 @@
+#
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright 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 'forwardable'
+
+require 'chef/log'
+require 'chef/rest'
+require 'chef/run_context'
+require 'chef/config'
+require 'chef/node'
+require 'chef/exceptions'
+
+class Chef
+ module PolicyBuilder
+
+ # PolicyBuilder that selects either a Policyfile or non-Policyfile
+ # implementation based on the content of the node object.
+ class Dynamic
+
+ extend Forwardable
+
+ attr_reader :node
+ attr_reader :node_name
+ attr_reader :ohai_data
+ attr_reader :json_attribs
+ attr_reader :override_runlist
+ attr_reader :events
+
+ def initialize(node_name, ohai_data, json_attribs, override_runlist, events)
+ @implementation = nil
+
+ @node_name = node_name
+ @ohai_data = ohai_data
+ @json_attribs = json_attribs
+ @override_runlist = override_runlist
+ @events = events
+
+ @node = nil
+ end
+
+ ## PolicyBuilder API ##
+
+ # Loads the node state from the server, then picks the correct
+ # implementation class based on the node and json_attribs.
+ #
+ # Calls #finish_load_node on the implementation object to complete the
+ # loading process. All subsequent lifecycle calls are delegated.
+ #
+ # @return [Chef::Node] the loaded node.
+ def load_node
+ events.node_load_start(node_name, config)
+ Chef::Log.debug("Building node object for #{node_name}")
+
+ @node =
+ if Chef::Config[:solo]
+ Chef::Node.build(node_name)
+ else
+ Chef::Node.find_or_create(node_name)
+ end
+ select_implementation(node)
+ implementation.finish_load_node(node)
+ node
+ rescue Exception => e
+ events.node_load_failed(node_name, e, config)
+ raise
+ end
+
+ ## Delegated Public API Methods ##
+
+ ### Accessors ###
+
+ def_delegator :implementation, :original_runlist
+ def_delegator :implementation, :run_context
+ def_delegator :implementation, :run_list_expansion
+
+ ### Lifecycle Methods ###
+
+ # @!method build_node
+ #
+ # Applies external attributes (e.g., from JSON file, environment,
+ # policyfile, etc.) and determines the correct expanded run list for the
+ # run.
+ #
+ # @return [Chef::Node]
+ def_delegator :implementation, :build_node
+
+ # @!method setup_run_context
+ #
+ # Synchronizes cookbooks and initializes the run context object for the
+ # run.
+ #
+ # @return [Chef::RunContext]
+ def_delegator :implementation, :setup_run_context
+
+ # @!method expanded_run_list
+ #
+ # Resolves the run list to a form containing only recipes and sets the
+ # `roles` and `recipes` automatic attributes on the node.
+ #
+ # @return [#recipes, #roles] A RunListExpansion or duck-type.
+ def_delegator :implementation, :expand_run_list
+
+ # @!method sync_cookbooks
+ #
+ # Synchronizes cookbooks. In a normal chef-client run, this is handled by
+ # #setup_run_context, but may be called directly in some circumstances.
+ #
+ # @return [Hash{String => Chef::CookbookManifest}] A map of
+ # CookbookManifest objects by cookbook name.
+ def_delegator :implementation, :sync_cookbooks
+
+ # @!method temporary_policy?
+ #
+ # Indicates whether the policy is temporary, which means an
+ # override_runlist was provided. Chef::Client uses this to decide whether
+ # to do the final node save at the end of the run or not.
+ #
+ # @return [true,false]
+ def_delegator :implementation, :temporary_policy?
+
+ ## Internal Public API ##
+
+ # Returns the selected implementation, or raises if not set. The
+ # implementation is set when #load_node is called.
+ #
+ # @return [PolicyBuilder::Policyfile, PolicyBuilder::ExpandNodeObject]
+ def implementation
+ @implementation or raise Exceptions::InvalidPolicybuilderCall, "#load_node must be called before other policy builder methods"
+ end
+
+ # @api private
+ #
+ # Sets the implementation based on the content of the node, node JSON
+ # (i.e., the `-j JSON_FILE` data), and config. This is only public for
+ # testing purposes; production code should call #load_node instead.
+ def select_implementation(node)
+ if policyfile_set_in_config? ||
+ policyfile_attribs_in_node_json? ||
+ node_has_policyfile_attrs?(node) ||
+ policyfile_compat_mode_config?
+ @implementation = Policyfile.new(node_name, ohai_data, json_attribs, override_runlist, events)
+ else
+ @implementation = ExpandNodeObject.new(node_name, ohai_data, json_attribs, override_runlist, events)
+ end
+ end
+
+ def config
+ Chef::Config
+ end
+
+ private
+
+ def node_has_policyfile_attrs?(node)
+ node.policy_name || node.policy_group
+ end
+
+ def policyfile_attribs_in_node_json?
+ json_attribs.key?("policy_name") || json_attribs.key?("policy_group")
+ end
+
+ def policyfile_set_in_config?
+ config[:use_policyfile] || config[:policy_name] || config[:policy_group]
+ end
+
+ def policyfile_compat_mode_config?
+ config[:deployment_group] && !config[:policy_document_native_api]
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb
index 524bdd95b1..2c6d644e42 100644
--- a/lib/chef/policy_builder/expand_node_object.rb
+++ b/lib/chef/policy_builder/expand_node_object.rb
@@ -33,6 +33,9 @@ class Chef
# expands the run_list on a node object and then queries the chef-server
# to find the correct set of cookbooks, given version constraints of the
# node's environment.
+ #
+ # Note that this class should only be used via PolicyBuilder::Dynamic and
+ # not instantiated directly.
class ExpandNodeObject
attr_reader :events
@@ -55,9 +58,10 @@ class Chef
@run_list_expansion = nil
end
- # This method injects the run_context and provider and resource priority
- # maps into the Chef class. The run_context has to be injected here, the provider and
- # resource maps could be moved if a better place can be found to do this work.
+ # This method injects the run_context and into the Chef class.
+ #
+ # NOTE: This is duplicated with the Policyfile implementation. If
+ # it gets any more complicated, it needs to be moved elsewhere.
#
# @param run_context [Chef::RunContext] the run_context to inject
def setup_chef_class(run_context)
@@ -93,25 +97,36 @@ class Chef
run_context
end
-
- # In client-server operation, loads the node state from the server. In
- # chef-solo operation, builds a new node object.
+ # DEPRECATED: As of Chef 12.5, chef selects either policyfile mode or
+ # "expand node" mode dynamically, based on the content of the node
+ # object, first boot JSON, and config. This happens in
+ # PolicyBuilder::Dynamic, which selects the implementation during
+ # #load_node and then delegates to either ExpandNodeObject or Policyfile
+ # implementations as appropriate. Tools authors should update their code
+ # to create a PolicyBuilder::Dynamc policy builder and allow it to select
+ # the proper implementation.
def load_node
- events.node_load_start(node_name, Chef::Config)
+ Chef.log_deprecation("ExpandNodeObject#load_node is deprecated. Please use Chef::PolicyBuilder::Dynamic instead of using ExpandNodeObject directly")
+
+ events.node_load_start(node_name, config)
Chef::Log.debug("Building node object for #{node_name}")
- if Chef::Config[:solo]
- @node = Chef::Node.build(node_name)
- else
- @node = Chef::Node.find_or_create(node_name)
- end
+ @node =
+ if Chef::Config[:solo]
+ Chef::Node.build(node_name)
+ else
+ Chef::Node.find_or_create(node_name)
+ end
+ finish_load_node(node)
+ node
rescue Exception => e
- # TODO: wrap this exception so useful error info can be given to the
- # user.
- events.node_load_failed(node_name, e, Chef::Config)
+ events.node_load_failed(node_name, e, config)
raise
end
+ def finish_load_node(node)
+ @node = node
+ end
# Applies environment, external JSON attributes, and override run list to
# the node, Then expands the run_list.
@@ -139,6 +154,7 @@ class Chef
Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]")
events.node_load_completed(node, @expanded_run_list_with_versions, Chef::Config)
+ events.run_list_expanded(@run_list_expansion)
node
end
diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb
index 5991e3ce10..d6dcdf67b2 100644
--- a/lib/chef/policy_builder/policyfile.rb
+++ b/lib/chef/policy_builder/policyfile.rb
@@ -68,22 +68,20 @@ class Chef
@node = nil
- Chef::Log.warn("Using experimental Policyfile feature")
-
if Chef::Config[:solo]
- raise UnsupportedFeature, "Policyfile does not support chef-solo at this time."
+ raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead."
end
if override_runlist
- raise UnsupportedFeature, "Policyfile does not support override run lists at this time"
+ raise UnsupportedFeature, "Policyfile does not support override run lists. Use named run_lists instead."
end
if json_attribs && json_attribs.key?("run_list")
- raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data at this time"
+ raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data."
end
if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty?
- raise UnsupportedFeature, "Policyfile does not work with Chef Environments"
+ raise UnsupportedFeature, "Policyfile does not work with Chef Environments."
end
end
@@ -112,18 +110,11 @@ class Chef
## PolicyBuilder API ##
- # Loads the node state from the server.
- def load_node
- events.node_load_start(node_name, Chef::Config)
- Chef::Log.debug("Building node object for #{node_name}")
-
- @node = Chef::Node.find_or_create(node_name)
+ def finish_load_node(node)
+ @node = node
+ select_policy_name_and_group
validate_policyfile
events.policyfile_loaded(policy)
- node
- rescue Exception => e
- events.node_load_failed(node_name, e, Chef::Config)
- raise
end
# Applies environment, external JSON attributes, and override run list to
@@ -154,25 +145,42 @@ class Chef
raise
end
+ # Synchronizes cookbooks and initializes the run context object for the
+ # run.
+ #
+ # @return [Chef::RunContext]
def setup_run_context(specific_recipes=nil)
Chef::Cookbook::FileVendor.fetch_from_remote(http_api)
sync_cookbooks
cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync)
run_context = Chef::RunContext.new(node, cookbook_collection, events)
+ setup_chef_class(run_context)
+
run_context.load(run_list_expansion_ish)
+ setup_chef_class(run_context)
run_context
end
+ # Sets `run_list` on the node from the policy, sets `roles` and `recipes`
+ # attributes on the node accordingly.
+ #
+ # @return [RunListExpansionIsh] A RunListExpansion duck-type.
def expand_run_list
+ CookbookCacheCleaner.instance.skip_removal = true if named_run_list_requested?
+
node.run_list(run_list)
node.automatic_attrs[:roles] = []
node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes
run_list_expansion_ish
end
-
+ # Synchronizes cookbooks. In a normal chef-client run, this is handled by
+ # #setup_run_context, but may be called directly in some circumstances.
+ #
+ # @return [Hash{String => Chef::CookbookManifest}] A map of
+ # CookbookManifest objects by cookbook name.
def sync_cookbooks
Chef::Log.debug("Synchronizing cookbooks")
synchronizer = Chef::CookbookSynchronizer.new(cookbooks_to_sync, events)
@@ -186,12 +194,18 @@ class Chef
# Whether or not this is a temporary policy. Since PolicyBuilder doesn't
# support override_runlist, this is always false.
+ #
+ # @return [false]
def temporary_policy?
false
end
## Internal Public API ##
+ # @api private
+ #
+ # Generates an array of strings with recipe names including version and
+ # identifier info.
def run_list_with_versions_for_display
run_list.map do |recipe_spec|
cookbook, recipe = parse_recipe_spec(recipe_spec)
@@ -201,6 +215,11 @@ class Chef
end
end
+ # @api private
+ #
+ # Sets up a RunListExpansionIsh object so that it can be used in place of
+ # a RunListExpansion object, to satisfy the API contract of
+ # #expand_run_list
def run_list_expansion_ish
recipes = run_list.map do |recipe_spec|
cookbook, recipe = parse_recipe_spec(recipe_spec)
@@ -209,11 +228,15 @@ class Chef
RunListExpansionIsh.new(recipes, [])
end
+ # @api private
+ #
+ # Sets attributes from the policyfile on the node, using the role priority.
def apply_policyfile_attributes
node.attributes.role_default = policy["default_attributes"]
node.attributes.role_override = policy["override_attributes"]
end
+ # @api private
def parse_recipe_spec(recipe_spec)
rmatch = recipe_spec.match(/recipe\[([^:]+)::([^:]+)\]/)
if rmatch.nil?
@@ -223,20 +246,31 @@ class Chef
end
end
+ # @api private
def cookbook_lock_for(cookbook_name)
cookbook_locks[cookbook_name]
end
+ # @api private
def run_list
- policy["run_list"]
+ if named_run_list_requested?
+ named_run_list or
+ raise ConfigurationError,
+ "Policy '#{retrieved_policy_name}' revision '#{revision_id}' does not have named_run_list '#{named_run_list_name}'" +
+ "(available named_run_lists: [#{available_named_run_lists.join(', ')}])"
+ else
+ policy["run_list"]
+ end
end
+ # @api private
def policy
@policy ||= http_api.get(policyfile_location)
rescue Net::HTTPServerException => e
raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.message}"
end
+ # @api private
def policyfile_location
if Chef::Config[:policy_document_native_api]
validate_policy_config!
@@ -273,6 +307,7 @@ class Chef
end
end
+ # @api private
def validate_recipe_spec(recipe_spec)
parse_recipe_spec(recipe_spec)
nil
@@ -282,11 +317,13 @@ class Chef
class ConfigurationError < StandardError; end
+ # @api private
def deployment_group
Chef::Config[:deployment_group] or
raise ConfigurationError, "Setting `deployment_group` is not configured."
end
+ # @api private
def validate_policy_config!
policy_group or
raise ConfigurationError, "Setting `policy_group` is not configured."
@@ -295,14 +332,75 @@ class Chef
raise ConfigurationError, "Setting `policy_name` is not configured."
end
+ # @api private
def policy_group
Chef::Config[:policy_group]
end
+ # @api private
def policy_name
Chef::Config[:policy_name]
end
+ # @api private
+ #
+ # Selects the `policy_name` and `policy_group` from the following sources
+ # in priority order:
+ #
+ # 1. JSON attribs (i.e., `-j JSON_FILE`)
+ # 2. `Chef::Config`
+ # 3. The node object
+ #
+ # The selected values are then copied to `Chef::Config` and the node.
+ def select_policy_name_and_group
+ policy_name_to_set =
+ policy_name_from_json_attribs ||
+ policy_name_from_config ||
+ policy_name_from_node
+
+ policy_group_to_set =
+ policy_group_from_json_attribs ||
+ policy_group_from_config ||
+ policy_group_from_node
+
+ node.policy_name = policy_name_to_set
+ node.policy_group = policy_group_to_set
+
+ Chef::Config[:policy_name] = policy_name_to_set
+ Chef::Config[:policy_group] = policy_group_to_set
+ end
+
+ # @api private
+ def policy_group_from_json_attribs
+ json_attribs["policy_group"]
+ end
+
+ # @api private
+ def policy_name_from_json_attribs
+ json_attribs["policy_name"]
+ end
+
+ # @api private
+ def policy_group_from_config
+ Chef::Config[:policy_group]
+ end
+
+ # @api private
+ def policy_name_from_config
+ Chef::Config[:policy_name]
+ end
+
+ # @api private
+ def policy_group_from_node
+ node.policy_group
+ end
+
+ # @api private
+ def policy_name_from_node
+ node.policy_name
+ end
+
+ # @api private
# Builds a 'cookbook_hash' map of the form
# "COOKBOOK_NAME" => "IDENTIFIER"
#
@@ -330,6 +428,7 @@ class Chef
raise
end
+ # @api private
# Fetches the CookbookVersion object for the given name and identifer
# specified in the lock_data.
# TODO: This only implements Chef 11 compatibility mode, which means that
@@ -343,20 +442,58 @@ class Chef
end
end
+ # @api private
def cookbook_locks
policy["cookbook_locks"]
end
+ # @api private
+ def revision_id
+ policy["revision_id"]
+ end
+
+ # @api private
def http_api
@api_service ||= Chef::REST.new(config[:chef_server_url])
end
+ # @api private
def config
Chef::Config
end
private
+ # This method injects the run_context and into the Chef class.
+ #
+ # NOTE: This is duplicated with the ExpandNodeObject implementation. If
+ # it gets any more complicated, it needs to be moved elsewhere.
+ #
+ # @param run_context [Chef::RunContext] the run_context to inject
+ def setup_chef_class(run_context)
+ Chef.set_run_context(run_context)
+ end
+
+ def retrieved_policy_name
+ policy["name"]
+ end
+
+ def named_run_list
+ policy["named_run_lists"] && policy["named_run_lists"][named_run_list_name]
+ end
+
+ def available_named_run_lists
+ (policy["named_run_lists"] || {}).keys
+ end
+
+ def named_run_list_requested?
+ !!Chef::Config[:named_run_list]
+ end
+
+ def named_run_list_name
+ Chef::Config[:named_run_list]
+ end
+
def compat_mode_manifest_for(cookbook_name, lock_data)
xyz_version = lock_data["dotted_decimal_identifier"]
rel_url = "cookbooks/#{cookbook_name}/#{xyz_version}"
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index 1a3b8ec72c..e97d8f9607 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -86,7 +86,30 @@ class Chef
#
def initialize(**options)
options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
- options[:name_property] = options.delete(:name_attribute) if options.has_key?(:name_attribute) && !options.has_key?(:name_property)
+
+ # Replace name_attribute with name_property
+ if options.has_key?(:name_attribute)
+ # If we have both name_attribute and name_property and they differ, raise an error
+ if options.has_key?(:name_property)
+ raise ArgumentError, "Cannot specify both name_property and name_attribute together on property #{options[:name]}#{options[:declared_in] ? " of resource #{options[:declared_in].resource_name}" : ""}."
+ end
+ # replace name_property with name_attribute in place
+ options = Hash[options.map { |k,v| k == :name_attribute ? [ :name_property, v ] : [ k,v ] }]
+ end
+
+ # Only pick the first of :default, :name_property and :name_attribute if
+ # more than one is specified.
+ if options.has_key?(:default) && options[:name_property]
+ if options[:default].nil? || options.keys.index(:name_property) < options.keys.index(:default)
+ options.delete(:default)
+ preferred_default = :name_property
+ else
+ options.delete(:name_property)
+ preferred_default = :default
+ end
+ Chef.log_deprecation("Cannot specify both default and name_property together on property #{options[:name]}#{options[:declared_in] ? " of resource #{options[:declared_in].resource_name}" : ""}. Only one (#{preferred_default}) will be obeyed. In Chef 13, this will become an error.")
+ end
+
@options = options
options[:name] = options[:name].to_sym if options[:name]
@@ -228,7 +251,7 @@ class Chef
if value.nil? && !explicitly_accepts_nil?(resource)
# If you say "my_property nil" and the property explicitly accepts
# nil values, we consider this a get.
- Chef::Log.deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
+ Chef.log_deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
return get(resource)
end
@@ -399,7 +422,16 @@ class Chef
# @return [Property] The new property type.
#
def derive(**modified_options)
- Property.new(**options.merge(**modified_options))
+ # Since name_property, name_attribute and default override each other,
+ # if you specify one of them in modified_options it overrides anything in
+ # the original options.
+ options = self.options
+ if modified_options.has_key?(:name_property) ||
+ modified_options.has_key?(:name_attribute) ||
+ modified_options.has_key?(:default)
+ options = options.reject { |k,v| k == :name_attribute || k == :name_property || k == :default }
+ end
+ Property.new(options.merge(modified_options))
end
#
@@ -424,10 +456,10 @@ class Chef
EOM
rescue SyntaxError
# If the name is not a valid ruby name, we use define_method.
- resource_class.define_method(name) do |value=NOT_PASSED|
+ declared_in.define_method(name) do |value=NOT_PASSED|
self.class.properties[name].call(self, value)
end
- resource_class.define_method("#{name}=") do |value|
+ declared_in.define_method("#{name}=") do |value|
self.class.properties[name].set(self, value)
end
end
@@ -447,6 +479,8 @@ class Chef
# A type accepts nil explicitly if "is" allows nil, it validates as nil, *and* is not simply
# an empty type.
#
+ # A type is presumed to accept nil if it does coercion (which must handle nil).
+ #
# These examples accept nil explicitly:
# ```ruby
# property :a, [ String, nil ]
@@ -478,7 +512,8 @@ class Chef
#
# @api private
def explicitly_accepts_nil?(resource)
- options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false)
+ options.has_key?(:coerce) ||
+ (options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false))
end
def get_value(resource)
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index f2a493c3e6..e22f11d9be 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -265,7 +265,7 @@ class Chef
provider_class = self
@included_resource_dsl_module = Module.new do
extend Forwardable
- define_singleton_method(:to_s) { "#{resource_class} forwarder module" }
+ define_singleton_method(:to_s) { "forwarder module for #{provider_class}" }
define_singleton_method(:inspect) { to_s }
# Add a delegator for each explicit property that will get the *current* value
# of the property by default instead of the *actual* value.
@@ -421,7 +421,7 @@ class Chef
module DeprecatedLWRPClass
def const_missing(class_name)
if deprecated_constants[class_name.to_sym]
- Chef::Log.deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::ProviderResolver.new(node, resource, action) instead.")
+ Chef.log_deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::ProviderResolver.new(node, resource, action) instead.")
deprecated_constants[class_name.to_sym]
else
raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
index 6d9b7f4397..77a0410593 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
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
index 379369ba6e..65830131ab 100644
--- a/lib/chef/provider/dsc_resource.rb
+++ b/lib/chef/provider/dsc_resource.rb
@@ -59,9 +59,7 @@ class Chef
a.block_action!
end
requirements.assert(:run) do |a|
- a.assertion {
- meta_configuration['RefreshMode'] == 'Disabled'
- }
+ a.assertion { dsc_refresh_mode_disabled? }
err = ["The LCM must have its RefreshMode set to Disabled. "]
a.failure_message Chef::Exceptions::ProviderNotFound, err.join(' ')
a.whyrun err + ["Assuming a previous resource sets the RefreshMode."]
@@ -85,6 +83,10 @@ class Chef
def supports_dsc_invoke_resource?
run_context && Chef::Platform.supports_dsc_invoke_resource?(node)
end
+
+ def dsc_refresh_mode_disabled?
+ Chef::Platform.dsc_refresh_mode_disabled?(node)
+ end
def generate_description
@converge_description
@@ -153,12 +155,6 @@ class Chef
cmdlet.run!
end
- def meta_configuration
- cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
- result = cmdlet.run!
- result.return_value
- end
-
end
end
end
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index b44112c19e..c3dd3b4ee1 100644
--- a/lib/chef/provider/execute.rb
+++ b/lib/chef/provider/execute.rb
@@ -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
diff --git a/lib/chef/provider/lwrp_base.rb b/lib/chef/provider/lwrp_base.rb
index a96c382a01..9c7cd15bbf 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/lwrp_base.rb
@@ -19,6 +19,7 @@
#
require 'chef/provider'
+require 'chef/dsl/recipe'
require 'chef/dsl/include_recipe'
class Chef
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index 83fc09c8ae..7a6582363e 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -111,7 +111,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
[]
diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb
index c5d52a8384..6ce0dd689f 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -61,7 +61,7 @@ class Chef
Chef::Log.debug("#{@new_resource} checking rpm status")
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
@@ -78,7 +78,7 @@ class Chef
@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
diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb
index 31faa78215..7fdbbcff35 100644
--- a/lib/chef/provider/package/windows/msi.rb
+++ b/lib/chef/provider/package/windows/msi.rb
@@ -18,7 +18,7 @@
# 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/win32/api/installer' if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
require 'chef/mixin/shell_out'
class Chef
@@ -26,7 +26,7 @@ class Chef
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)
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 81454380a3..aff8dc9326 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.rb
@@ -791,7 +791,7 @@ class Chef
"/usr/bin/python"
end
rescue StandardError => e
- Chef::Log.warn("An error occured attempting to determine correct python executable. Using default.")
+ Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.")
Chef::Log.debug(e)
"/usr/bin/python"
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index b876b6d8ee..e04efb6b42 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/platform/query_helpers'
require 'chef/provider/windows_script'
class Chef
@@ -40,16 +41,24 @@ class Chef
# Powershell.exe is always in "v1.0" folder (for backwards compatibility)
interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter)
- "\"#{interpreter_path}\" #{flags} \"#{script_file.path}\""
- end
-
- def flags
# 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'.
- interpreter_flags = [default_interpreter_flags, '-File'].join(' ')
+ #
+ # 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(' ')
@@ -87,7 +96,7 @@ EOH
# 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}"
+ 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
@@ -107,6 +116,8 @@ EOH
end
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
@@ -188,6 +199,9 @@ elseif ( $LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0 )
$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,
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb
index 85ceb5cdae..3c1c50b963 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/file'
require 'chef/resource/directory'
-require 'chef/resource/remote_file'
+require 'chef/resource/cookbook_file'
require 'chef/mixin/file_class'
-require 'chef/platform'
-require 'uri'
-require 'tempfile'
-require 'net/https'
-require 'set'
+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 = 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/http.rb b/lib/chef/provider/remote_file/http.rb
index f17ab5a56d..e1f1cb2da7 100644
--- a/lib/chef/provider/remote_file/http.rb
+++ b/lib/chef/provider/remote_file/http.rb
@@ -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/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index 33a9778715..3ad11a7672 100644
--- a/lib/chef/provider/service/redhat.rb
+++ b/lib/chef/provider/service/redhat.rb
@@ -61,8 +61,10 @@ class Chef
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
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
index eaea6bb1ab..7040503c6b 100644
--- a/lib/chef/provider/service/solaris.rb
+++ b/lib/chef/provider/service/solaris.rb
@@ -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/subversion.rb b/lib/chef/provider/subversion.rb
index 5f36483c32..e3e3d5158a 100644
--- a/lib/chef/provider/subversion.rb
+++ b/lib/chef/provider/subversion.rb
@@ -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
@@ -180,6 +177,7 @@ class Chef
attrs
end
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 == ['.','..']
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/content.rb b/lib/chef/provider/template/content.rb
index a231bd509e..693b19a8c6 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -29,30 +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
# 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[: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)
+ 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
@@ -61,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 244b11db98..76aefbf1c8 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -89,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."
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index 0c0c85e18b..d9e235d4b1 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -257,10 +257,13 @@ 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?
+ @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
rescue Chef::Exceptions::DsclCommandFailed => e
diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb
index b242095f0c..c16db22ad4 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");
@@ -23,7 +25,6 @@ class Chef
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
@@ -43,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
@@ -67,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/windows.rb b/lib/chef/provider/user/windows.rb
index e282a11d45..76519bb498 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -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
@@ -100,7 +103,6 @@ class Chef
field_list = {
'comment' => 'full_name',
'home' => 'home_dir',
- 'gid' => 'primary_group_id',
'uid' => 'user_id',
'shell' => 'script_path',
'password' => 'password'
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 8459bc1328..82a24fc078 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -157,8 +157,8 @@ class Chef
# perf concern otherwise.)
handlers = providers.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource) }
handlers.each do |handler|
- Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ Chef.log_deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
+ Chef.log_deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
end
end
handlers
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 5bef40625f..90453bd00e 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -19,7 +19,6 @@
#
require 'chef/exceptions'
-require 'chef/mixin/params_validate'
require 'chef/dsl/platform_introspection'
require 'chef/dsl/data_query'
require 'chef/dsl/registry_helper'
@@ -29,7 +28,7 @@ require 'chef/mixin/convert_to_class_name'
require 'chef/guard_interpreter/resource_guard_interpreter'
require 'chef/resource/conditional'
require 'chef/resource/conditional_action_not_nothing'
-require 'chef/resource/action_provider'
+require 'chef/resource/action_class'
require 'chef/resource_collection'
require 'chef/node_map'
require 'chef/node'
@@ -40,6 +39,7 @@ require 'chef/resource_resolver'
require 'set'
require 'chef/mixin/deprecation'
+require 'chef/mixin/properties'
require 'chef/mixin/provides'
require 'chef/mixin/shell_out'
require 'chef/mixin/powershell_out'
@@ -61,6 +61,34 @@ class Chef
include Chef::Mixin::ShellOut
include Chef::Mixin::PowershellOut
+ # Bring in `property` and `property_type`
+ include Chef::Mixin::Properties
+
+ #
+ # The name of this particular resource.
+ #
+ # This special resource attribute is set automatically from the declaration
+ # of the resource, e.g.
+ #
+ # execute 'Vitruvius' do
+ # command 'ls'
+ # end
+ #
+ # Will set the name to "Vitruvius".
+ #
+ # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`.
+ #
+ # This is also used for resource notifications and subscribes in the same manner.
+ #
+ # This will coerce any object into a string via #to_s. Arrays are a special case
+ # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more
+ # awkward `package[["foo", "bar"]]` that #to_s would produce.
+ #
+ # @param name [Object] The name to set, typically a String or Array
+ # @return [String] The name of this Resource.
+ #
+ property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(', ') : v.to_s }, desired_state: false
+
#
# The node the current Chef run is using.
#
@@ -133,30 +161,6 @@ class Chef
end
#
- # The list of properties defined on this resource.
- #
- # Everything defined with `property` is in this list.
- #
- # @param include_superclass [Boolean] `true` to include properties defined
- # on superclasses; `false` or `nil` to return the list of properties
- # directly on this class.
- #
- # @return [Hash<Symbol,Property>] The list of property names and types.
- #
- def self.properties(include_superclass=true)
- @properties ||= {}
- if include_superclass
- if superclass.respond_to?(:properties)
- superclass.properties.merge(@properties)
- else
- @properties.dup
- end
- else
- @properties
- end
- end
-
- #
# The action or actions that will be taken when this resource is run.
#
# @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`)
@@ -681,14 +685,13 @@ class Chef
# Resource Definition Interface (for resource developers)
#
- include Chef::Mixin::ParamsValidate
include Chef::Mixin::Deprecation
#
# The provider class for this resource.
#
# If `action :x do ... end` has been declared on this resource or its
- # superclasses, this will return the `action_provider_class`.
+ # superclasses, this will return the `action_class`.
#
# If this is not set, `provider_for_action` will dynamically determine the
# provider.
@@ -699,7 +702,7 @@ class Chef
#
# @return The provider class for this resource.
#
- # @see Chef::Resource.action_provider_class
+ # @see Chef::Resource.action_class
#
def provider(arg=nil)
klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
@@ -708,249 +711,13 @@ class Chef
arg
end
set_or_return(:provider, klass, kind_of: [ Class ]) ||
- self.class.action_provider_class
+ self.class.action_class
end
def provider=(arg)
provider(arg)
end
#
- # Create a property on this resource class.
- #
- # If a superclass has this property, or if this property has already been
- # defined by this resource, this will *override* the previous value.
- #
- # @param name [Symbol] The name of the property.
- # @param type [Object,Array<Object>] The type(s) of this property.
- # If present, this is prepended to the `is` validation option.
- # @param options [Hash<Symbol,Object>] Validation options.
- # @option options [Object,Array] :is An object, or list of
- # objects, that must match the value using Ruby's `===` operator
- # (`options[:is].any? { |v| v === value }`).
- # @option options [Object,Array] :equal_to An object, or list
- # of objects, that must be equal to the value using Ruby's `==`
- # operator (`options[:is].any? { |v| v == value }`)
- # @option options [Regexp,Array<Regexp>] :regex An object, or
- # list of objects, that must match the value with `regex.match(value)`.
- # @option options [Class,Array<Class>] :kind_of A class, or
- # list of classes, that the value must be an instance of.
- # @option options [Hash<String,Proc>] :callbacks A hash of
- # messages -> procs, all of which match the value. The proc must
- # return a truthy or falsey value (true means it matches).
- # @option options [Symbol,Array<Symbol>] :respond_to A method
- # name, or list of method names, the value must respond to.
- # @option options [Symbol,Array<Symbol>] :cannot_be A property,
- # or a list of properties, that the value cannot have (such as `:nil` or
- # `:empty`). The method with a questionmark at the end is called on the
- # value (e.g. `value.empty?`). If the value does not have this method,
- # it is considered valid (i.e. if you don't respond to `empty?` we
- # assume you are not empty).
- # @option options [Proc] :coerce A proc which will be called to
- # transform the user input to canonical form. The value is passed in,
- # and the transformed value returned as output. Lazy values will *not*
- # be passed to this method until after they are evaluated. Called in the
- # context of the resource (meaning you can access other properties).
- # @option options [Boolean] :required `true` if this property
- # must be present; `false` otherwise. This is checked after the resource
- # is fully initialized.
- # @option options [Boolean] :name_property `true` if this
- # property defaults to the same value as `name`. Equivalent to
- # `default: lazy { name }`, except that #property_is_set? will
- # return `true` if the property is set *or* if `name` is set.
- # @option options [Boolean] :name_attribute Same as `name_property`.
- # @option options [Object] :default The value this property
- # will return if the user does not set one. If this is `lazy`, it will
- # be run in the context of the instance (and able to access other
- # properties).
- # @option options [Boolean] :desired_state `true` if this property is
- # part of desired state. Defaults to `true`.
- # @option options [Boolean] :identity `true` if this property
- # is part of object identity. Defaults to `false`.
- #
- # @example Bare property
- # property :x
- #
- # @example With just a type
- # property :x, String
- #
- # @example With just options
- # property :x, default: 'hi'
- #
- # @example With type and options
- # property :x, String, default: 'hi'
- #
- def self.property(name, type=NOT_PASSED, **options)
- name = name.to_sym
-
- options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
- options.merge!(name: name, declared_in: self)
-
- if type == NOT_PASSED
- # If a type is not passed, the property derives from the
- # superclass property (if any)
- if properties.has_key?(name)
- property = properties[name].derive(**options)
- else
- property = property_type(**options)
- end
-
- # If a Property is specified, derive a new one from that.
- elsif type.is_a?(Property) || (type.is_a?(Class) && type <= Property)
- property = type.derive(**options)
-
- # If a primitive type was passed, combine it with "is"
- else
- if options[:is]
- options[:is] = ([ type ] + [ options[:is] ]).flatten(1)
- else
- options[:is] = type
- end
- property = property_type(**options)
- end
-
- if !options[:default].frozen? && (options[:default].is_a?(Array) || options[:default].is_a?(Hash))
- Chef::Log.warn("Property #{self}.#{name} has an array or hash default (#{options[:default]}). This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes. Either freeze the constant with `.freeze` to prevent appending, or use lazy { #{options[:default].inspect} }.")
- end
-
- local_properties = properties(false)
- local_properties[name] = property
-
- property.emit_dsl
- end
-
- #
- # Create a reusable property type that can be used in multiple properties
- # in different resources.
- #
- # @param options [Hash<Symbol,Object>] Validation options. see #property for
- # the list of options.
- #
- # @example
- # property_type(default: 'hi')
- #
- def self.property_type(**options)
- Property.derive(**options)
- end
-
- #
- # The name of this particular resource.
- #
- # This special resource attribute is set automatically from the declaration
- # of the resource, e.g.
- #
- # execute 'Vitruvius' do
- # command 'ls'
- # end
- #
- # Will set the name to "Vitruvius".
- #
- # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`.
- #
- # This is also used for resource notifications and subscribes in the same manner.
- #
- # This will coerce any object into a string via #to_s. Arrays are a special case
- # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more
- # awkward `package[["foo", "bar"]]` that #to_s would produce.
- #
- # @param name [Object] The name to set, typically a String or Array
- # @return [String] The name of this Resource.
- #
- property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(', ') : v.to_s }, desired_state: false
-
- #
- # Whether this property has been set (or whether it has a default that has
- # been retrieved).
- #
- # @param name [Symbol] The name of the property.
- # @return [Boolean] `true` if the property has been set.
- #
- def property_is_set?(name)
- property = self.class.properties[name.to_sym]
- raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
- property.is_set?(self)
- end
-
- #
- # Clear this property as if it had never been set. It will thereafter return
- # the default.
- # been retrieved).
- #
- # @param name [Symbol] The name of the property.
- #
- def reset_property(name)
- property = self.class.properties[name.to_sym]
- raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
- property.reset(self)
- end
-
- #
- # Create a lazy value for assignment to a default value.
- #
- # @param block The block to run when the value is retrieved.
- #
- # @return [Chef::DelayedEvaluator] The lazy value
- #
- def self.lazy(&block)
- DelayedEvaluator.new(&block)
- end
-
- #
- # Get or set the list of desired state properties for this resource.
- #
- # State properties are properties that describe the desired state
- # of the system, such as file permissions or ownership.
- # In general, state properties are properties that could be populated by
- # examining the state of the system (e.g., File.stat can tell you the
- # permissions on an existing file). Contrarily, properties that are not
- # "state properties" usually modify the way Chef itself behaves, for example
- # by providing additional options for a package manager to use when
- # installing a package.
- #
- # This list is used by the Chef client auditing system to extract
- # information from resources to describe changes made to the system.
- #
- # This method is unnecessary when declaring properties with `property`;
- # properties are added to state_properties by default, and can be turned off
- # with `desired_state: false`.
- #
- # ```ruby
- # property :x # part of desired state
- # property :y, desired_state: false # not part of desired state
- # ```
- #
- # @param names [Array<Symbol>] A list of property names to set as desired
- # state.
- #
- # @return [Array<Property>] All properties in desired state.
- #
- def self.state_properties(*names)
- if !names.empty?
- names = names.map { |name| name.to_sym }.uniq
-
- local_properties = properties(false)
- # Add new properties to the list.
- names.each do |name|
- property = properties[name]
- if !property
- self.property name, instance_variable_name: false, desired_state: true
- elsif !property.desired_state?
- self.property name, desired_state: true
- end
- end
-
- # If state_attrs *excludes* something which is currently desired state,
- # mark it as desired_state: false.
- local_properties.each do |name,property|
- if property.desired_state? && !names.include?(name)
- self.property name, desired_state: false
- end
- end
- end
-
- properties.values.select { |property| property.desired_state? }
- end
-
- #
# Set or return the list of "state properties" implemented by the Resource
# subclass.
#
@@ -975,56 +742,6 @@ class Chef
end
#
- # Set the identity of this resource to a particular set of properties.
- #
- # This drives #identity, which returns data that uniquely refers to a given
- # resource on the given node (in such a way that it can be correlated
- # across Chef runs).
- #
- # This method is unnecessary when declaring properties with `property`;
- # properties can be added to identity during declaration with
- # `identity: true`.
- #
- # ```ruby
- # property :x, identity: true # part of identity
- # property :y # not part of identity
- # ```
- #
- # If no properties are marked as identity, "name" is considered the identity.
- #
- # @param names [Array<Symbol>] A list of property names to set as the identity.
- #
- # @return [Array<Property>] All identity properties.
- #
- def self.identity_properties(*names)
- if !names.empty?
- names = names.map { |name| name.to_sym }
-
- # Add or change properties that are not part of the identity.
- names.each do |name|
- property = properties[name]
- if !property
- self.property name, instance_variable_name: false, identity: true
- elsif !property.identity?
- self.property name, identity: true
- end
- end
-
- # If identity_properties *excludes* something which is currently part of
- # the identity, mark it as identity: false.
- properties.each do |name,property|
- if property.identity? && !names.include?(name)
- self.property name, identity: false
- end
- end
- end
-
- result = properties.values.select { |property| property.identity? }
- result = [ properties[:name] ] if result.empty?
- result
- end
-
- #
# Set the identity of this resource to a particular property.
#
# This drives #identity, which returns data that uniquely refers to a given
@@ -1211,7 +928,7 @@ class Chef
# @deprecated Use resource_name instead.
#
def self.dsl_name
- Chef::Log.deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13. Use resource_name instead."
+ Chef.log_deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13. Use resource_name instead."
if name
name = self.name.split('::')[-1]
convert_to_snake_case(name)
@@ -1288,7 +1005,7 @@ class Chef
#
def self.provider_base(arg=nil)
if arg
- Chef::Log.deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
+ Chef.log_deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
end
@provider_base ||= arg || Chef::Provider
end
@@ -1376,7 +1093,8 @@ class Chef
#
def self.action(action, &recipe_block)
action = action.to_sym
- new_action_provider_class.action(action, &recipe_block)
+ declare_action_class
+ action_class.action(action, &recipe_block)
self.allowed_actions += [ action ]
default_action action if Array(default_action) == [:nothing]
end
@@ -1410,7 +1128,7 @@ class Chef
# @return A new copy of the resource, with values filled in from the actual
# current value.
#
- def current_resource
+ def current_value
provider = provider_for_action(Array(action).first)
if provider.whyrun_mode? && !provider.whyrun_supported?
raise "Cannot retrieve #{self.class.current_resource} in why-run mode: #{provider} does not support why-run"
@@ -1420,7 +1138,7 @@ class Chef
end
#
- # The action provider class is an automatic `Provider` created to handle
+ # The action class is an automatic `Provider` created to handle
# actions declared by `action :x do ... end`.
#
# This class will be returned by `resource.provider` if `resource.provider`
@@ -1429,40 +1147,38 @@ class Chef
#
# If the user has not declared actions on this class or its superclasses
# using `action :x do ... end`, then there is no need for this class and
- # `action_provider_class` will be `nil`.
+ # `action_class` will be `nil`.
#
# @api private
#
- def self.action_provider_class
- @action_provider_class ||
+ def self.action_class
+ @action_class ||
# If the superclass needed one, then we need one as well.
- if superclass.respond_to?(:action_provider_class) && superclass.action_provider_class
- new_action_provider_class
+ if superclass.respond_to?(:action_class) && superclass.action_class
+ declare_action_class
end
end
#
- # Ensure the action provider class actually gets created. This is called
+ # Ensure the action class actually gets created. This is called
# when the user does `action :x do ... end`.
#
+ # If a block is passed, it is run inside the action_class.
+ #
# @api private
- def self.new_action_provider_class
- return @action_provider_class if @action_provider_class
+ def self.declare_action_class
+ return @action_class if @action_class
- if superclass.respond_to?(:action_provider_class)
- base_provider = superclass.action_provider_class
+ if superclass.respond_to?(:action_class)
+ base_provider = superclass.action_class
end
base_provider ||= Chef::Provider
resource_class = self
- @action_provider_class = Class.new(base_provider) do
- include ActionProvider
- define_singleton_method(:to_s) { "#{resource_class} action provider" }
- def self.inspect
- to_s
- end
+ @action_class = Class.new(base_provider) do
+ include ActionClass
+ self.resource_class = resource_class
end
- @action_provider_class
end
#
diff --git a/lib/chef/resource/action_provider.rb b/lib/chef/resource/action_class.rb
index d71b54ef4d..12211418e9 100644
--- a/lib/chef/resource/action_provider.rb
+++ b/lib/chef/resource/action_class.rb
@@ -20,7 +20,7 @@ require 'chef/exceptions'
class Chef
class Resource
- module ActionProvider
+ module ActionClass
#
# If load_current_value! is defined on the resource, use that.
#
@@ -63,6 +63,20 @@ class Chef
end
module ClassMethods
+ #
+ # The Chef::Resource class this ActionClass was declared against.
+ #
+ # @return [Class] The Chef::Resource class this ActionClass was declared against.
+ #
+ attr_accessor :resource_class
+
+ def to_s
+ "#{resource_class} action provider"
+ end
+
+ def inspect
+ to_s
+ end
end
end
end
diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb
index 0c2fdfa819..7e9d21ebd2 100644
--- a/lib/chef/resource/chef_gem.rb
+++ b/lib/chef/resource/chef_gem.rb
@@ -50,9 +50,9 @@ class Chef
# Chef::Resource.run_action: Caveat: this skips Chef::Runner.run_action, where notifications are handled
# Action could be an array of symbols, but probably won't (think install + enable for a package)
if compile_time.nil?
- Chef::Log.deprecation "#{self} chef_gem compile_time installation is deprecated"
- Chef::Log.deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
- Chef::Log.deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
+ Chef.log_deprecation "#{self} chef_gem compile_time installation is deprecated"
+ Chef.log_deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
+ Chef.log_deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
end
if compile_time || compile_time.nil?
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index ec669a75d3..11c4ae045c 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -102,7 +102,7 @@ class Chef
end
def path(arg=nil)
- Chef::Log.warn "'path' attribute of 'execute' is not used by any provider in Chef 11 and Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13."
+ Chef::Log.warn "The 'path' attribute of 'execute' is not used by any provider in Chef 11 or Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13."
set_or_return(
:path,
diff --git a/lib/chef/resource/file/verification.rb b/lib/chef/resource/file/verification.rb
index faf4791884..ba0bb08201 100644
--- a/lib/chef/resource/file/verification.rb
+++ b/lib/chef/resource/file/verification.rb
@@ -108,7 +108,7 @@ class Chef
def verify_command(path, opts)
# First implementation interpolated `file`; docs & RFC claim `path`
# is interpolated. Until `file` can be deprecated, interpolate both.
- Chef::Log.deprecation(
+ Chef.log_deprecation(
'%{file} is deprecated in verify command and will not be '\
'supported in Chef 13. Please use %{path} instead.'
) if @command.include?('%{file}')
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index 443e0ed819..a9a669f18c 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -1,8 +1,8 @@
#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Author:: Christopher Walters (<cw@opscode.com>)
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2008-2012 Opscode, Inc.
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Christopher Walters (<cw@chef.io>)
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,7 +45,7 @@ class Chef
def build_from_file(cookbook_name, filename, run_context)
if LWRPBase.loaded_lwrps[filename]
- Chef::Log.info("LWRP resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ Chef::Log.info("Custom resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
return loaded_lwrps[filename]
end
@@ -60,7 +60,7 @@ class Chef
# Make a useful string for the class (rather than <Class:312894723894>)
resource_class.instance_eval do
define_singleton_method(:to_s) do
- "LWRP resource #{resource_name} from cookbook #{cookbook_name}"
+ "Custom resource #{resource_name} from cookbook #{cookbook_name}"
end
define_singleton_method(:inspect) { to_s }
end
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index 30bed367cb..5081adf918 100644
--- a/lib/chef/resource/script.rb
+++ b/lib/chef/resource/script.rb
@@ -40,7 +40,7 @@ class Chef
unless arg.nil?
# Chef-13: change this to raise if the user is trying to set a value here
Chef::Log.warn "Specifying command attribute on a script resource is a coding error, use the 'code' attribute, or the execute resource"
- Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef-13"
+ Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef 13"
end
super
end
diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb
index ae6a37caa2..a6f4cb4897 100644
--- a/lib/chef/resource/subversion.rb
+++ b/lib/chef/resource/subversion.rb
@@ -28,12 +28,17 @@ class Chef
super
@svn_arguments = '--no-auth-cache'
@svn_info_args = '--no-auth-cache'
+ @svn_binary = nil
end
# Override exception to strip password if any, so it won't appear in logs and different Chef notifications
def custom_exception_message(e)
"#{self} (#{defined_at}) had an error: #{e.class.name}: #{svn_password ? e.message.gsub(svn_password, "[hidden_password]") : e.message}"
end
+
+ def svn_binary(arg=nil)
+ set_or_return(:svn_binary, arg, :kind_of => [String])
+ end
end
end
end
diff --git a/lib/chef/resource/windows_script.rb b/lib/chef/resource/windows_script.rb
index 48e2b535a8..2bbd01d5aa 100644
--- a/lib/chef/resource/windows_script.rb
+++ b/lib/chef/resource/windows_script.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/platform/query_helpers'
require 'chef/resource/script'
require 'chef/mixin/windows_architecture_helper'
@@ -51,9 +52,12 @@ class Chef
protected
def assert_architecture_compatible!(desired_architecture)
- if ! node_supports_windows_architecture?(node, desired_architecture)
+ if desired_architecture == :i386 && Chef::Platform.windows_nano_server?
raise Chef::Exceptions::Win32ArchitectureIncorrect,
- "cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'"
+ "cannot execute script with requested architecture 'i386' on Windows Nano Server"
+ elsif ! node_supports_windows_architecture?(node, desired_architecture)
+ raise Chef::Exceptions::Win32ArchitectureIncorrect,
+ "cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'"
end
end
end
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 7d13a5a5ce..1175b0afb3 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -112,6 +112,7 @@ class Chef
@exception = nil
@rest_client = rest_client
@error_descriptions = {}
+ @expanded_run_list = {}
end
def run_started(run_status)
@@ -217,6 +218,10 @@ class Chef
end
end
+ def run_list_expanded(run_list_expansion)
+ @expanded_run_list = run_list_expansion
+ end
+
def post_reporting_data
if reporting_enabled?
run_data = prepare_run_data
@@ -271,6 +276,7 @@ class Chef
run_data["data"] = {}
run_data["start_time"] = start_time.to_s
run_data["end_time"] = end_time.to_s
+ run_data["expanded_run_list"] = Chef::JSONCompat.to_json(@expanded_run_list)
if exception
exception_data = {}
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 47b3df18af..67cf134c62 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -56,7 +56,7 @@ class Chef
attr_reader :resource_name
# @api private
def resource
- Chef::Log.deprecation("Chef::ResourceResolver.resource deprecated. Use resource_name instead.")
+ Chef.log_deprecation("Chef::ResourceResolver.resource deprecated. Use resource_name instead.")
resource_name
end
# @api private
@@ -174,8 +174,8 @@ class Chef
if handlers.empty?
handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
handlers.each do |handler|
- Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ Chef.log_deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
+ Chef.log_deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
end
end
handlers
diff --git a/lib/chef/rest.rb b/lib/chef/rest.rb
index f87cec9b76..4106a01077 100644
--- a/lib/chef/rest.rb
+++ b/lib/chef/rest.rb
@@ -166,7 +166,7 @@ class Chef
def retriable_http_request(method, url, req_body, headers)
rest_request = Chef::HTTP::HTTPRequest.new(method, url, req_body, headers)
- Chef::Log.debug("Sending HTTP Request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
+ Chef::Log.debug("Sending HTTP request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
retrying_http_errors(url) do
yield rest_request
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index b1113f594e..f7ab88f7e0 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -268,7 +268,7 @@ class Chef
# @see DSL::IncludeRecipe#load_recipe
#
def load_recipe(recipe_name, current_cookbook: nil)
- Chef::Log.debug("Loading Recipe #{recipe_name} via include_recipe")
+ Chef::Log.debug("Loading recipe #{recipe_name} via include_recipe")
cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name, current_cookbook: current_cookbook)
@@ -308,7 +308,7 @@ ERROR_MESSAGE
raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
end
- Chef::Log.debug("Loading Recipe File #{recipe_file}")
+ Chef::Log.debug("Loading recipe file #{recipe_file}")
recipe = Chef::Recipe.new('@recipe_files', recipe_file, self)
recipe.from_file(recipe_file)
recipe
@@ -522,6 +522,9 @@ ERROR_MESSAGE
ChildRunContext.new(self)
end
+ # @api private
+ attr_writer :resource_collection
+
protected
attr_reader :cookbook_compiler
@@ -532,23 +535,18 @@ ERROR_MESSAGE
###
# These need to be settable so deploy can run a resource_collection
# independent of any cookbooks via +recipe_eval+
- def resource_collection=(value)
- Chef::Log.deprecation("Setting run_context.resource_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
- @resource_collection = value
- end
-
def audits=(value)
- Chef::Log.deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ Chef.log_deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
@audits = value
end
def immediate_notification_collection=(value)
- Chef::Log.deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ Chef.log_deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
@immediate_notification_collection = value
end
def delayed_notification_collection=(value)
- Chef::Log.deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ Chef.log_deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
@delayed_notification_collection = value
end
end
diff --git a/lib/chef/run_list/run_list_expansion.rb b/lib/chef/run_list/run_list_expansion.rb
index 46b45f1d9e..64e4326fb8 100644
--- a/lib/chef/run_list/run_list_expansion.rb
+++ b/lib/chef/run_list/run_list_expansion.rb
@@ -22,6 +22,7 @@ require 'chef/mixin/deep_merge'
require 'chef/role'
require 'chef/rest'
+require 'chef/json_compat'
class Chef
class RunList
@@ -54,6 +55,13 @@ class Chef
# * Duplicate roles are not shown.
attr_reader :run_list_trace
+ # Like run list trace but instead of saving the entries as strings it saves their objects
+ # The to_json method uses this list to construct json.
+ attr_reader :better_run_list_trace
+
+ attr_reader :all_missing_roles
+ attr_reader :role_errors
+
def initialize(environment, run_list_items, source=nil)
@environment = environment
@missing_roles_with_including_role = Array.new
@@ -68,6 +76,9 @@ class Chef
@applied_roles = {}
@run_list_trace = Hash.new {|h, key| h[key] = [] }
+ @better_run_list_trace = Hash.new {|h, key| h[key] = [] }
+ @all_missing_roles = {}
+ @role_errors = {}
end
# Did we find any errors (expanding roles)?
@@ -124,6 +135,7 @@ class Chef
def role_not_found(name, included_by)
Chef::Log.error("Role #{name} (included by '#{included_by}') is in the runlist but does not exist. Skipping expand.")
@missing_roles_with_including_role << [name, included_by]
+ @all_missing_roles[name] = true
nil
end
@@ -131,6 +143,15 @@ class Chef
@missing_roles_with_including_role.map {|item| item.first }
end
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def to_hash
+ seen_items = {:recipe => {}, :role => {}}
+ {:id => @environment, :run_list => convert_run_list_trace('top level', seen_items)}
+ end
+
private
# these methods modifies internal state based on arguments, so hide it.
@@ -140,8 +161,10 @@ class Chef
end
def expand_run_list_items(items, included_by="top level")
+
if entry = items.shift
@run_list_trace[included_by.to_s] << entry.to_s
+ @better_run_list_trace[included_by.to_s] << entry
case entry.type
when :recipe
@@ -156,8 +179,26 @@ class Chef
end
end
+ # Recursive helper to decode the non-nested hash form back into a tree
+ def convert_run_list_trace(base, seen_items)
+ @better_run_list_trace[base].map do |item|
+ skipped = seen_items[item.type][item.name]
+ seen_items[item.type][item.name] = true
+ case item.type
+ when :recipe
+ {:type => 'recipe', :name => item.name, :version => item.version, :skipped => !!skipped}
+ when :role
+ error = @role_errors[item.name]
+ missing = @all_missing_roles[item.name]
+ {:type => :role, :name => item.name, :children => (missing || error || skipped) ? [] : convert_run_list_trace(item.to_s, seen_items),
+ :missing => missing, :error => error, :skipped => skipped}
+ end
+ end
+ end
+
end
+
# Expand a run list from disk. Suitable for chef-solo
class RunListExpansionFromDisk < RunListExpansion
@@ -184,8 +225,14 @@ class Chef
else
raise
end
+ rescue Exception => e
+ @role_errors[name] = e.to_s
+ raise
end
+
end
end
end
+
+
diff --git a/lib/chef/run_list/versioned_recipe_list.rb b/lib/chef/run_list/versioned_recipe_list.rb
index 2824f08f31..803156aef9 100644
--- a/lib/chef/run_list/versioned_recipe_list.rb
+++ b/lib/chef/run_list/versioned_recipe_list.rb
@@ -82,6 +82,21 @@ class Chef
qualified_recipe
end
end
+
+ # Get an array of strings of both fully-qualified and unexpanded recipe names
+ # in response to chef/chef#3767
+ # Chef-13 will revert to the behaviour of just including the fully-qualified name
+ #
+ # @return [Array] Array of strings with fully-qualified and unexpanded recipe names
+ def with_duplicate_names
+ self.map do |recipe_name|
+ if recipe_name.include?('::')
+ recipe_name
+ else
+ [recipe_name, "#{recipe_name}::default"]
+ end
+ end.flatten
+ end
end
end
end
diff --git a/lib/chef/run_lock.rb b/lib/chef/run_lock.rb
index cefe637db6..9e0952bdcb 100644
--- a/lib/chef/run_lock.rb
+++ b/lib/chef/run_lock.rb
@@ -87,27 +87,8 @@ class Chef
# Either acquire() or test() methods should be called in order to
# get the ownership of run_lock.
def test
- # ensure the runlock_file path exists
- create_path(File.dirname(runlock_file))
- @runlock = File.open(runlock_file,'a+')
-
- if Chef::Platform.windows?
- acquire_win32_mutex
- else
- # If we support FD_CLOEXEC, then use it.
- # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not
- # ruby-1.8.7/1.9.3
- if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC')
- runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC)
- end
- # Flock will return 0 if it can acquire the lock otherwise it
- # will return false
- if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0
- true
- else
- false
- end
- end
+ create_lock
+ acquire_lock
end
#
@@ -147,6 +128,34 @@ class Chef
end
end
+ # @api private solely for race condition tests
+ def create_lock
+ # ensure the runlock_file path exists
+ create_path(File.dirname(runlock_file))
+ @runlock = File.open(runlock_file,'a+')
+ end
+
+ # @api private solely for race condition tests
+ def acquire_lock
+ if Chef::Platform.windows?
+ acquire_win32_mutex
+ else
+ # If we support FD_CLOEXEC, then use it.
+ # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not
+ # ruby-1.8.7/1.9.3
+ if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC')
+ runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC)
+ end
+ # Flock will return 0 if it can acquire the lock otherwise it
+ # will return false
+ if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0
+ true
+ else
+ false
+ end
+ end
+ end
+
private
def reset
diff --git a/lib/chef/search/query.rb b/lib/chef/search/query.rb
index 6469a18c49..658af8779c 100644
--- a/lib/chef/search/query.rb
+++ b/lib/chef/search/query.rb
@@ -88,8 +88,21 @@ WARNDEP
if block
response["rows"].each { |row| block.call(row) if row }
- unless (response["start"] + response["rows"].length) >= response["total"]
- args_h[:start] = response["start"] + response["rows"].length
+ #
+ # args_h[:rows] and args_h[:start] are the page size and
+ # start position requested of the search index backing the
+ # search API.
+ #
+ # The response may contain fewer rows than arg_h[:rows] if
+ # the page of index results included deleted nodes which
+ # have been filtered from the returned data. In this case,
+ # we still want to start the next page at start +
+ # args_h[:rows] to avoid asking the search backend for
+ # overlapping pages (which could result in duplicates).
+ #
+ next_start = response["start"] + (args_h[:rows] || response["rows"].length)
+ unless next_start >= response["total"]
+ args_h[:start] = next_start
search(type, query, args_h, &block)
end
true
@@ -99,6 +112,7 @@ WARNDEP
end
private
+
def validate_type(t)
unless t.kind_of?(String) || t.kind_of?(Symbol)
msg = "Invalid search object type #{t.inspect} (#{t.class}), must be a String or Symbol." +
diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb
index c2dc6e045c..b8336b5135 100644
--- a/lib/chef/util/diff.rb
+++ b/lib/chef/util/diff.rb
@@ -64,7 +64,7 @@ class Chef
def use_tempfile_if_missing(file)
tempfile = nil
unless File.exists?(file)
- Chef::Log.debug("file #{file} does not exist to diff against, using empty tempfile")
+ Chef::Log.debug("File #{file} does not exist to diff against, using empty tempfile")
tempfile = Tempfile.new("chef-diff")
file = tempfile.path
end
@@ -139,7 +139,7 @@ class Chef
return "(new content is binary, diff output suppressed)" if is_binary?(new_file)
begin
- Chef::Log.debug("running: diff -u #{old_file} #{new_file}")
+ Chef::Log.debug("Running: diff -u #{old_file} #{new_file}")
diff_str = udiff(old_file, new_file)
rescue Exception => e
diff --git a/lib/chef/util/windows.rb b/lib/chef/util/windows.rb
index 777fe4adbb..7d29a67ac5 100644
--- a/lib/chef/util/windows.rb
+++ b/lib/chef/util/windows.rb
@@ -15,42 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#requires: gem install windows-pr
-require 'windows/api'
-require 'windows/error'
-require 'windows/handle'
-require 'windows/unicode'
-require 'windows/msvcrt/buffer'
-require 'windows/msvcrt/string'
-require 'windows/network/management'
class Chef
class Util
class Windows
- protected
-
- include ::Windows::Error
- include ::Windows::Unicode
- include ::Windows::MSVCRT::Buffer
- include ::Windows::MSVCRT::String
- include ::Windows::Network::Management
-
- PTR_SIZE = 4 #XXX 64-bit
-
- def lpwstr_to_s(buffer, offset)
- str = 0.chr * (256 * 2) #XXX unhardcode this length (*2 for WCHAR)
- wcscpy str, buffer[offset*PTR_SIZE,PTR_SIZE].unpack('L')[0]
- wide_to_multi str
- end
-
- def dword_to_i(buffer, offset)
- buffer[offset*PTR_SIZE,PTR_SIZE].unpack('i')[0] || 0
- end
-
- #return pointer for use with pack('L')
- def str_to_ptr(v)
- [v].pack('p*').unpack('L')[0]
- end
end
end
end
diff --git a/lib/chef/util/windows/net_use.rb b/lib/chef/util/windows/net_use.rb
index 62d7e169dc..b94576e702 100644
--- a/lib/chef/util/windows/net_use.rb
+++ b/lib/chef/util/windows/net_use.rb
@@ -21,61 +21,18 @@
#see also cmd.exe: net use /?
require 'chef/util/windows'
+require 'chef/win32/net'
class Chef::Util::Windows::NetUse < Chef::Util::Windows
-
- private
-
- USE_NOFORCE = 0
- USE_FORCE = 1
- USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
-
- USE_INFO_2 = [
- [:local, nil],
- [:remote, nil],
- [:password, nil],
- [:status, 0],
- [:asg_type, 0],
- [:refcount, 0],
- [:usecount, 0],
- [:username, nil],
- [:domainname, nil]
- ]
-
- USE_INFO_2_TEMPLATE =
- USE_INFO_2.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join
-
- SIZEOF_USE_INFO_2 = #sizeof(USE_INFO_2)
- USE_INFO_2.inject(0) do |sum, item|
- sum + (item[1].class == Fixnum ? 4 : PTR_SIZE)
- end
-
- def use_info_2(args)
- USE_INFO_2.collect { |field|
- args.include?(field[0]) ? args[field[0]] : field[1]
- }
- end
-
- def use_info_2_pack(use)
- use.collect { |v|
- v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v))
- }.pack(USE_INFO_2_TEMPLATE)
+ def initialize(localname)
+ @use_name = localname
end
- def use_info_2_unpack(buffer)
- use = Hash.new
- USE_INFO_2.each_with_index do |field,offset|
- use[field[0]] = field[1].class == Fixnum ?
- dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset)
+ def to_ui2_struct(use_info)
+ use_info.inject({}) do |memo, (k,v)|
+ memo["ui2_#{k}".to_sym] = v
+ memo
end
- use
- end
-
- public
-
- def initialize(localname)
- @localname = localname
- @name = multi_to_wide(localname)
end
def add(args)
@@ -84,38 +41,45 @@ class Chef::Util::Windows::NetUse < Chef::Util::Windows
args = Hash.new
args[:remote] = remote
end
- args[:local] ||= @localname
- use = use_info_2(args)
- buffer = use_info_2_pack(use)
- rc = NetUseAdd.call(nil, 2, buffer, nil)
- if rc != NERR_Success
- raise ArgumentError, get_last_error(rc)
+ args[:local] ||= use_name
+ ui2_hash = to_ui2_struct(args)
+
+ begin
+ Chef::ReservedNames::Win32::Net.net_use_add_l2(nil, ui2_hash)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
end
- def get_info
- ptr = 0.chr * PTR_SIZE
- rc = NetUseGetInfo.call(nil, @name, 2, ptr)
-
- if rc != NERR_Success
- raise ArgumentError, get_last_error(rc)
+ def from_use_info_struct(ui2_hash)
+ ui2_hash.inject({}) do |memo, (k,v)|
+ memo[k.to_s.sub('ui2_', '').to_sym] = v
+ memo
end
+ end
- ptr = ptr.unpack('L')[0]
- buffer = 0.chr * SIZEOF_USE_INFO_2
- memcpy(buffer, ptr, buffer.size)
- NetApiBufferFree(ptr)
- use_info_2_unpack(buffer)
+ def get_info
+ begin
+ ui2 = Chef::ReservedNames::Win32::Net.net_use_get_info_l2(nil, use_name)
+ from_use_info_struct(ui2)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
+ end
end
def device
get_info()[:remote]
end
- #XXX should we use some FORCE here?
+
def delete
- rc = NetUseDel.call(nil, @name, USE_NOFORCE)
- if rc != NERR_Success
- raise ArgumentError, get_last_error(rc)
+ begin
+ Chef::ReservedNames::Win32::Net.net_use_del(nil, use_name, :use_noforce)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
end
+
+ def use_name
+ @use_name
+ end
end
diff --git a/lib/chef/util/windows/net_user.rb b/lib/chef/util/windows/net_user.rb
index 26fbe53db6..4ce051228a 100644
--- a/lib/chef/util/windows/net_user.rb
+++ b/lib/chef/util/windows/net_user.rb
@@ -88,7 +88,6 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
def initialize(username)
@username = username
- @name = multi_to_wide(username)
end
LOGON32_PROVIDER_DEFAULT = Security::LOGON32_PROVIDER_DEFAULT
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index faa61aee54..c769533aa6 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -21,7 +21,7 @@
class Chef
CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
- VERSION = '12.5.0.current.0'
+ VERSION = '12.5.1'
end
#
diff --git a/lib/chef/win32/api/file.rb b/lib/chef/win32/api/file.rb
index 728a6c14df..9ff1ad40d6 100644
--- a/lib/chef/win32/api/file.rb
+++ b/lib/chef/win32/api/file.rb
@@ -20,6 +20,7 @@
require 'chef/win32/api'
require 'chef/win32/api/security'
require 'chef/win32/api/system'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb
index 082cf4bb9a..b173987a05 100644
--- a/lib/chef/win32/api/net.rb
+++ b/lib/chef/win32/api/net.rb
@@ -17,6 +17,7 @@
#
require 'chef/win32/api'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
@@ -40,6 +41,10 @@ class Chef
UF_NORMAL_ACCOUNT = 0x000200
UF_DONT_EXPIRE_PASSWD = 0x010000
+ USE_NOFORCE = 0
+ USE_FORCE = 1
+ USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
+
NERR_Success = 0
NERR_InvalidComputer = 2351
NERR_NotPrimary = 2226
@@ -55,37 +60,7 @@ class Chef
ffi_lib "netapi32"
- class USER_INFO_3 < FFI::Struct
- layout :usri3_name, :LPWSTR,
- :usri3_password, :LPWSTR,
- :usri3_password_age, :DWORD,
- :usri3_priv, :DWORD,
- :usri3_home_dir, :LPWSTR,
- :usri3_comment, :LPWSTR,
- :usri3_flags, :DWORD,
- :usri3_script_path, :LPWSTR,
- :usri3_auth_flags, :DWORD,
- :usri3_full_name, :LPWSTR,
- :usri3_usr_comment, :LPWSTR,
- :usri3_parms, :LPWSTR,
- :usri3_workstations, :LPWSTR,
- :usri3_last_logon, :DWORD,
- :usri3_last_logoff, :DWORD,
- :usri3_acct_expires, :DWORD,
- :usri3_max_storage, :DWORD,
- :usri3_units_per_week, :DWORD,
- :usri3_logon_hours, :PBYTE,
- :usri3_bad_pw_count, :DWORD,
- :usri3_num_logons, :DWORD,
- :usri3_logon_server, :LPWSTR,
- :usri3_country_code, :DWORD,
- :usri3_code_page, :DWORD,
- :usri3_user_id, :DWORD,
- :usri3_primary_group_id, :DWORD,
- :usri3_profile, :LPWSTR,
- :usri3_home_dir_drive, :LPWSTR,
- :usri3_password_expired, :DWORD
-
+ module StructHelpers
def set(key, val)
val = if val.is_a? String
encoded = if val.encoding == Encoding::UTF_16LE
@@ -117,6 +92,47 @@ class Chef
end
end
+ def as_ruby
+ members.inject({}) do |memo, key|
+ memo[key] = get(key)
+ memo
+ end
+ end
+ end
+
+
+ class USER_INFO_3 < FFI::Struct
+ include StructHelpers
+ layout :usri3_name, :LPWSTR,
+ :usri3_password, :LPWSTR,
+ :usri3_password_age, :DWORD,
+ :usri3_priv, :DWORD,
+ :usri3_home_dir, :LPWSTR,
+ :usri3_comment, :LPWSTR,
+ :usri3_flags, :DWORD,
+ :usri3_script_path, :LPWSTR,
+ :usri3_auth_flags, :DWORD,
+ :usri3_full_name, :LPWSTR,
+ :usri3_usr_comment, :LPWSTR,
+ :usri3_parms, :LPWSTR,
+ :usri3_workstations, :LPWSTR,
+ :usri3_last_logon, :DWORD,
+ :usri3_last_logoff, :DWORD,
+ :usri3_acct_expires, :DWORD,
+ :usri3_max_storage, :DWORD,
+ :usri3_units_per_week, :DWORD,
+ :usri3_logon_hours, :PBYTE,
+ :usri3_bad_pw_count, :DWORD,
+ :usri3_num_logons, :DWORD,
+ :usri3_logon_server, :LPWSTR,
+ :usri3_country_code, :DWORD,
+ :usri3_code_page, :DWORD,
+ :usri3_user_id, :DWORD,
+ :usri3_primary_group_id, :DWORD,
+ :usri3_profile, :LPWSTR,
+ :usri3_home_dir_drive, :LPWSTR,
+ :usri3_password_expired, :DWORD
+
def usri3_logon_hours
val = self[:usri3_logon_hours]
if !val.nil? && !val.null?
@@ -125,13 +141,6 @@ class Chef
nil
end
end
-
- def as_ruby
- members.inject({}) do |memo, key|
- memo[key] = get(key)
- memo
- end
- end
end
class LOCALGROUP_MEMBERS_INFO_0 < FFI::Struct
@@ -146,19 +155,36 @@ class Chef
layout :lgrpi0_name, :LPWSTR
end
+ class USE_INFO_2 < FFI::Struct
+ include StructHelpers
+
+ layout :ui2_local, :LMSTR,
+ :ui2_remote, :LMSTR,
+ :ui2_password, :LMSTR,
+ :ui2_status, :DWORD,
+ :ui2_asg_type, :DWORD,
+ :ui2_refcount, :DWORD,
+ :ui2_usecount, :DWORD,
+ :ui2_username, :LPWSTR,
+ :ui2_domainname, :LMSTR
+ end
+
+
#NET_API_STATUS NetLocalGroupAdd(
#_In_ LPCWSTR servername,
#_In_ DWORD level,
#_In_ LPBYTE buf,
#_Out_ LPDWORD parm_err
#);
- safe_attach_function :NetLocalGroupAdd, [ :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
+ safe_attach_function :NetLocalGroupAdd, [
+ :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupDel(
#_In_ LPCWSTR servername,
#_In_ LPCWSTR groupname
#);
- safe_attach_function :NetLocalGroupDel, [ :LPCWSTR, :LPCWSTR], :DWORD
+ safe_attach_function :NetLocalGroupDel, [:LPCWSTR, :LPCWSTR], :DWORD
#NET_API_STATUS NetLocalGroupGetMembers(
#_In_ LPCWSTR servername,
@@ -170,7 +196,7 @@ class Chef
#_Out_ LPDWORD totalentries,
#_Inout_ PDWORD_PTR resumehandle
#);
- safe_attach_function :NetLocalGroupGetMembers, [
+ safe_attach_function :NetLocalGroupGetMembers, [
:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD,
:LPDWORD, :LPDWORD, :PDWORD_PTR
], :DWORD
@@ -185,12 +211,15 @@ class Chef
# _Out_ LPDWORD totalentries,
# _Inout_ LPDWORD resume_handle
# );
- safe_attach_function :NetUserEnum, [ :LPCWSTR, :DWORD, :DWORD, :LPBYTE, :DWORD, :LPDWORD, :LPDWORD, :LPDWORD ], :DWORD
+ safe_attach_function :NetUserEnum, [
+ :LPCWSTR, :DWORD, :DWORD, :LPBYTE,
+ :DWORD, :LPDWORD, :LPDWORD, :LPDWORD
+ ], :DWORD
# NET_API_STATUS NetApiBufferFree(
# _In_ LPVOID Buffer
# );
- safe_attach_function :NetApiBufferFree, [ :LPVOID ], :DWORD
+ safe_attach_function :NetApiBufferFree, [:LPVOID], :DWORD
#NET_API_STATUS NetUserAdd(
#_In_ LMSTR servername,
@@ -198,7 +227,9 @@ class Chef
#_In_ LPBYTE buf,
#_Out_ LPDWORD parm_err
#);
- safe_attach_function :NetUserAdd, [:LMSTR, :DWORD, :LPBYTE, :LPDWORD ], :DWORD
+ safe_attach_function :NetUserAdd, [
+ :LMSTR, :DWORD, :LPBYTE, :LPDWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupAddMembers(
# _In_ LPCWSTR servername,
@@ -207,7 +238,9 @@ class Chef
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
#);
- safe_attach_function :NetLocalGroupAddMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD
+ safe_attach_function :NetLocalGroupAddMembers, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupSetMembers(
# _In_ LPCWSTR servername,
@@ -216,7 +249,9 @@ class Chef
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
#);
- safe_attach_function :NetLocalGroupSetMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD
+ safe_attach_function :NetLocalGroupSetMembers, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupDelMembers(
# _In_ LPCWSTR servername,
@@ -225,7 +260,9 @@ class Chef
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
#);
- safe_attach_function :NetLocalGroupDelMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD
+ safe_attach_function :NetLocalGroupDelMembers, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
+ ], :DWORD
#NET_API_STATUS NetUserGetInfo(
# _In_ LPCWSTR servername,
@@ -233,7 +270,9 @@ class Chef
# _In_ DWORD level,
# _Out_ LPBYTE *bufptr
#);
- safe_attach_function :NetUserGetInfo, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE], :DWORD
+ safe_attach_function :NetUserGetInfo, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE
+ ], :DWORD
#NET_API_STATUS NetApiBufferFree(
# _In_ LPVOID Buffer
@@ -247,7 +286,9 @@ class Chef
# _In_ LPBYTE buf,
# _Out_ LPDWORD parm_err
#);
- safe_attach_function :NetUserSetInfo, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
+ safe_attach_function :NetUserSetInfo, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD
+ ], :DWORD
#NET_API_STATUS NetUserDel(
# _In_ LPCWSTR servername,
@@ -255,6 +296,28 @@ class Chef
#);
safe_attach_function :NetUserDel, [:LPCWSTR, :LPCWSTR], :DWORD
+#NET_API_STATUS NetUseDel(
+ #_In_ LMSTR UncServerName,
+ #_In_ LMSTR UseName,
+ #_In_ DWORD ForceCond
+#);
+ safe_attach_function :NetUseDel, [:LMSTR, :LMSTR, :DWORD], :DWORD
+
+#NET_API_STATUS NetUseGetInfo(
+ #_In_ LMSTR UncServerName,
+ #_In_ LMSTR UseName,
+ #_In_ DWORD Level,
+ #_Out_ LPBYTE *BufPtr
+#);
+ safe_attach_function :NetUseGetInfo, [:LMSTR, :LMSTR, :DWORD, :pointer], :DWORD
+
+#NET_API_STATUS NetUseAdd(
+ #_In_ LMSTR UncServerName,
+ #_In_ DWORD Level,
+ #_In_ LPBYTE Buf,
+ #_Out_ LPDWORD ParmError
+#);
+ safe_attach_function :NetUseAdd, [:LMSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
end
end
end
diff --git a/lib/chef/win32/api/registry.rb b/lib/chef/win32/api/registry.rb
new file mode 100644
index 0000000000..cbbf6b66bb
--- /dev/null
+++ b/lib/chef/win32/api/registry.rb
@@ -0,0 +1,51 @@
+#
+# Author:: Salim Alam (<salam@chef.io>)
+# Copyright:: Copyright 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/win32/api'
+
+class Chef
+ module ReservedNames::Win32
+ module API
+ module Registry
+ extend Chef::ReservedNames::Win32::API
+
+ ###############################################
+ # Win32 API Bindings
+ ###############################################
+
+ ffi_lib 'advapi32'
+
+ # LONG WINAPI RegDeleteKeyEx(
+ # _In_ HKEY hKey,
+ # _In_ LPCTSTR lpSubKey,
+ # _In_ REGSAM samDesired,
+ # _Reserved_ DWORD Reserved
+ # );
+ safe_attach_function :RegDeleteKeyExW, [ :HKEY, :LPCTSTR, :LONG, :DWORD ], :LONG
+ safe_attach_function :RegDeleteKeyExA, [ :HKEY, :LPCTSTR, :LONG, :DWORD ], :LONG
+
+ # LONG WINAPI RegDeleteValue(
+ # _In_ HKEY hKey,
+ # _In_opt_ LPCTSTR lpValueName
+ # );
+ safe_attach_function :RegDeleteValueW, [ :HKEY, :LPCTSTR ], :LONG
+
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/win32/api/unicode.rb b/lib/chef/win32/api/unicode.rb
index 2e3a599f0a..2a9166aa99 100644
--- a/lib/chef/win32/api/unicode.rb
+++ b/lib/chef/win32/api/unicode.rb
@@ -129,49 +129,6 @@ int WideCharToMultiByte(
=end
safe_attach_function :WideCharToMultiByte, [:UINT, :DWORD, :LPCWSTR, :int, :LPSTR, :int, :LPCSTR, :LPBOOL], :int
- ###############################################
- # Helpers
- ###############################################
-
- def utf8_to_wide(ustring)
- # ensure it is actually UTF-8
- # Ruby likes to mark binary data as ASCII-8BIT
- ustring = (ustring + "").force_encoding('UTF-8') if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
-
- # ensure we have the double-null termination Windows Wide likes
- ustring = ustring + "\000\000" if ustring.length == 0 or ustring[-1].chr != "\000"
-
- # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
- ustring = begin
- if ustring.respond_to?(:encode)
- ustring.encode('UTF-16LE')
- else
- require 'iconv'
- Iconv.conv("UTF-16LE", "UTF-8", ustring)
- end
- end
- ustring
- end
-
- def wide_to_utf8(wstring)
- # ensure it is actually UTF-16LE
- # Ruby likes to mark binary data as ASCII-8BIT
- wstring = wstring.force_encoding('UTF-16LE') if wstring.respond_to?(:force_encoding)
-
- # encode it all as UTF-8
- wstring = begin
- if wstring.respond_to?(:encode)
- wstring.encode('UTF-8')
- else
- require 'iconv'
- Iconv.conv("UTF-8", "UTF-16LE", wstring)
- end
- end
- # remove trailing CRLF and NULL characters
- wstring.strip!
- wstring
- end
-
end
end
end
diff --git a/lib/chef/win32/crypto.rb b/lib/chef/win32/crypto.rb
index 79cf51b002..e9c1da954b 100644
--- a/lib/chef/win32/crypto.rb
+++ b/lib/chef/win32/crypto.rb
@@ -19,6 +19,7 @@
require 'chef/win32/error'
require 'chef/win32/api/memory'
require 'chef/win32/api/crypto'
+require 'chef/win32/unicode'
require 'digest'
class Chef
@@ -29,7 +30,7 @@ class Chef
def self.encrypt(str, &block)
data_blob = CRYPT_INTEGER_BLOB.new
- unless CryptProtectData(CRYPT_INTEGER_BLOB.new(str.to_wstring), nil, nil, nil, nil, 0, data_blob)
+ unless CryptProtectData(CRYPT_INTEGER_BLOB.new(str.to_wstring), nil, nil, nil, nil, CRYPTPROTECT_LOCAL_MACHINE, data_blob)
Chef::ReservedNames::Win32::Error.raise!
end
bytes = data_blob[:pbData].get_bytes(0, data_blob[:cbData])
diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb
index 57347643fc..700ddb24d3 100644
--- a/lib/chef/win32/file.rb
+++ b/lib/chef/win32/file.rb
@@ -17,10 +17,11 @@
# limitations under the License.
#
+require 'chef/mixin/wide_string'
require 'chef/win32/api/file'
require 'chef/win32/api/security'
require 'chef/win32/error'
-require 'chef/mixin/wstring'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
@@ -161,9 +162,9 @@ class Chef
def self.file_access_check(path, desired_access)
security_descriptor = Chef::ReservedNames::Win32::Security.get_file_security(path)
- token_rights = Chef::ReservedNames::Win32::Security::TOKEN_IMPERSONATE |
+ token_rights = Chef::ReservedNames::Win32::Security::TOKEN_IMPERSONATE |
Chef::ReservedNames::Win32::Security::TOKEN_QUERY |
- Chef::ReservedNames::Win32::Security::TOKEN_DUPLICATE |
+ Chef::ReservedNames::Win32::Security::TOKEN_DUPLICATE |
Chef::ReservedNames::Win32::Security::STANDARD_RIGHTS_READ
token = Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
@@ -176,7 +177,7 @@ class Chef
mapping[:GenericExecute] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_EXECUTE
mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS
- Chef::ReservedNames::Win32::Security.access_check(security_descriptor, duplicate_token,
+ Chef::ReservedNames::Win32::Security.access_check(security_descriptor, duplicate_token,
desired_access, mapping)
end
diff --git a/lib/chef/win32/mutex.rb b/lib/chef/win32/mutex.rb
index 0b7d99f111..0d8eba1b3c 100644
--- a/lib/chef/win32/mutex.rb
+++ b/lib/chef/win32/mutex.rb
@@ -17,6 +17,7 @@
#
require 'chef/win32/api/synchronization'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
@@ -78,7 +79,7 @@ class Chef
# of the process goes away and this class is only being used
# to synchronize chef-clients runs on a node.
Chef::Log.error("Can not release mutex '#{name}'. This might cause issues \
-if the mutex is attempted to be acquired by other threads.")
+if other threads attempt to acquire the mutex.")
Chef::ReservedNames::Win32::Error.raise!
end
end
@@ -113,5 +114,3 @@ if the mutex is attempted to be acquired by other threads.")
end
end
end
-
-
diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb
index 0de310daf1..59f29c4d1b 100644
--- a/lib/chef/win32/net.rb
+++ b/lib/chef/win32/net.rb
@@ -18,11 +18,11 @@
require 'chef/win32/api/net'
require 'chef/win32/error'
-require 'chef/mixin/wstring'
+require 'chef/mixin/wide_string'
class Chef
module ReservedNames::Win32
- class NetUser
+ class Net
include Chef::ReservedNames::Win32::API::Error
extend Chef::ReservedNames::Win32::API::Error
@@ -286,6 +286,59 @@ END
net_api_error!(rc)
end
end
+
+ def self.net_use_del(server_name, use_name, force=:use_noforce)
+ server_name = wstring(server_name)
+ use_name = wstring(use_name)
+ force_const = case force
+ when :use_noforce
+ USE_NOFORCE
+ when :use_force
+ USE_FORCE
+ when :use_lots_of_force
+ USE_LOTS_OF_FORCE
+ else
+ raise ArgumentError, "force must be one of [:use_noforce, :use_force, or :use_lots_of_force]"
+ end
+
+ rc = NetUseDel(server_name, use_name, force_const)
+ if rc != NERR_Success
+ net_api_error!(rc)
+ end
+ end
+
+ def self.net_use_get_info_l2(server_name, use_name)
+ server_name = wstring(server_name)
+ use_name = wstring(use_name)
+ ui2_p = FFI::MemoryPointer.new(:pointer)
+
+ rc = NetUseGetInfo(server_name, use_name, 2, ui2_p)
+ if rc != NERR_Success
+ net_api_error!(rc)
+ end
+
+ ui2 = USE_INFO_2.new(ui2_p.read_pointer).as_ruby
+ NetApiBufferFree(ui2_p.read_pointer)
+
+ ui2
+ end
+
+ def self.net_use_add_l2(server_name, ui2_hash)
+ server_name = wstring(server_name)
+ group_name = wstring(group_name)
+
+ buf = USE_INFO_2.new
+
+ ui2_hash.each do |(k,v)|
+ buf.set(k,v)
+ end
+
+ rc = NetUseAdd(server_name, 2, buf, nil)
+ if rc != NERR_Success
+ net_api_error!(rc)
+ end
+ end
end
+ NetUser = Net # For backwards compatibility
end
end
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
index 1a1aa12fad..12ad08a965 100644
--- a/lib/chef/win32/registry.rb
+++ b/lib/chef/win32/registry.rb
@@ -17,8 +17,12 @@
# limitations under the License.
#
require 'chef/reserved_names'
+require 'chef/win32/api'
+require 'chef/mixin/wide_string'
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ require 'chef/monkey_patches/win32/registry'
+ require 'chef/win32/api/registry'
require 'win32/registry'
require 'win32/api'
end
@@ -27,6 +31,14 @@ class Chef
class Win32
class Registry
+ if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ include Chef::ReservedNames::Win32::API::Registry
+ extend Chef::ReservedNames::Win32::API::Registry
+ end
+
+ include Chef::Mixin::WideString
+ extend Chef::Mixin::WideString
+
attr_accessor :run_context
attr_accessor :architecture
@@ -115,38 +127,23 @@ class Chef
Chef::Log.debug("Registry key #{key_path}, does not exist, not deleting")
return true
end
- #key_path is in the form "HKLM\Software\Opscode" for example, extracting
- #hive = HKLM,
- #hive_namespace = ::Win32::Registry::HKEY_LOCAL_MACHINE
- hive = key_path.split("\\").shift
- hive_namespace, key_including_parent = get_hive_and_key(key_path)
- if has_subkeys?(key_path)
- if recursive == true
- subkeys = get_subkeys(key_path)
- subkeys.each do |key|
- keypath_to_check = hive+"\\"+key_including_parent+"\\"+key
- Chef::Log.debug("Deleting registry key #{key_path} recursively")
- delete_key(keypath_to_check, true)
- end
- delete_key_ex(hive_namespace, key_including_parent)
- else
- raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified"
- end
- else
- delete_key_ex(hive_namespace, key_including_parent)
- return true
+ if has_subkeys?(key_path) && !recursive
+ raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified"
end
+ hive, key_including_parent = get_hive_and_key(key_path)
+ # key_including_parent: Software\\Root\\Branch\\Fruit
+ # key => Fruit
+ # key_parent => Software\\Root\\Branch
+ key_parts = key_including_parent.split("\\")
+ key = key_parts.pop
+ key_parent = key_parts.join("\\")
+ hive.open(key_parent, ::Win32::Registry::KEY_WRITE | registry_system_architecture) do |reg|
+ reg.delete_key(key, recursive)
+ end
+ Chef::Log.debug("Registry key #{key_path} deleted")
true
end
- #Using the 'RegDeleteKeyEx' Windows API that correctly supports WOW64 systems (Win2003)
- #instead of the 'RegDeleteKey'
- def delete_key_ex(hive, key)
- regDeleteKeyEx = ::Win32::API.new('RegDeleteKeyEx', 'LPLL', 'L', 'advapi32')
- hive_num = hive.hkey - (1 << 32)
- regDeleteKeyEx.call(hive_num, key, ::Win32::Registry::KEY_WRITE | registry_system_architecture, 0)
- end
-
def key_exists?(key_path)
hive, key = get_hive_and_key(key_path)
begin
diff --git a/lib/chef/win32/security.rb b/lib/chef/win32/security.rb
index 5c83180bc0..bc80517d80 100644
--- a/lib/chef/win32/security.rb
+++ b/lib/chef/win32/security.rb
@@ -22,7 +22,7 @@ require 'chef/win32/memory'
require 'chef/win32/process'
require 'chef/win32/unicode'
require 'chef/win32/security/token'
-require 'chef/mixin/wstring'
+require 'chef/mixin/wide_string'
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/security/token.rb b/lib/chef/win32/security/token.rb
index 9e494a73b9..8d4e54ad8c 100644
--- a/lib/chef/win32/security/token.rb
+++ b/lib/chef/win32/security/token.rb
@@ -18,7 +18,7 @@
require 'chef/win32/security'
require 'chef/win32/api/security'
-
+require 'chef/win32/unicode'
require 'ffi'
class Chef
diff --git a/lib/chef/win32/unicode.rb b/lib/chef/win32/unicode.rb
index e7399d5255..d63b9790b9 100644
--- a/lib/chef/win32/unicode.rb
+++ b/lib/chef/win32/unicode.rb
@@ -17,6 +17,7 @@
# limitations under the License.
#
+require 'chef/mixin/wide_string'
require 'chef/win32/api/unicode'
class Chef
@@ -30,6 +31,8 @@ end
module FFI
class Pointer
+ include Chef::Mixin::WideString
+
def read_wstring(num_wchars = nil)
if num_wchars.nil?
# Find the length of the string
@@ -43,13 +46,15 @@ module FFI
num_wchars = length
end
- Chef::ReservedNames::Win32::Unicode.wide_to_utf8(self.get_bytes(0, num_wchars*2))
+ wide_to_utf8(self.get_bytes(0, num_wchars*2))
end
end
end
class String
+ include Chef::Mixin::WideString
+
def to_wstring
- Chef::ReservedNames::Win32::Unicode.utf8_to_wide(self)
+ utf8_to_wide(self)
end
end