diff options
author | AbhishekKr <abhikumar163@gmail.com> | 2016-09-10 02:28:30 +0530 |
---|---|---|
committer | AbhishekKr <abhikumar163@gmail.com> | 2016-09-10 02:28:30 +0530 |
commit | 5c01c0f74941d4f788e254ae4d040c0e4a2a477d (patch) | |
tree | 8cf25328d577db77d191d88bdc85277629553f3e /lib | |
parent | c3c6cfc156d0a0bfe7c7dd4f273a2c2bdc4b0281 (diff) | |
parent | 3731bff0641d0cb12780bc52376e50d9a0a71644 (diff) | |
download | chef-5c01c0f74941d4f788e254ae4d040c0e4a2a477d.tar.gz |
Merge branch 'master' of https://github.com/chef/chef
Diffstat (limited to 'lib')
129 files changed, 1537 insertions, 649 deletions
diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 6c2fc8b11b..f9735a3769 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -28,10 +28,7 @@ require "mixlib/cli" require "tmpdir" require "rbconfig" require "chef/application/exit_code" -require "resolv" -# on linux, we replace the glibc resolver with the ruby resolv library, which -# supports reloading. -require "resolv-replace" if RbConfig::CONFIG["host_os"] =~ /linux/ +require "yaml" class Chef class Application @@ -112,7 +109,22 @@ class Chef config_content = config_fetcher.read_config apply_config(config_content, config[:config_file]) end + extra_config_options = config.delete(:config_option) Chef::Config.merge!(config) + if extra_config_options + extra_parsed_options = extra_config_options.inject({}) do |memo, option| + # Sanity check value. + Chef::Application.fatal!("Unparsable config option #{option.inspect}") if option.empty? || !option.include?("=") + # Split including whitespace if someone does truly odd like + # --config-option "foo = bar" + key, value = option.split(/\s*=\s*/, 2) + # Call to_sym because Chef::Config expects only symbol keys. Also + # runs a simple parse on the string for some common types. + memo[key.to_sym] = YAML.safe_load(value) + memo + end + Chef::Config.merge!(extra_parsed_options) + end end def set_specific_recipes @@ -336,6 +348,13 @@ class Chef class << self def debug_stacktrace(e) message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" + + cause = e.cause if e.respond_to?(:cause) + while cause != nil + message << "\n\n>>>> Caused by #{cause.class}: #{cause}\n#{cause.backtrace.join("\n")}" + cause = cause.respond_to?(:cause) ? cause.cause : nil + end + chef_stacktrace_out = "Generated at #{Time.now}\n" chef_stacktrace_out += message diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index 2a2f70c3a1..cbaa494b71 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -41,6 +41,14 @@ class Chef::Application::Client < Chef::Application :long => "--config CONFIG", :description => "The configuration file to use" + option :config_option, + :long => "--config-option OPTION=VALUE", + :description => "Override a single configuration option", + :proc => lambda { |option, existing| + (existing ||= []) << option + existing + } + option :formatter, :short => "-F FORMATTER", :long => "--format FORMATTER", @@ -196,9 +204,9 @@ class Chef::Application::Client < Chef::Application :description => "Replace current run list with specified items for a single run", :proc => lambda {|items| items = items.split(",") - items.compact.map {|item| + items.compact.map do |item| Chef::RunList::RunListItem.new(item) - } + end } option :runlist, @@ -207,9 +215,9 @@ class Chef::Application::Client < Chef::Application :description => "Permanently replace current run list with specified items", :proc => lambda {|items| items = items.split(",") - items.compact.map {|item| + items.compact.map do |item| Chef::RunList::RunListItem.new(item) - } + end } option :why_run, :short => "-W", diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb index 34598574dd..c80d0245f1 100644 --- a/lib/chef/application/knife.rb +++ b/lib/chef/application/knife.rb @@ -33,6 +33,14 @@ class Chef::Application::Knife < Chef::Application :description => "The configuration file to use", :proc => lambda { |path| File.expand_path(path, Dir.pwd) } + option :config_option, + :long => "--config-option OPTION=VALUE", + :description => "Override a single configuration option", + :proc => lambda { |option, existing| + (existing ||= []) << option + existing + } + verbosity_level = 0 option :verbosity, :short => "-V", diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index a7c4038f4c..446a0f007d 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -41,6 +41,14 @@ class Chef::Application::Solo < Chef::Application :default => Chef::Config.platform_specific_path("/etc/chef/solo.rb"), :description => "The configuration file to use" + option :config_option, + :long => "--config-option OPTION=VALUE", + :description => "Override a single configuration option", + :proc => lambda { |option, existing| + (existing ||= []) << option + existing + } + option :formatter, :short => "-F FORMATTER", :long => "--format FORMATTER", @@ -160,9 +168,9 @@ class Chef::Application::Solo < Chef::Application :description => "Replace current run list with specified items", :proc => lambda {|items| items = items.split(",") - items.compact.map {|item| + items.compact.map do |item| Chef::RunList::RunListItem.new(item) - } + end } option :client_fork, diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb index a40cae93dd..8546a21bb4 100644 --- a/lib/chef/audit/audit_reporter.rb +++ b/lib/chef/audit/audit_reporter.rb @@ -140,7 +140,11 @@ class Chef # Save the audit report to local disk error_file = "failed-audit-data.json" Chef::FileCache.store(error_file, Chef::JSONCompat.to_json_pretty(run_data), 0640) - Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}") + if Chef::Config.chef_zero.enabled + Chef::Log.debug("Saving audit report to #{Chef::FileCache.load(error_file, false)}") + else + Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}") + end end else Chef::Log.error("Failed to post audit report to server (#{e})") diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb index aa5a6d5a69..6b3e830f8d 100644 --- a/lib/chef/chef_fs/chef_fs_data_store.rb +++ b/lib/chef/chef_fs/chef_fs_data_store.rb @@ -458,6 +458,7 @@ class Chef # We want to delete just the ones that == POLICY next unless policy.name.rpartition("-")[0] == path[1] policy.delete(false) + FileSystemCache.instance.delete!(policy.file_path) found_policy = true end raise ChefZero::DataStore::DataNotFoundError.new(path) if !found_policy diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb index c824bc90df..2d887f4780 100644 --- a/lib/chef/chef_fs/command_line.rb +++ b/lib/chef/chef_fs/command_line.rb @@ -242,48 +242,50 @@ class Chef return [ [ :error, old_entry, new_entry, nil, nil, e ] ] end - private + class << self + private - def self.sort_keys(json_object) - if json_object.is_a?(Array) - json_object.map { |o| sort_keys(o) } - elsif json_object.is_a?(Hash) - new_hash = {} - json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) } - new_hash - else - json_object + def sort_keys(json_object) + if json_object.is_a?(Array) + json_object.map { |o| sort_keys(o) } + elsif json_object.is_a?(Hash) + new_hash = {} + json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) } + new_hash + else + json_object + end end - end - def self.canonicalize_json(json_text) - parsed_json = Chef::JSONCompat.parse(json_text) - sorted_json = sort_keys(parsed_json) - Chef::JSONCompat.to_json_pretty(sorted_json) - end - - def self.diff_text(old_path, new_path, old_value, new_value) - # Copy to tempfiles before diffing - # TODO don't copy things that are already in files! Or find an in-memory diff algorithm - begin - new_tempfile = Tempfile.new("new") - new_tempfile.write(new_value) - new_tempfile.close + def canonicalize_json(json_text) + parsed_json = Chef::JSONCompat.parse(json_text) + sorted_json = sort_keys(parsed_json) + Chef::JSONCompat.to_json_pretty(sorted_json) + end + def diff_text(old_path, new_path, old_value, new_value) + # Copy to tempfiles before diffing + # TODO don't copy things that are already in files! Or find an in-memory diff algorithm begin - old_tempfile = Tempfile.new("old") - old_tempfile.write(old_value) - old_tempfile.close + new_tempfile = Tempfile.new("new") + new_tempfile.write(new_value) + new_tempfile.close - result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path) - result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}") - result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}") - result + begin + old_tempfile = Tempfile.new("old") + old_tempfile.write(old_value) + old_tempfile.close + + result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path) + result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}") + result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}") + result + ensure + old_tempfile.close! + end ensure - old_tempfile.close! + new_tempfile.close! end - ensure - new_tempfile.close! end end end diff --git a/lib/chef/chef_fs/data_handler/data_handler_base.rb b/lib/chef/chef_fs/data_handler/data_handler_base.rb index b34aff4b98..3668f77dd5 100644 --- a/lib/chef/chef_fs/data_handler/data_handler_base.rb +++ b/lib/chef/chef_fs/data_handler/data_handler_base.rb @@ -93,7 +93,7 @@ class Chef # name to recipe[name]. Then calls uniq on the result. # def normalize_run_list(run_list) - run_list.map {|item| + run_list.map do |item| case item.to_s when /^recipe\[.*\]$/ item # explicit recipe @@ -102,7 +102,7 @@ class Chef else "recipe[#{item}]" end - }.uniq + end.uniq end # diff --git a/lib/chef/chef_fs/file_pattern.rb b/lib/chef/chef_fs/file_pattern.rb index a308a0fe2c..9c12bd4b96 100644 --- a/lib/chef/chef_fs/file_pattern.rb +++ b/lib/chef/chef_fs/file_pattern.rb @@ -160,7 +160,7 @@ class Chef pattern end - private + private def regexp calculate diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb index 69dce54f00..1a8da2fd6b 100644 --- a/lib/chef/chef_fs/file_system.rb +++ b/lib/chef/chef_fs/file_system.rb @@ -68,7 +68,7 @@ class Chef list_from(exact_child, &block) end - # Otherwise, go through all children and find any matches + # Otherwise, go through all children and find any matches elsif entry.dir? results = Parallelizer.parallelize(entry.children) { |child| Chef::ChefFS::FileSystem.list(child, pattern) } results.flatten(1).each(&block) @@ -257,172 +257,174 @@ class Chef [ are_same, a_value, b_value ] end - private - - # Copy two entries (could be files or dirs) - def self.copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path) - # A NOTE about this algorithm: - # There are cases where this algorithm does too many network requests. - # knife upload with a specific filename will first check if the file - # exists (a "dir" in the parent) before deciding whether to POST or - # PUT it. If we just tried PUT (or POST) and then tried the other if - # the conflict failed, we wouldn't need to check existence. - # On the other hand, we may already have DONE the request, in which - # case we shouldn't waste time trying PUT if we know the file doesn't - # exist. - # Will need to decide how that works with checksums, though. - error = false - begin - dest_path = format_path.call(dest_entry) if ui - src_path = format_path.call(src_entry) if ui - if !src_entry.exists? - if options[:purge] - # If we would not have uploaded it, we will not purge it. - if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?) - if options[:dry_run] - ui.output "Would delete #{dest_path}" if ui - else - begin - dest_entry.delete(true) - ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui - rescue Chef::ChefFS::FileSystem::NotFoundError - ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui + class << self + private + + # Copy two entries (could be files or dirs) + def copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path) + # A NOTE about this algorithm: + # There are cases where this algorithm does too many network requests. + # knife upload with a specific filename will first check if the file + # exists (a "dir" in the parent) before deciding whether to POST or + # PUT it. If we just tried PUT (or POST) and then tried the other if + # the conflict failed, we wouldn't need to check existence. + # On the other hand, we may already have DONE the request, in which + # case we shouldn't waste time trying PUT if we know the file doesn't + # exist. + # Will need to decide how that works with checksums, though. + error = false + begin + dest_path = format_path.call(dest_entry) if ui + src_path = format_path.call(src_entry) if ui + if !src_entry.exists? + if options[:purge] + # If we would not have uploaded it, we will not purge it. + if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?) + if options[:dry_run] + ui.output "Would delete #{dest_path}" if ui + else + begin + dest_entry.delete(true) + ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui + rescue Chef::ChefFS::FileSystem::NotFoundError + ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui + end end - end - else - ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui - end - end - - elsif !dest_entry.exists? - if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?) - # If the entry can do a copy directly from filesystem, do that. - if new_dest_parent.respond_to?(:create_child_from) - if options[:dry_run] - ui.output "Would create #{dest_path}" if ui else - new_dest_parent.create_child_from(src_entry) - ui.output "Created #{dest_path}" if ui + ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui end - return end - if src_entry.dir? - if options[:dry_run] - ui.output "Would create #{dest_path}" if ui - new_dest_dir = new_dest_parent.child(src_entry.name) - else - new_dest_dir = new_dest_parent.create_child(src_entry.name, nil) - ui.output "Created #{dest_path}" if ui - end - # Directory creation is recursive. - if recurse_depth != 0 - parallel_do(src_entry.children) do |src_child| - new_dest_child = new_dest_dir.child(src_child.name) - child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) - error ||= child_error + elsif !dest_entry.exists? + if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?) + # If the entry can do a copy directly from filesystem, do that. + if new_dest_parent.respond_to?(:create_child_from) + if options[:dry_run] + ui.output "Would create #{dest_path}" if ui + else + new_dest_parent.create_child_from(src_entry) + ui.output "Created #{dest_path}" if ui end + return end - else - if options[:dry_run] - ui.output "Would create #{dest_path}" if ui - else - child = new_dest_parent.create_child(src_entry.name, src_entry.read) - ui.output "Created #{format_path.call(child)}" if ui - end - end - end - - else - # Both exist. - # If the entry can do a copy directly, do that. - if dest_entry.respond_to?(:copy_from) - if options[:force] || compare(src_entry, dest_entry)[0] == false - if options[:dry_run] - ui.output "Would update #{dest_path}" if ui + if src_entry.dir? + if options[:dry_run] + ui.output "Would create #{dest_path}" if ui + new_dest_dir = new_dest_parent.child(src_entry.name) + else + new_dest_dir = new_dest_parent.create_child(src_entry.name, nil) + ui.output "Created #{dest_path}" if ui + end + # Directory creation is recursive. + if recurse_depth != 0 + parallel_do(src_entry.children) do |src_child| + new_dest_child = new_dest_dir.child(src_child.name) + child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) + error ||= child_error + end + end else - dest_entry.copy_from(src_entry, options) - ui.output "Updated #{dest_path}" if ui + if options[:dry_run] + ui.output "Would create #{dest_path}" if ui + else + child = new_dest_parent.create_child(src_entry.name, src_entry.read) + ui.output "Created #{format_path.call(child)}" if ui + end end end - return - end - # If they are different types, log an error. - if src_entry.dir? - if dest_entry.dir? - # If both are directories, recurse into their children - if recurse_depth != 0 - parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child| - child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) - error ||= child_error + else + # Both exist. + + # If the entry can do a copy directly, do that. + if dest_entry.respond_to?(:copy_from) + if options[:force] || compare(src_entry, dest_entry)[0] == false + if options[:dry_run] + ui.output "Would update #{dest_path}" if ui + else + dest_entry.copy_from(src_entry, options) + ui.output "Updated #{dest_path}" if ui end end - else - # If they are different types. - ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui return end - else - if dest_entry.dir? - ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui - return - else - # Both are files! Copy them unless we're sure they are the same.' - if options[:diff] == false - should_copy = false - elsif options[:force] - should_copy = true - src_value = nil + # If they are different types, log an error. + if src_entry.dir? + if dest_entry.dir? + # If both are directories, recurse into their children + if recurse_depth != 0 + parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child| + child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) + error ||= child_error + end + end else - are_same, src_value, _dest_value = compare(src_entry, dest_entry) - should_copy = !are_same + # If they are different types. + ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui + return end - if should_copy - if options[:dry_run] - ui.output "Would update #{dest_path}" if ui + else + if dest_entry.dir? + ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui + return + else + + # Both are files! Copy them unless we're sure they are the same.' + if options[:diff] == false + should_copy = false + elsif options[:force] + should_copy = true + src_value = nil else - src_value = src_entry.read if src_value.nil? - dest_entry.write(src_value) - ui.output "Updated #{dest_path}" if ui + are_same, src_value, _dest_value = compare(src_entry, dest_entry) + should_copy = !are_same + end + if should_copy + if options[:dry_run] + ui.output "Would update #{dest_path}" if ui + else + src_value = src_entry.read if src_value.nil? + dest_entry.write(src_value) + ui.output "Updated #{dest_path}" if ui + end end end end end + rescue RubyFileError => e + ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui + rescue DefaultEnvironmentCannotBeModifiedError => e + ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui + rescue OperationFailedError => e + ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui + error = true + rescue OperationNotAllowedError => e + ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui + error = true end - rescue RubyFileError => e - ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui - rescue DefaultEnvironmentCannotBeModifiedError => e - ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui - rescue OperationFailedError => e - ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui - error = true - rescue OperationNotAllowedError => e - ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui - error = true + error end - error - end - def self.get_or_create_parent(entry, options, ui, format_path) - parent = entry.parent - if parent && !parent.exists? - parent_path = format_path.call(parent) if ui - parent_parent = get_or_create_parent(parent, options, ui, format_path) - if options[:dry_run] - ui.output "Would create #{parent_path}" if ui - else - parent = parent_parent.create_child(parent.name, nil) - ui.output "Created #{parent_path}" if ui + def get_or_create_parent(entry, options, ui, format_path) + parent = entry.parent + if parent && !parent.exists? + parent_path = format_path.call(parent) if ui + parent_parent = get_or_create_parent(parent, options, ui, format_path) + if options[:dry_run] + ui.output "Would create #{parent_path}" if ui + else + parent = parent_parent.create_child(parent.name, nil) + ui.output "Created #{parent_path}" if ui + end end + return parent end - return parent - end - def self.parallel_do(enum, options = {}, &block) - Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block) + def parallel_do(enum, options = {}, &block) + Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block) + end end end end diff --git a/lib/chef/chef_fs/file_system/repository/acls_dir.rb b/lib/chef/chef_fs/file_system/repository/acls_dir.rb index 619031aa70..110befdf22 100644 --- a/lib/chef/chef_fs/file_system/repository/acls_dir.rb +++ b/lib/chef/chef_fs/file_system/repository/acls_dir.rb @@ -28,14 +28,16 @@ class Chef module Repository class AclsDir < Repository::Directory + BARE_FILES = %w{ organization.json root } + def can_have_child?(name, is_dir) - is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : name == "organization.json" + is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : BARE_FILES.include?(name) end protected def make_child_entry(child_name) - if child_name == "organization.json" + if BARE_FILES.include? child_name Acl.new(child_name, self) else AclsSubDir.new(child_name, self) diff --git a/lib/chef/chef_fs/file_system/repository/base_file.rb b/lib/chef/chef_fs/file_system/repository/base_file.rb index a768bcf971..3e1edc8d62 100644 --- a/lib/chef/chef_fs/file_system/repository/base_file.rb +++ b/lib/chef/chef_fs/file_system/repository/base_file.rb @@ -16,6 +16,8 @@ # limitations under the License. # +require "chef/chef_fs/file_system_cache" + class Chef module ChefFS module FileSystem @@ -99,6 +101,7 @@ class Chef end def delete(_) + FileSystemCache.instance.delete!(file_path) File.delete(file_path) rescue Errno::ENOENT raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb index 9d1538e46e..4019c6985b 100644 --- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb +++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb @@ -120,6 +120,7 @@ class Chef end def delete(recurse) + FileSystemCache.instance.delete!(file_path) begin if dir? if !recurse diff --git a/lib/chef/chef_fs/file_system/repository/directory.rb b/lib/chef/chef_fs/file_system/repository/directory.rb index 43f5719822..328cf92b03 100644 --- a/lib/chef/chef_fs/file_system/repository/directory.rb +++ b/lib/chef/chef_fs/file_system/repository/directory.rb @@ -84,6 +84,7 @@ class Chef if child.exists? raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child) end + FileSystemCache.instance.delete!(child.file_path) if file_contents child.write(file_contents) else @@ -122,6 +123,7 @@ class Chef raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self) end begin + FileSystemCache.instance.delete!(file_path) Dir.mkdir(file_path) rescue Errno::EEXIST raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self) @@ -138,6 +140,7 @@ class Chef raise MustDeleteRecursivelyError.new(self, $!) end FileUtils.rm_r(file_path) + FileSystemCache.instance.delete!(file_path) else raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!) end @@ -149,6 +152,10 @@ class Chef protected + def write(data) + raise FileSystemError.new(self, nil, "attempted to write to a directory entry") + end + def make_child_entry(child_name) raise "Not Implemented" end diff --git a/lib/chef/chef_fs/file_system_cache.rb b/lib/chef/chef_fs/file_system_cache.rb index 38b4fda7ad..a9d8d8bfe4 100644 --- a/lib/chef/chef_fs/file_system_cache.rb +++ b/lib/chef/chef_fs/file_system_cache.rb @@ -49,6 +49,17 @@ class Chef val end + def delete!(path) + parent = _get_parent(path) + Chef::Log.debug("Deleting parent #{parent} and #{path} from FileSystemCache") + if @cache.key?(path) + @cache.delete(path) + end + if !parent.nil? && @cache.key?(parent) + @cache.delete(parent) + end + end + def fetch(path) if @cache.key?(path) @cache[path] @@ -57,6 +68,13 @@ class Chef end end + private + + def _get_parent(path) + parts = ChefFS::PathUtils.split(path) + return nil if parts.nil? || parts.length < 2 + ChefFS::PathUtils.join(*parts[0..-2]) + end end end end diff --git a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb index 9d02bbab78..ab578bdb7f 100644 --- a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb +++ b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb @@ -184,9 +184,7 @@ class Chef sleep(0.01) end - until @unconsumed_output.empty? - yield @unconsumed_output.pop - end + yield @unconsumed_output.pop until @unconsumed_output.empty? # If no one is working on our tasks and we're allowed to # work on them in the main thread, process an input to @@ -227,9 +225,7 @@ class Chef def stop @unconsumed_input.clear - while @in_process.size > 0 - sleep(0.05) - end + sleep(0.05) while @in_process.size > 0 @unconsumed_output.clear end diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb index d9b027f322..af8b2e043e 100644 --- a/lib/chef/cookbook/cookbook_version_loader.rb +++ b/lib/chef/cookbook/cookbook_version_loader.rb @@ -3,6 +3,7 @@ require "chef/cookbook_version" require "chef/cookbook/chefignore" require "chef/cookbook/metadata" require "chef/util/path_helper" +require "find" class Chef class Cookbook @@ -168,7 +169,7 @@ class Chef when /\.json$/ apply_json_metadata(metadata_file) else - raise RuntimeError, "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}" + raise "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}" end end @@ -223,27 +224,31 @@ class Chef # however if the file is named ".uploaded-cookbook-version.json" it is # assumed to be managed by chef-zero and not part of the cookbook. def load_all_files - Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(cookbook_path), "*"), File::FNM_DOTMATCH).each do |fs_entry| - if File.directory?(fs_entry) - dir_relpath = Chef::Util::PathHelper.relative_path_from(@cookbook_path, fs_entry) - - next if dir_relpath.to_s.start_with?(".") - - Dir.glob(File.join(fs_entry, "**/*"), File::FNM_DOTMATCH).each do |file| - next if File.directory?(file) - file = Pathname.new(file).cleanpath.to_s - name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file) - cookbook_settings[:all_files][name] = file - end - elsif File.file?(fs_entry) - file = Pathname.new(fs_entry).cleanpath.to_s - - next if File.basename(file) == UPLOADED_COOKBOOK_VERSION_FILE - - name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file) - cookbook_settings[:all_files][name] = file - else # pipes, devices, other weirdness - next + return unless File.exist?(cookbook_path) + + # If cookbook_path is a symlink, Find on Windows Ruby 2.3 will not traverse it. + # Dir.entries will do so on all platforms, so we iterate the top level using + # Dir.entries. Since we have different behavior at the top anyway (hidden + # directories at the top level are not included for backcompat), this + # actually keeps things a bit cleaner. + Dir.entries(cookbook_path).each do |top_filename| + # Skip top-level directories starting with "." + top_path = File.join(cookbook_path, top_filename) + next if File.directory?(top_path) && top_filename.start_with?(".") + + # Use Find.find because it: + # (a) returns any children, recursively + # (b) includes top_path as well + # (c) skips symlinks, which is backcompat (no judgement on whether it was *right*) + Find.find(top_path) do |path| + # Only add files, not directories + next unless File.file?(path) + # Don't add .uploaded-cookbook-version.json + next if File.basename(path) == UPLOADED_COOKBOOK_VERSION_FILE + + relative_path = Chef::Util::PathHelper.relative_path_from(cookbook_path, path) + path = Pathname.new(path).cleanpath.to_s + cookbook_settings[:all_files][relative_path] = path end end end diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb index 603f80748c..ab83da9e55 100644 --- a/lib/chef/cookbook/metadata.rb +++ b/lib/chef/cookbook/metadata.rb @@ -722,7 +722,7 @@ class Chef end end - private + private # Helper to match a gem style version (ohai_version/chef_version) against a set of # Gem::Dependency version constraints. If none are present, it always matches. if diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb index 1ee30bacc7..bb44bc3d5c 100644 --- a/lib/chef/cookbook/synchronizer.rb +++ b/lib/chef/cookbook/synchronizer.rb @@ -152,11 +152,11 @@ class Chef queue << lambda do |lock| full_file_path = sync_file(file) - lock.synchronize { + lock.synchronize do # Save the full_path of the downloaded file to be restored in the manifest later save_full_file_path(file, full_file_path) mark_file_synced(file) - } + end end end @@ -291,7 +291,7 @@ class Chef end def server_api - Chef::ServerAPI.new(Chef::Config[:chef_server_url]) + Thread.current[:server_api] ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], keepalives: true) end end diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb index 9fb8d0d4bc..c0e85ff984 100644 --- a/lib/chef/cookbook_site_streaming_uploader.rb +++ b/lib/chef/cookbook_site_streaming_uploader.rb @@ -31,7 +31,7 @@ class Chef # inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html class CookbookSiteStreamingUploader - DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION } + DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION } # rubocop:disable Style/ConstantName class << self @@ -149,11 +149,11 @@ class Chef alias :to_s :body # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[]) - def headers + def headers # rubocop:disable Lint/NestedMethodDefinition self end - def status + def status # rubocop:disable Lint/NestedMethodDefinition code.to_i end end diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb index 95c27799ec..bb75234563 100644 --- a/lib/chef/cookbook_uploader.rb +++ b/lib/chef/cookbook_uploader.rb @@ -55,7 +55,7 @@ class Chef checksum_files.merge!(cb.checksums) end - checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil ; memo } + checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil; memo } new_sandbox = rest.post("sandboxes", { :checksums => checksums }) Chef::Log.info("Uploading files") diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb index 1e903608b5..8de9cb26dd 100644 --- a/lib/chef/cookbook_version.rb +++ b/lib/chef/cookbook_version.rb @@ -316,13 +316,13 @@ class Chef error_message << error_locations.join("\n") existing_files = segment_filenames(segment) # Strip the root_dir prefix off all files for readability - pretty_existing_files = existing_files.map { |path| + pretty_existing_files = existing_files.map do |path| if root_dir path[root_dir.length + 1..-1] else path end - } + end # Show the files that the cookbook does have. If the user made a typo, # hopefully they'll see it here. unless pretty_existing_files.empty? @@ -599,12 +599,12 @@ class Chef end end - def <=>(o) - raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name + def <=>(other) + raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != other.name # FIXME: can we change the interface to the Metadata class such # that metadata.version returns a Chef::Version instance instead # of a string? - Chef::Version.new(self.version) <=> Chef::Version.new(o.version) + Chef::Version.new(self.version) <=> Chef::Version.new(other.version) end private @@ -623,7 +623,7 @@ class Chef # For each filename, produce a mapping of base filename (i.e. recipe name # or attribute file) to on disk location def filenames_by_name(filenames) - filenames.select { |filename| filename =~ /\.rb$/ }.inject({}) { |memo, filename| memo[File.basename(filename, ".rb")] = filename ; memo } + filenames.select { |filename| filename =~ /\.rb$/ }.inject({}) { |memo, filename| memo[File.basename(filename, ".rb")] = filename; memo } end def file_vendor diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/dsl/powershell.rb index 1a900af6f6..7dc7a9a0f6 100644 --- a/lib/chef/dsl/powershell.rb +++ b/lib/chef/dsl/powershell.rb @@ -21,7 +21,7 @@ require "chef/util/powershell/ps_credential" class Chef module DSL module Powershell - def ps_credential(username = "placeholder", password) + def ps_credential(username = "placeholder", password) # rubocop:disable Style/OptionalArguments Chef::Util::Powershell::PSCredential.new(username, password) end end diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb index 6c6b2fa3bb..04c960c7af 100644 --- a/lib/chef/event_dispatch/base.rb +++ b/lib/chef/event_dispatch/base.rb @@ -232,11 +232,11 @@ class Chef end # Called after the recipe has been loaded - def recipe_file_loaded(path) + def recipe_file_loaded(path, recipe) end # Called after a recipe file fails to load - def recipe_file_load_failed(path, exception) + def recipe_file_load_failed(path, exception, recipe) end # Called when a recipe cannot be resolved diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 43759568a7..a4d5ff60e2 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -184,7 +184,7 @@ class Chef # A different version of a cookbook was added to a # VersionedRecipeList than the one already there. - class CookbookVersionConflict < ArgumentError ; end + class CookbookVersionConflict < ArgumentError; end # does not follow X.Y.Z format. ArgumentError? class InvalidPlatformVersion < ArgumentError; end diff --git a/lib/chef/file_access_control/windows.rb b/lib/chef/file_access_control/windows.rb index 6b7184bbd3..6f1ac5f581 100644 --- a/lib/chef/file_access_control/windows.rb +++ b/lib/chef/file_access_control/windows.rb @@ -128,7 +128,7 @@ class Chef end def should_update_dacl? - return true unless ::File.exists?(file) + return true unless ::File.exists?(file) || ::File.symlink?(file) dacl = target_dacl existing_dacl = existing_descriptor.dacl inherits = target_inherits @@ -161,7 +161,7 @@ class Chef end def should_update_group? - return true unless ::File.exists?(file) + return true unless ::File.exists?(file) || ::File.symlink?(file) (group = target_group) && (group != existing_descriptor.group) end @@ -180,7 +180,7 @@ class Chef end def should_update_owner? - return true unless ::File.exists?(file) + return true unless ::File.exists?(file) || ::File.symlink?(file) (owner = target_owner) && (owner != existing_descriptor.owner) end diff --git a/lib/chef/file_cache.rb b/lib/chef/file_cache.rb index cefc9da1eb..8e9bb1e3e4 100644 --- a/lib/chef/file_cache.rb +++ b/lib/chef/file_cache.rb @@ -85,7 +85,7 @@ class Chef File.join(create_cache_path(File.join(file_path_array), true), file_name) ) else - raise RuntimeError, "Cannot move #{file} to #{path}!" + raise "Cannot move #{file} to #{path}!" end end diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb index 3641e619e9..536bf72e02 100644 --- a/lib/chef/formatters/base.rb +++ b/lib/chef/formatters/base.rb @@ -203,12 +203,12 @@ class Chef end # Delegates to #file_loaded - def recipe_file_loaded(path) + def recipe_file_loaded(path, recipe) file_loaded(path) end # Delegates to #file_load_failed - def recipe_file_load_failed(path, exception) + def recipe_file_load_failed(path, exception, recipe) file_load_failed(path, exception) end diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb index d43f1993b2..7dbbf1d948 100644 --- a/lib/chef/formatters/doc.rb +++ b/lib/chef/formatters/doc.rb @@ -32,9 +32,9 @@ class Chef def pretty_elapsed_time time = elapsed_time - if time < 60 then + if time < 60 message = Time.at(time).utc.strftime("%S seconds") - elsif time < 3600 then + elsif time < 3600 message = Time.at(time).utc.strftime("%M minutes %S seconds") else message = Time.at(time).utc.strftime("%H hours %M minutes %S seconds") diff --git a/lib/chef/http.rb b/lib/chef/http.rb index 3e69f58383..924081bc6b 100644 --- a/lib/chef/http.rb +++ b/lib/chef/http.rb @@ -77,6 +77,9 @@ class Chef attr_reader :middlewares + # [Boolean] if we're doing keepalives or not + attr_reader :keepalives + # Create a HTTP client object. The supplied +url+ is used as the base for # all subsequent requests. For example, when initialized with a base url # http://localhost:4000, a call to +get+ with 'nodes' will make an @@ -87,6 +90,7 @@ class Chef @sign_on_redirect = true @redirects_followed = 0 @redirect_limit = 10 + @keepalives = options[:keepalives] || false @options = options @middlewares = [] @@ -228,6 +232,33 @@ class Chef def http_client(base_url = nil) base_url ||= url + if keepalives && !base_url.nil? + # only reuse the http_client if we want keepalives and have a base_url + @http_client ||= {} + # the per-host per-port cache here gets peristent connections correct when + # redirecting to different servers + if base_url.is_a?(String) # sigh, this kind of abuse can't happen with strongly typed languages + @http_client[base_url] ||= build_http_client(base_url) + else + @http_client[base_url.host] ||= {} + @http_client[base_url.host][base_url.port] ||= build_http_client(base_url) + end + else + build_http_client(base_url) + end + end + + # DEPRECATED: This is only kept around to provide access to cache control data in + # lib/chef/provider/remote_file/http.rb + # FIXME: Find a better API. + def last_response + @last_response + end + + private + + # @api private + def build_http_client(base_url) if chef_zero_uri?(base_url) # PERFORMANCE CRITICAL: *MUST* lazy require here otherwise we load up webrick # via chef-zero and that hits DNS (at *require* time) which may timeout, @@ -239,12 +270,11 @@ class Chef SocketlessChefZeroClient.new(base_url) else - BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy) + BasicClient.new(base_url, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: keepalives) end end - protected - + # @api private def create_url(path) return path if path.is_a?(URI) if path =~ /^(http|https|chefzero):\/\//i @@ -259,6 +289,7 @@ class Chef end end + # @api private def apply_request_middleware(method, url, headers, data) middlewares.inject([method, url, headers, data]) do |req_data, middleware| Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_request") @@ -266,6 +297,7 @@ class Chef end end + # @api private def apply_response_middleware(response, rest_request, return_value) middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware| Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_response") @@ -273,6 +305,7 @@ class Chef end end + # @api private def apply_stream_complete_middleware(response, rest_request, return_value) middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware| Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_stream_complete") @@ -280,6 +313,7 @@ class Chef end end + # @api private def log_failed_request(response, return_value) return_value ||= {} error_message = "HTTP Request Returned #{response.code} #{response.message}: " @@ -287,12 +321,14 @@ class Chef Chef::Log.info(error_message) end + # @api private def success_response?(response) response.kind_of?(Net::HTTPSuccess) || response.kind_of?(Net::HTTPRedirection) end # Runs a synchronous HTTP request, with no middleware applied (use #request # to have the middleware applied). The entire response will be loaded into memory. + # @api private def send_http_request(method, url, headers, body, &response_handler) headers = build_headers(method, url, headers, body) @@ -328,6 +364,7 @@ class Chef # Wraps an HTTP request with retry logic. # === Arguments # url:: URL of the request, used for error messages + # @api private def retrying_http_errors(url) http_attempts = 0 begin @@ -377,18 +414,22 @@ class Chef end end + # @api private def http_retry_delay config[:http_retry_delay] end + # @api private def http_retry_count config[:http_retry_count] end + # @api private def config Chef::Config end + # @api private def follow_redirect raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit @redirects_followed += 1 @@ -399,13 +440,13 @@ class Chef @redirects_followed = 0 end - private - + # @api private def chef_zero_uri?(uri) uri = URI.parse(uri) unless uri.respond_to?(:scheme) uri.scheme == "chefzero" end + # @api private def redirected_to(response) return nil unless response.kind_of?(Net::HTTPRedirection) # Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this @@ -413,6 +454,7 @@ class Chef response["location"] end + # @api private def build_headers(method, url, headers = {}, json_body = false) headers = @default_headers.merge(headers) headers["Content-Length"] = json_body.bytesize.to_s if json_body @@ -420,6 +462,7 @@ class Chef headers end + # @api private def stream_to_tempfile(url, response, &progress_block) content_length = response["Content-Length"] tf = Tempfile.open("chef-rest") @@ -443,18 +486,5 @@ class Chef raise end - public - - ############################################################################ - # DEPRECATED - ############################################################################ - - # This is only kept around to provide access to cache control data in - # lib/chef/provider/remote_file/http.rb - # Find a better API. - def last_response - @last_response - end - end end diff --git a/lib/chef/http/auth_credentials.rb b/lib/chef/http/auth_credentials.rb index d5dbff3758..053b2c938e 100644 --- a/lib/chef/http/auth_credentials.rb +++ b/lib/chef/http/auth_credentials.rb @@ -49,7 +49,7 @@ class Chef sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params) signed = sign_obj.sign(key).merge({ :host => host }) - signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo } + signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1]; memo } end end diff --git a/lib/chef/http/basic_client.rb b/lib/chef/http/basic_client.rb index 3a87fe85e4..460744ea2a 100644 --- a/lib/chef/http/basic_client.rb +++ b/lib/chef/http/basic_client.rb @@ -34,6 +34,7 @@ class Chef attr_reader :url attr_reader :http_client attr_reader :ssl_policy + attr_reader :keepalives # Instantiate a BasicClient. # === Arguments: @@ -43,7 +44,11 @@ class Chef def initialize(url, opts = {}) @url = url @ssl_policy = opts[:ssl_policy] || DefaultSSLPolicy - @http_client = build_http_client + @keepalives = opts[:keepalives] || false + end + + def http_client + @http_client ||= build_http_client end def host @@ -114,7 +119,11 @@ class Chef http_client.read_timeout = config[:rest_timeout] http_client.open_timeout = config[:rest_timeout] - http_client + if keepalives + http_client.start + else + http_client + end end def config diff --git a/lib/chef/http/socketless_chef_zero_client.rb b/lib/chef/http/socketless_chef_zero_client.rb index 1acac5e758..d2f1f45b1d 100644 --- a/lib/chef/http/socketless_chef_zero_client.rb +++ b/lib/chef/http/socketless_chef_zero_client.rb @@ -197,9 +197,9 @@ class Chef private def headers_extracted_from_options - options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value| + options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map do |name, value| [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value] - } + end end end diff --git a/lib/chef/key.rb b/lib/chef/key.rb index 1c25c5daad..38822e8b26 100644 --- a/lib/chef/key.rb +++ b/lib/chef/key.rb @@ -201,72 +201,71 @@ class Chef chef_rest.delete("#{api_base}/#{@actor}/keys/#{@name}") end - # Class methods - def self.from_hash(key_hash) - if key_hash.has_key?("user") - key = Chef::Key.new(key_hash["user"], "user") - elsif key_hash.has_key?("client") - key = Chef::Key.new(key_hash["client"], "client") - else - raise Chef::Exceptions::MissingKeyAttribute, "The hash passed to from_hash does not contain the key 'user' or 'client'. Please pass a hash that defines one of those keys." + class << self + def from_hash(key_hash) + if key_hash.has_key?("user") + key = Chef::Key.new(key_hash["user"], "user") + elsif key_hash.has_key?("client") + key = Chef::Key.new(key_hash["client"], "client") + else + raise Chef::Exceptions::MissingKeyAttribute, "The hash passed to from_hash does not contain the key 'user' or 'client'. Please pass a hash that defines one of those keys." + end + key.name key_hash["name"] if key_hash.key?("name") + key.public_key key_hash["public_key"] if key_hash.key?("public_key") + key.private_key key_hash["private_key"] if key_hash.key?("private_key") + key.create_key key_hash["create_key"] if key_hash.key?("create_key") + key.expiration_date key_hash["expiration_date"] if key_hash.key?("expiration_date") + key end - key.name key_hash["name"] if key_hash.key?("name") - key.public_key key_hash["public_key"] if key_hash.key?("public_key") - key.private_key key_hash["private_key"] if key_hash.key?("private_key") - key.create_key key_hash["create_key"] if key_hash.key?("create_key") - key.expiration_date key_hash["expiration_date"] if key_hash.key?("expiration_date") - key - end - - def self.from_json(json) - Chef::Key.from_hash(Chef::JSONCompat.from_json(json)) - end - def self.json_create(json) - Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Key#from_json or one of the load_by methods.") - Chef::Key.from_json(json) - end + def from_json(json) + Chef::Key.from_hash(Chef::JSONCompat.from_json(json)) + end - def self.list_by_user(actor, inflate = false) - keys = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys") - self.list(keys, actor, :load_by_user, inflate) - end + def json_create(json) + Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Key#from_json or one of the load_by methods.") + Chef::Key.from_json(json) + end - def self.list_by_client(actor, inflate = false) - keys = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys") - self.list(keys, actor, :load_by_client, inflate) - end + def list_by_user(actor, inflate = false) + keys = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys") + self.list(keys, actor, :load_by_user, inflate) + end - def self.load_by_user(actor, key_name) - response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys/#{key_name}") - Chef::Key.from_hash(response.merge({ "user" => actor })) - end + def list_by_client(actor, inflate = false) + keys = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys") + self.list(keys, actor, :load_by_client, inflate) + end - def self.load_by_client(actor, key_name) - response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys/#{key_name}") - Chef::Key.from_hash(response.merge({ "client" => actor })) - end + def load_by_user(actor, key_name) + response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys/#{key_name}") + Chef::Key.from_hash(response.merge({ "user" => actor })) + end - def self.generate_fingerprint(public_key) - openssl_key_object = OpenSSL::PKey::RSA.new(public_key) - data_string = OpenSSL::ASN1::Sequence([ - OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n), - OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e), - ]) - OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(":") - end + def load_by_client(actor, key_name) + response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys/#{key_name}") + Chef::Key.from_hash(response.merge({ "client" => actor })) + end - private + def generate_fingerprint(public_key) + openssl_key_object = OpenSSL::PKey::RSA.new(public_key) + data_string = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n), + OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e), + ]) + OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(":") + end - def self.list(keys, actor, load_method_symbol, inflate) - if inflate - keys.inject({}) do |key_map, result| - name = result["name"] - key_map[name] = Chef::Key.send(load_method_symbol, actor, name) - key_map + def list(keys, actor, load_method_symbol, inflate) + if inflate + keys.inject({}) do |key_map, result| + name = result["name"] + key_map[name] = Chef::Key.send(load_method_symbol, actor, name) + key_map + end + else + keys end - else - keys end end end diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb index 356bf47fd4..c9ecfbf0cc 100644 --- a/lib/chef/knife.rb +++ b/lib/chef/knife.rb @@ -233,61 +233,66 @@ class Chef end end - private - OFFICIAL_PLUGINS = %w{ec2 rackspace windows openstack terremark bluebox} - def self.path_from_caller(caller_line) - caller_line.split(/:\d+/).first - end - - # :nodoc: - # Error out and print usage. probably because the arguments given by the - # user could not be resolved to a subcommand. - def self.subcommand_not_found!(args) - ui.fatal("Cannot find subcommand for: '#{args.join(' ')}'") + class << self + private - # Mention rehash when the subcommands cache(plugin_manifest.json) is used - if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader) || - subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::CustomManifestLoader) - ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.") + # @api private + def path_from_caller(caller_line) + caller_line.split(/:\d+/).first end - if category_commands = guess_category(args) - list_commands(category_commands) - elsif missing_plugin = ( OFFICIAL_PLUGINS.find { |plugin| plugin == args[0] } ) - ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10") - ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`") - ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK") - else - list_commands - end + # :nodoc: + # Error out and print usage. probably because the arguments given by the + # user could not be resolved to a subcommand. + # @api private + def subcommand_not_found!(args) + ui.fatal("Cannot find subcommand for: '#{args.join(' ')}'") + + # Mention rehash when the subcommands cache(plugin_manifest.json) is used + if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader) || + subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::CustomManifestLoader) + ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.") + end - exit 10 - end + if category_commands = guess_category(args) + list_commands(category_commands) + elsif missing_plugin = ( OFFICIAL_PLUGINS.find { |plugin| plugin == args[0] } ) + ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10") + ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`") + ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK") + else + list_commands + end + + exit 10 + end - def self.list_commands(preferred_category = nil) - category_desc = preferred_category ? preferred_category + " " : "" - msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n" - subcommand_loader.list_commands(preferred_category).sort.each do |category, commands| - next if category =~ /deprecated/i - msg "** #{category.upcase} COMMANDS **" - commands.sort.each do |command| - subcommand_loader.load_command(command) - msg subcommands[command].banner if subcommands[command] + # @api private + def list_commands(preferred_category = nil) + category_desc = preferred_category ? preferred_category + " " : "" + msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n" + subcommand_loader.list_commands(preferred_category).sort.each do |category, commands| + next if category =~ /deprecated/i + msg "** #{category.upcase} COMMANDS **" + commands.sort.each do |command| + subcommand_loader.load_command(command) + msg subcommands[command].banner if subcommands[command] + end + msg end - msg end - end - def self.reset_config_path! - @@chef_config_dir = nil + # @api private + def reset_config_path! + @@chef_config_dir = nil + end + end reset_config_path! - public - # Create a new instance of the current class configured for the given # arguments and options def initialize(argv = []) @@ -324,31 +329,35 @@ class Chef exit(1) end - # Returns a subset of the Chef::Config[:knife] Hash that is relevant to the - # currently executing knife command. This is used by #configure_chef to - # apply settings from knife.rb to the +config+ hash. + # keys from mixlib-cli options + def cli_keys + self.class.options.keys + end + + # extracts the settings from the Chef::Config[:knife] sub-hash that correspond + # to knife cli options -- in preparation for merging config values with cli values + # + # NOTE: due to weirdness in mixlib-config #has_key? is only true if the value has + # been set by the user -- the Chef::Config defaults return #has_key?() of false and + # this code DEPENDS on that functionality since applying the default values in + # Chef::Config[:knife] would break the defaults in the cli that we would otherwise + # overwrite. def config_file_settings - config_file_settings = {} - self.class.options.keys.each do |key| - config_file_settings[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key) + cli_keys.each_with_object({}) do |key, memo| + memo[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key) end - config_file_settings end - # Apply Config in this order: - # defaults from mixlib-cli - # settings from config file, via Chef::Config[:knife] - # config from command line + # config is merged in this order (inverse of precedence) + # default_config - mixlib-cli defaults (accessor from the mixin) + # config_file_settings - Chef::Config[:knife] sub-hash + # config - mixlib-cli settings (accessor from the mixin) def merge_configs - # Apply config file settings on top of mixlib-cli defaults - combined_config = default_config.merge(config_file_settings) - # Apply user-supplied options on top of the above combination - combined_config = combined_config.merge(config) - # replace the config hash from mixlib-cli with our own. - # Need to use the mutate-in-place #replace method instead of assigning to - # the instance variable because other code may have a reference to the - # original config hash object. - config.replace(combined_config) + # other code may have a handle to the config object, so use Hash#replace to deliberately + # update-in-place. + config.replace( + default_config.merge(config_file_settings).merge(config) + ) end # Catch-all method that does any massaging needed for various config diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb index 18914f6be2..ee4d9ce7af 100644 --- a/lib/chef/knife/bootstrap.rb +++ b/lib/chef/knife/bootstrap.rb @@ -101,6 +101,14 @@ class Chef :description => "The proxy server for the node being bootstrapped", :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p } + option :bootstrap_proxy_user, + :long => "--bootstrap-proxy-user PROXY_USER", + :description => "The proxy authentication username for the node being bootstrapped" + + option :bootstrap_proxy_pass, + :long => "--bootstrap-proxy-pass PROXY_PASS", + :description => "The proxy authentication password for the node being bootstrapped" + option :bootstrap_no_proxy, :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]", :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode", diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb index e1b2365361..08cdf6c7dd 100644 --- a/lib/chef/knife/client_delete.rb +++ b/lib/chef/knife/client_delete.rb @@ -43,7 +43,7 @@ class Chef exit 1 end - delete_object(Chef::ApiClientV1, @client_name, "client") { + delete_object(Chef::ApiClientV1, @client_name, "client") do object = Chef::ApiClientV1.load(@client_name) if object.validator unless config[:delete_validators] @@ -52,7 +52,7 @@ class Chef end end object.destroy - } + end end end diff --git a/lib/chef/knife/cookbook_bulk_delete.rb b/lib/chef/knife/cookbook_bulk_delete.rb index bd1c8a22cc..cdd1584e36 100644 --- a/lib/chef/knife/cookbook_bulk_delete.rb +++ b/lib/chef/knife/cookbook_bulk_delete.rb @@ -42,7 +42,7 @@ class Chef all_cookbooks = Chef::CookbookVersion.list cookbooks_names = all_cookbooks.keys.grep(regex) - cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name];hash } + cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name]; hash } ui.msg "All versions of the following cookbooks will be deleted:" ui.msg "" ui.msg ui.list(cookbooks_to_delete.keys.sort, :columns_down) diff --git a/lib/chef/knife/cookbook_show.rb b/lib/chef/knife/cookbook_show.rb index a20e62ffc2..d0c930de0a 100644 --- a/lib/chef/knife/cookbook_show.rb +++ b/lib/chef/knife/cookbook_show.rb @@ -63,7 +63,7 @@ class Chef node[:platform_version] = config[:platform_version] if config.has_key?(:platform_version) class << node - def attribute?(name) + def attribute?(name) # rubocop:disable Lint/NestedMethodDefinition has_key?(name) end end diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb index 6db1a83c73..b2670f196b 100644 --- a/lib/chef/knife/core/bootstrap_context.rb +++ b/lib/chef/knife/core/bootstrap_context.rb @@ -114,6 +114,16 @@ validation_client_name "#{@chef_config[:validation_client_name]}" client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n} end + if knife_config[:bootstrap_proxy_user] + client_rb << %Q{http_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n} + client_rb << %Q{https_proxy_user "#{knife_config[:bootstrap_proxy_user]}"\n} + end + + if knife_config[:bootstrap_proxy_pass] + client_rb << %Q{http_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n} + client_rb << %Q{https_proxy_pass "#{knife_config[:bootstrap_proxy_pass]}"\n} + end + if knife_config[:bootstrap_no_proxy] client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} end diff --git a/lib/chef/knife/core/gem_glob_loader.rb b/lib/chef/knife/core/gem_glob_loader.rb index f5eff26903..34c5c53d75 100644 --- a/lib/chef/knife/core/gem_glob_loader.rb +++ b/lib/chef/knife/core/gem_glob_loader.rb @@ -81,9 +81,9 @@ class Chef files = [] if check_load_path - files = $LOAD_PATH.map { |load_path| + files = $LOAD_PATH.map do |load_path| Dir["#{File.expand_path glob, Chef::Util::PathHelper.escape_glob_dir(load_path)}#{Gem.suffix_pattern}"] - }.flatten.select { |file| File.file? file.untaint } + end.flatten.select { |file| File.file? file.untaint } end gem_files = latest_gem_specs.map do |spec| @@ -110,7 +110,7 @@ class Chef end def check_spec_for_glob(spec, glob) - dirs = if spec.require_paths.size > 1 then + dirs = if spec.require_paths.size > 1 "{#{spec.require_paths.join(',')}}" else spec.require_paths.first diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb index 38d1ab3f42..d102c1e955 100644 --- a/lib/chef/knife/search.rb +++ b/lib/chef/knife/search.rb @@ -18,6 +18,7 @@ require "chef/knife" require "chef/knife/core/node_presenter" +require "addressable/uri" class Chef class Knife @@ -85,8 +86,7 @@ class Chef end q = Chef::Search::Query.new - escaped_query = URI.escape(@query, - Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")) + escaped_query = Addressable::URI.encode_component(@query, Addressable::URI::CharacterClasses::QUERY) result_items = [] result_count = 0 diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb index 2bbcbfc79e..6f266b2719 100644 --- a/lib/chef/knife/ssh.rb +++ b/lib/chef/knife/ssh.rb @@ -164,9 +164,7 @@ class Chef end def configure_session - list = config[:manual] ? - @name_args[0].split(" ") : - search_nodes + list = config[:manual] ? @name_args[0].split(" ") : search_nodes if list.length == 0 if @action_nodes.length == 0 ui.fatal("No nodes returned from search") @@ -548,24 +546,24 @@ class Chef configure_session exit_status = - case @name_args[1] - when "interactive" - interactive - when "screen" - screen - when "tmux" - tmux - when "macterm" - macterm - when "cssh" - cssh - when "csshx" - Chef::Log.warn("knife ssh csshx will be deprecated in a future release") - Chef::Log.warn("please use knife ssh cssh instead") - cssh - else - ssh_command(@name_args[1..-1].join(" ")) - end + case @name_args[1] + when "interactive" + interactive + when "screen" + screen + when "tmux" + tmux + when "macterm" + macterm + when "cssh" + cssh + when "csshx" + Chef::Log.warn("knife ssh csshx will be deprecated in a future release") + Chef::Log.warn("please use knife ssh cssh instead") + cssh + else + ssh_command(@name_args[1..-1].join(" ")) + end session.close if exit_status != 0 diff --git a/lib/chef/knife/status.rb b/lib/chef/knife/status.rb index 551d5addfc..0e3cd7e7d6 100644 --- a/lib/chef/knife/status.rb +++ b/lib/chef/knife/status.rb @@ -96,13 +96,13 @@ class Chef all_nodes << node end - output(all_nodes.sort { |n1, n2| + output(all_nodes.sort do |n1, n2| if config[:sort_reverse] || Chef::Config[:knife][:sort_status_reverse] (n2["ohai_time"] || 0) <=> (n1["ohai_time"] || 0) else (n1["ohai_time"] || 0) <=> (n2["ohai_time"] || 0) end - }) + end) end end diff --git a/lib/chef/mixin/command/unix.rb b/lib/chef/mixin/command/unix.rb index bfd507979f..aa541c3637 100644 --- a/lib/chef/mixin/command/unix.rb +++ b/lib/chef/mixin/command/unix.rb @@ -64,7 +64,7 @@ class Chef $VERBOSE = nil ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) - cid = fork { + cid = fork do pw.last.close STDIN.reopen pw.first pw.first.close @@ -111,7 +111,7 @@ class Chef end ps.last.close unless ps.last.closed? exit! - } + end ensure $VERBOSE = verbose end diff --git a/lib/chef/mixin/powershell_type_coercions.rb b/lib/chef/mixin/powershell_type_coercions.rb index 381cbed637..6159c87948 100644 --- a/lib/chef/mixin/powershell_type_coercions.rb +++ b/lib/chef/mixin/powershell_type_coercions.rb @@ -63,7 +63,7 @@ class Chef end def unsafe?(s) - ["'", '#', "`", '"'].any? do |x| + ["'", "#", "`", '"'].any? do |x| s.include? x end end diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb index ae2406f1ae..8ff2cc4501 100644 --- a/lib/chef/mixin/properties.rb +++ b/lib/chef/mixin/properties.rb @@ -79,6 +79,9 @@ class Chef # part of desired state. Defaults to `true`. # @option options [Boolean] :identity `true` if this property # is part of object identity. Defaults to `false`. + # @option options [Boolean] :sensitive `true` if this property could + # contain sensitive information and whose value should be redacted + # in any resource reporting / auditing output. Defaults to `false`. # # @example Bare property # property :x diff --git a/lib/chef/mixin/securable.rb b/lib/chef/mixin/securable.rb index a88d534b37..55b4e0a6d1 100644 --- a/lib/chef/mixin/securable.rb +++ b/lib/chef/mixin/securable.rb @@ -43,7 +43,7 @@ class Chef :mode, arg, :callbacks => { - "not in valid numeric range" => lambda { |m| + "not in valid numeric range" => lambda do |m| if m.kind_of?(String) m =~ /^0/ || m = "0#{m}" end @@ -54,7 +54,7 @@ class Chef else Integer(m) <= 07777 && Integer(m) >= 0 end - }, + end, } ) end diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb index 1f7deb21d2..a258a91075 100644 --- a/lib/chef/mixin/shell_out.rb +++ b/lib/chef/mixin/shell_out.rb @@ -78,6 +78,36 @@ class Chef return my_command_args end + # Helper for sublcasses to convert an array of string args into a string. It + # will compact nil or empty strings in the array and will join the array elements + # with spaces, without introducing any double spaces for nil/empty elements. + # + # @param args [String] variable number of string arguments + # @return [String] nicely concatenated string or empty string + def a_to_s(*args) + clean_array(*args).join(" ") + end + + # Helper for sublcasses to reject nil and empty strings out of an array. It allows + # using the array form of shell_out (which avoids the need to surround arguments with + # quote marks to deal with shells). + # + # Usage: + # shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username)) + # + # universal_options and useradd_options can be nil, empty array, empty string, strings or arrays + # and the result makes sense. + # + # keeping this separate from shell_out!() makes it a bit easier to write expectations against the + # shell_out args and be able to omit nils and such in the tests (and to test that the nils are + # being rejected correctly). + # + # @param args [String] variable number of string arguments + # @return [Array] array of strings with nil and null string rejection + def clean_array(*args) + args.flatten.reject { |i| i.nil? || i == "" }.map(&:to_s) + end + private def shell_out_command(*command_args) diff --git a/lib/chef/mixin/uris.rb b/lib/chef/mixin/uris.rb index 24e8a4f9ed..7dc04d662b 100644 --- a/lib/chef/mixin/uris.rb +++ b/lib/chef/mixin/uris.rb @@ -17,6 +17,7 @@ # require "uri" +require "addressable/uri" class Chef module Mixin @@ -34,7 +35,7 @@ class Chef URI.parse(source) rescue URI::InvalidURIError Chef::Log.warn("#{source} was an invalid URI. Trying to escape invalid characters") - URI.parse(URI.escape(source)) + URI.parse(Addressable::URI.encode(source)) end end diff --git a/lib/chef/mixin/windows_architecture_helper.rb b/lib/chef/mixin/windows_architecture_helper.rb index 11e1b5559d..49252af484 100644 --- a/lib/chef/mixin/windows_architecture_helper.rb +++ b/lib/chef/mixin/windows_architecture_helper.rb @@ -74,16 +74,15 @@ class Chef def node_supports_windows_architecture?(node, desired_architecture) assert_valid_windows_architecture!(desired_architecture) - return (node_windows_architecture(node) == :x86_64 || - desired_architecture == :i386) ? true : false + return ( node_windows_architecture(node) == :x86_64 ) || ( desired_architecture == :i386 ) end def valid_windows_architecture?(architecture) - return (architecture == :x86_64) || (architecture == :i386) + return ( architecture == :x86_64 ) || ( architecture == :i386 ) end def assert_valid_windows_architecture!(architecture) - if ! valid_windows_architecture?(architecture) + if !valid_windows_architecture?(architecture) raise Chef::Exceptions::Win32ArchitectureIncorrect, "The specified architecture was not valid. It must be one of :i386 or :x86_64" end diff --git a/lib/chef/monkey_patches/webrick-utils.rb b/lib/chef/monkey_patches/webrick-utils.rb index 7880adb202..ccca5825e2 100644 --- a/lib/chef/monkey_patches/webrick-utils.rb +++ b/lib/chef/monkey_patches/webrick-utils.rb @@ -31,7 +31,7 @@ module WEBrick Socket::AI_PASSIVE) # flag last_error = nil sockets = [] - res.each {|ai| + res.each do |ai| begin logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger sock = TCPServer.new(ai[3], port) @@ -42,7 +42,7 @@ module WEBrick logger.warn("TCPServer Error: #{ex}") if logger last_error = ex end - } + end raise last_error if sockets.empty? return sockets end diff --git a/lib/chef/monologger.rb b/lib/chef/monologger.rb index 8bcdae1293..82816e887f 100644 --- a/lib/chef/monologger.rb +++ b/lib/chef/monologger.rb @@ -61,7 +61,7 @@ class MonoLogger < Logger @dev.close rescue nil end - private + private def open_logfile(filename) if FileTest.exist?(filename) diff --git a/lib/chef/node.rb b/lib/chef/node.rb index 54faab6d3e..212b1ced14 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -642,8 +642,8 @@ class Chef end end - def <=>(other_node) - self.name <=> other_node.name + def <=>(other) + self.name <=> other.name end private diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index f5fe89251d..95b3b09f7e 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -537,12 +537,12 @@ class Chef end def inspect - "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map {|iv| + "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map do |iv| "#{iv}=#{instance_variable_get(iv).inspect}" - }.join(", ") << ">" + end.join(", ") << ">" end - private + private # Helper method for merge_all/merge_defaults/merge_overrides. # diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb index 40474242f0..bc565d92ef 100644 --- a/lib/chef/platform/provider_mapping.rb +++ b/lib/chef/platform/provider_mapping.rb @@ -197,8 +197,11 @@ class Chef def resource_matching_provider(platform, version, resource_type) if resource_type.kind_of?(Chef::Resource) - class_name = resource_type.class.name ? resource_type.class.name.split("::").last : - convert_to_class_name(resource_type.resource_name.to_s) + class_name = if resource_type.class.name + resource_type.class.name.split("::").last + else + convert_to_class_name(resource_type.resource_name.to_s) + end if Chef::Provider.const_defined?(class_name, false) Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'provides #{convert_to_snake_case(class_name).to_sym.inspect}'.") diff --git a/lib/chef/property.rb b/lib/chef/property.rb index 0589cb4c54..a357ba9ee3 100644 --- a/lib/chef/property.rb +++ b/lib/chef/property.rb @@ -230,14 +230,25 @@ class Chef end # + # Whether this property is sensitive or not. + # + # Defaults to false. + # + # @return [Boolean] + # + def sensitive? + options.fetch(:sensitive, false) + end + + # # Validation options. (See Chef::Mixin::ParamsValidate#validate.) # # @return [Hash<Symbol,Object>] # def validation_options - @validation_options ||= options.reject { |k, v| - [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable].include?(k) - } + @validation_options ||= options.reject do |k, v| + [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable, :sensitive].include?(k) + end end # diff --git a/lib/chef/provider/apt_repository.rb b/lib/chef/provider/apt_repository.rb index 1e7db80620..9e077c8cbb 100644 --- a/lib/chef/provider/apt_repository.rb +++ b/lib/chef/provider/apt_repository.rb @@ -19,6 +19,7 @@ require "chef/resource" require "chef/dsl/declare_resource" require "chef/mixin/shell_out" +require "chef/mixin/which" require "chef/http/simple" require "chef/provider/noop" @@ -28,9 +29,10 @@ class Chef use_inline_resources include Chef::Mixin::ShellOut + extend Chef::Mixin::Which provides :apt_repository do - uses_apt? + which("apt-get") end def whyrun_supported? @@ -104,12 +106,6 @@ class Chef end end - def self.uses_apt? - ENV["PATH"] ||= "" - paths = %w{ /bin /usr/bin /sbin /usr/sbin } + ENV["PATH"].split(::File::PATH_SEPARATOR) - paths.any? { |path| ::File.executable?(::File.join(path, "apt-get")) } - end - def is_key_id?(id) id = id[2..-1] if id.start_with?("0x") id =~ /^\h+$/ && [8, 16, 40].include?(id.length) @@ -254,4 +250,4 @@ class Chef end end -Chef::Provider::Noop.provides :apt_resource +Chef::Provider::Noop.provides :apt_repository diff --git a/lib/chef/provider/apt_update.rb b/lib/chef/provider/apt_update.rb index d2dd5cfb14..0320e9a83f 100644 --- a/lib/chef/provider/apt_update.rb +++ b/lib/chef/provider/apt_update.rb @@ -16,15 +16,20 @@ # limitations under the License. # -require "chef/resource" -require "chef/dsl/declare_resource" +require "chef/provider" +require "chef/provider/noop" +require "chef/mixin/which" class Chef class Provider class AptUpdate < Chef::Provider use_inline_resources - provides :apt_update, os: "linux" + extend Chef::Mixin::Which + + provides :apt_update do + which("apt-get") + end APT_CONF_DIR = "/etc/apt/apt.conf.d" STAMP_DIR = "/var/lib/apt/periodic" @@ -78,3 +83,5 @@ class Chef end end end + +Chef::Provider::Noop.provides :apt_update diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb index 36b67ab6a5..7baaeec0c5 100644 --- a/lib/chef/provider/cron.rb +++ b/lib/chef/provider/cron.rb @@ -187,8 +187,7 @@ class Chef end crontab << line end - description = cron_found ? "remove #{@new_resource.name} from crontab" : - "save unmodified crontab" + description = cron_found ? "remove #{@new_resource.name} from crontab" : "save unmodified crontab" converge_by(description) do write_crontab crontab Chef::Log.info("#{@new_resource} deleted crontab entry") @@ -237,7 +236,7 @@ class Chef newcron = "" newcron << "# Chef Name: #{new_resource.name}\n" [ :mailto, :path, :shell, :home ].each do |v| - newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v) + newcron << "#{v.to_s.upcase}=\"#{@new_resource.send(v)}\"\n" if @new_resource.send(v) end @new_resource.environment.each do |name, value| newcron << "#{name}=#{value}\n" diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb index 79769d9773..66783ceb0f 100644 --- a/lib/chef/provider/dsc_script.rb +++ b/lib/chef/provider/dsc_script.rb @@ -32,12 +32,12 @@ class Chef @dsc_resource = dsc_resource @resource_converged = false @operations = { - :set => Proc.new { |config_manager, document, shellout_flags| + :set => Proc.new do |config_manager, document, shellout_flags| config_manager.set_configuration(document, shellout_flags) - }, - :test => Proc.new { |config_manager, document, shellout_flags| + end, + :test => Proc.new do |config_manager, document, shellout_flags| config_manager.test_configuration(document, shellout_flags) - } } + end } end def action_run diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb index bb0762ceb7..7f85085eeb 100644 --- a/lib/chef/provider/file.rb +++ b/lib/chef/provider/file.rb @@ -154,7 +154,6 @@ class Chef do_contents_changes do_acl_changes do_selinux - do_resolv_conf_fixup load_resource_attributes_from_file(@new_resource) end @@ -446,13 +445,6 @@ class Chef end end - def do_resolv_conf_fixup - # reload /etc/resolv.conf after we edit it -- only on linux -- and see lib/chef/application.rb - if new_resource.path == "/etc/resolv.conf" && RbConfig::CONFIG["host_os"] =~ /linux/ - Resolv::DefaultResolver.replace_resolvers [Resolv::DNS.new("/etc/resolv.conf")] - end - end - def do_acl_changes if access_controls.requires_changes? converge_by(access_controls.describe_changes) do diff --git a/lib/chef/provider/group/usermod.rb b/lib/chef/provider/group/usermod.rb index e4b19181aa..bef4b667a2 100644 --- a/lib/chef/provider/group/usermod.rb +++ b/lib/chef/provider/group/usermod.rb @@ -57,7 +57,7 @@ class Chef # This provider only supports adding members with # append. Only if the action is create we will go # ahead and add members. - if @new_resource.action == :create + if @new_resource.action.include?(:create) members.each do |member| add_member(member) end diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb index 5fce97e5b3..16d30319b3 100644 --- a/lib/chef/provider/link.rb +++ b/lib/chef/provider/link.rb @@ -141,9 +141,16 @@ class Chef def action_delete if @current_resource.to # Exists - converge_by("delete link at #{@new_resource.target_file}") do - ::File.delete(@new_resource.target_file) - Chef::Log.info("#{@new_resource} deleted") + if Chef::Platform.windows? && ::File.directory?(@current_resource.target_file) + converge_by("delete link to dir at #{@new_resource.target_file}") do + ::Dir.delete(@new_resource.target_file) + Chef::Log.info("#{@new_resource} deleted") + end + else + converge_by("delete link to file at #{@new_resource.target_file}") do + ::File.delete(@new_resource.target_file) + Chef::Log.info("#{@new_resource} deleted") + end end end end diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb index f2e71a6621..69ecf2ddb9 100644 --- a/lib/chef/provider/osx_profile.rb +++ b/lib/chef/provider/osx_profile.rb @@ -66,33 +66,33 @@ class Chef def define_resource_requirements requirements.assert(:remove) do |a| if @new_profile_identifier - a.assertion { + a.assertion do !@new_profile_identifier.nil? && !@new_profile_identifier.end_with?(".mobileconfig") && - /^\w+(?:\.\w+)+$/.match(@new_profile_identifier) - } + /^\w+(?:(\.| )\w+)+$/.match(@new_profile_identifier) + end a.failure_message RuntimeError, "when removing using the identifier attribute, it must match the profile identifier" else new_profile_name = @new_resource.profile_name - a.assertion { + a.assertion do !new_profile_name.end_with?(".mobileconfig") && - /^\w+(?:\.\w+)+$/.match(new_profile_name) - } + /^\w+(?:(\.| )\w+)+$/.match(new_profile_name) + end a.failure_message RuntimeError, "When removing by resource name, it must match the profile identifier " end end requirements.assert(:install) do |a| if @new_profile_hash.is_a?(Hash) - a.assertion { + a.assertion do @new_profile_hash.include?("PayloadIdentifier") - } + end a.failure_message RuntimeError, "The specified profile does not seem to be valid" end if @new_profile_hash.is_a?(String) - a.assertion { + a.assertion do @new_profile_hash.end_with?(".mobileconfig") - } + end a.failure_message RuntimeError, "#{new_profile_hash}' is not a valid profile" end end diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb index cac0fb6eb0..3f641145e6 100644 --- a/lib/chef/provider/package.rb +++ b/lib/chef/provider/package.rb @@ -194,12 +194,12 @@ class Chef end def action_reconfig - if @current_resource.version == nil then + if @current_resource.version == nil Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do") return end - unless @new_resource.response_file then + unless @new_resource.response_file Chef::Log.debug("#{@new_resource} no response_file provided - nothing to do") return end @@ -588,16 +588,6 @@ class Chef end args end - - # Helper for sublcasses to convert an array of string args into a string. It - # will compact nil or empty strings in the array and will join the array elements - # with spaces, without introducing any double spaces for nil/empty elements. - # - # @param args [String] variable number of string arguments - # @return [String] nicely concatenated string or empty string - def a_to_s(*args) - args.flatten.reject { |i| i.nil? || i == "" }.join(" ") - end end end end diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb index 0aeec951b1..187197d143 100644 --- a/lib/chef/provider/package/rubygems.rb +++ b/lib/chef/provider/package/rubygems.rb @@ -159,19 +159,22 @@ class Chef # Find the newest gem version available from Gem.sources that satisfies # the constraints of +gem_dependency+ def find_newest_remote_version(gem_dependency, *sources) - available_gems = dependency_installer.find_gems_with_sources(gem_dependency) - spec, source = if available_gems.respond_to?(:last) - # DependencyInstaller sorts the results such that the last one is - # always the one it considers best. - spec_with_source = available_gems.last - spec_with_source && spec_with_source - else - # Rubygems 2.0 returns a Gem::Available set, which is a - # collection of AvailableSet::Tuple structs - available_gems.pick_best! - best_gem = available_gems.set.first - best_gem && [best_gem.spec, best_gem.source] - end + spec, source = + if Chef::Config[:rubygems_cache_enabled] + # This code caches every gem on rubygems.org and uses lots of RAM + available_gems = dependency_installer.find_gems_with_sources(gem_dependency) + available_gems.pick_best! + best_gem = available_gems.set.first + best_gem && [best_gem.spec, best_gem.source] + else + # Use the API that 'gem install' calls which does not pull down the rubygems universe + begin + rs = dependency_installer.resolve_dependencies gem_dependency.name, gem_dependency.requirement + rs.specs.select { |s| s.name == gem_dependency.name }.first + rescue Gem::UnsatisfiableDependencyError + nil + end + end version = spec && spec.version if version @@ -519,9 +522,7 @@ class Chef install_via_gem_command(name, version) end elsif @new_resource.gem_binary.nil? - # domain is used by Gem::DependencyInstaller rather than by Chef code - # domain can be :local, :remote or :both - @gem_env.install(@new_resource.source, domain: :local) + @gem_env.install(@new_resource.source) else install_via_gem_command(name, version) end diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb index a5f3ff7191..753d3c279e 100644 --- a/lib/chef/provider/package/windows.rb +++ b/lib/chef/provider/package/windows.rb @@ -201,6 +201,7 @@ class Chef def resource_for_provider @resource_for_provider = Chef::Resource::WindowsPackage.new(new_resource.name).tap do |r| r.source(Chef::Util::PathHelper.validate_path(source_location)) unless source_location.nil? + r.cookbook_name = new_resource.cookbook_name r.version(new_resource.version) r.timeout(new_resource.timeout) r.returns(new_resource.returns) @@ -216,6 +217,7 @@ class Chef def source_resource @source_resource ||= Chef::Resource::RemoteFile.new(default_download_cache_path, run_context).tap do |r| r.source(new_resource.source) + r.cookbook_name = new_resource.cookbook_name r.checksum(new_resource.checksum) r.backup(false) diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb index 211845c073..44a2f19d1e 100644 --- a/lib/chef/provider/package/windows/exe.rb +++ b/lib/chef/provider/package/windows/exe.rb @@ -71,7 +71,7 @@ class Chef uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) } .map { |version| version.uninstall_string }.uniq.each do |uninstall_string| Chef::Log.debug("Registry provided uninstall string for #{new_resource} is '#{uninstall_string}'") - shell_out!(uninstall_command(uninstall_string), { returns: new_resource.returns }) + shell_out!(uninstall_command(uninstall_string), { :timeout => new_resource.timeout, :returns => new_resource.returns }) end end diff --git a/lib/chef/provider/package/windows/registry_uninstall_entry.rb b/lib/chef/provider/package/windows/registry_uninstall_entry.rb index 3fa00b6081..a693558883 100644 --- a/lib/chef/provider/package/windows/registry_uninstall_entry.rb +++ b/lib/chef/provider/package/windows/registry_uninstall_entry.rb @@ -79,8 +79,6 @@ class Chef attr_reader :uninstall_string attr_reader :data - private - UNINSTALL_SUBKEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'.freeze end end diff --git a/lib/chef/provider/package/yum/rpm_utils.rb b/lib/chef/provider/package/yum/rpm_utils.rb index a748c664a9..032597d047 100644 --- a/lib/chef/provider/package/yum/rpm_utils.rb +++ b/lib/chef/provider/package/yum/rpm_utils.rb @@ -243,16 +243,16 @@ class Chef self.new(*args) end - def <=>(y) - compare_versions(y) + def <=>(other) + compare_versions(other) end - def compare(y) - compare_versions(y, false) + def compare(other) + compare_versions(other, false) end - def partial_compare(y) - compare_versions(y, true) + def partial_compare(other) + compare_versions(other, true) end # RPM::Version rpm_version_to_s equivalent @@ -352,8 +352,8 @@ class Chef alias :name :n alias :arch :a - def <=>(y) - compare(y) + def <=>(other) + compare(other) end def compare(y) diff --git a/lib/chef/provider/package/yum/yum_cache.rb b/lib/chef/provider/package/yum/yum_cache.rb index fb25a91c8c..7462529113 100644 --- a/lib/chef/provider/package/yum/yum_cache.rb +++ b/lib/chef/provider/package/yum/yum_cache.rb @@ -197,7 +197,7 @@ class Chef def shabang?(file) ::File.open(file, "r") do |f| - f.read(2) == '#!' + f.read(2) == "#!" end rescue Errno::ENOENT false diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb index 5935e83301..b382c20c31 100644 --- a/lib/chef/provider/remote_file/ftp.rb +++ b/lib/chef/provider/remote_file/ftp.rb @@ -153,9 +153,9 @@ class Chef def parse_path path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it. directories = path.split(%r{/}, -1) - directories.each {|d| + directories.each do |d| d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } - } + end unless filename = directories.pop raise ArgumentError, "no filename: #{path.inspect}" end diff --git a/lib/chef/provider/remote_file/sftp.rb b/lib/chef/provider/remote_file/sftp.rb index 530977e3c8..21c5c4ca04 100644 --- a/lib/chef/provider/remote_file/sftp.rb +++ b/lib/chef/provider/remote_file/sftp.rb @@ -68,9 +68,9 @@ class Chef def validate_path! path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it. directories = path.split(%r{/}, -1) - directories.each {|d| + directories.each do |d| d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } - } + end unless filename = directories.pop raise ArgumentError, "no filename: #{path.inspect}" end diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb index 67b71953f7..9d11032055 100644 --- a/lib/chef/provider/service/debian.rb +++ b/lib/chef/provider/service/debian.rb @@ -106,13 +106,13 @@ class Chef def service_currently_enabled?(priority) enabled = false - priority.each { |runlevel, arguments| + priority.each do |runlevel, arguments| Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}") # if we are in a update-rc.d default startup runlevel && we start in this runlevel if %w{ 1 2 3 4 5 S }.include?(runlevel) && arguments[0] == :start enabled = true end - } + end enabled end diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb index fe4768b2e8..d75e85989f 100644 --- a/lib/chef/provider/service/simple.rb +++ b/lib/chef/provider/service/simple.rb @@ -76,8 +76,9 @@ class Chef end requirements.assert(:all_actions) do |a| - a.assertion { @new_resource.status_command || supports[:status] || - (!ps_cmd.nil? && !ps_cmd.empty?) } + a.assertion do + @new_resource.status_command || supports[:status] || + (!ps_cmd.nil? && !ps_cmd.empty?) end a.failure_message Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this node's 'command.ps' attribute" end requirements.assert(:all_actions) do |a| @@ -108,7 +109,7 @@ class Chef shell_out_with_systems_locale!(@new_resource.reload_command) end - protected + protected def determine_current_status! if @new_resource.status_command diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb index 1e5398eba8..868b3e1ac1 100644 --- a/lib/chef/provider/service/solaris.rb +++ b/lib/chef/provider/service/solaris.rb @@ -40,7 +40,7 @@ class Chef @current_resource.service_name(@new_resource.service_name) [@init_command, @status_command].each do |cmd| - unless ::File.executable? cmd then + unless ::File.executable? cmd raise Chef::Exceptions::Service, "#{cmd} not executable!" end end diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb index 1597d46a3d..712f0f60c3 100644 --- a/lib/chef/provider/service/systemd.rb +++ b/lib/chef/provider/service/systemd.rb @@ -78,10 +78,10 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple if new_resource.user uid = node["etc"]["passwd"][new_resource.user]["uid"] options = { - "environment" => { + :environment => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{uid}/bus", }, - "user" => new_resource.user, + :user => new_resource.user, } args = "--user" else diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb index 4acad768a4..941c4209dd 100644 --- a/lib/chef/provider/service/upstart.rb +++ b/lib/chef/provider/service/upstart.rb @@ -80,8 +80,11 @@ class Chef shared_resource_requirements requirements.assert(:all_actions) do |a| if !@command_success - whyrun_msg = @new_resource.status_command ? "Provided status command #{@new_resource.status_command} failed." : - "Could not determine upstart state for service" + whyrun_msg = if @new_resource.status_command + "Provided status command #{@new_resource.status_command} failed." + else + "Could not determine upstart state for service" + end end a.assertion { @command_success } # no failure here, just document the assumptions made. @@ -116,7 +119,7 @@ class Chef end else begin - if upstart_state == "running" + if upstart_goal_state == "start" @current_resource.running true else @current_resource.running false @@ -224,7 +227,7 @@ class Chef conf.write_file end - def upstart_state + def upstart_goal_state command = "/sbin/status #{@job}" status = popen4(command) do |pid, stdin, stdout, stderr| stdout.each_line do |line| @@ -235,7 +238,7 @@ class Chef # service (goal) state line =~ UPSTART_STATE_FORMAT data = Regexp.last_match - return data[2] + return data[1] end end end diff --git a/lib/chef/provider/support/yum_repo.erb b/lib/chef/provider/support/yum_repo.erb new file mode 100644 index 0000000000..7d9a2d09e2 --- /dev/null +++ b/lib/chef/provider/support/yum_repo.erb @@ -0,0 +1,125 @@ +# This file was generated by Chef +# Do NOT modify this file by hand. + +[<%= @config.repositoryid %>] +name=<%= @config.description %> +<% if @config.baseurl %> +baseurl=<%= @config.baseurl %> +<% end %> +<% if @config.cost %> +cost=<%= @config.cost %> +<% end %> +<% if @config.enabled %> +enabled=1 +<% else %> +enabled=0 +<% end %> +<% if @config.enablegroups %> +enablegroups=1 +<% end %> +<% if @config.exclude %> +exclude=<%= @config.exclude %> +<% end %> +<% if @config.failovermethod %> +failovermethod=<%= @config.failovermethod %> +<% end %> +<% if @config.fastestmirror_enabled %> +fastestmirror_enabled=<%= @config.fastestmirror_enabled %> +<% end %> +<% if @config.gpgcheck %> +gpgcheck=1 +<% else %> +gpgcheck=0 +<% end %> +<% if @config.gpgkey %> +gpgkey=<%= case @config.gpgkey + when Array + @config.gpgkey.join("\n ") + else + @config.gpgkey + end %> +<% end -%> +<% if @config.http_caching %> +http_caching=<%= @config.http_caching %> +<% end %> +<% if @config.include_config %> +include=<%= @config.include_config %> +<% end %> +<% if @config.includepkgs %> +includepkgs=<%= @config.includepkgs %> +<% end %> +<% if @config.keepalive %> +keepalive=1 +<% end %> +<% if @config.metadata_expire %> +metadata_expire=<%= @config.metadata_expire %> +<% end %> +<% if @config.mirrorlist %> +mirrorlist=<%= @config.mirrorlist %> +<% end %> +<% if @config.mirror_expire %> +mirror_expire=<%= @config.mirror_expire %> +<% end %> +<% if @config.mirrorlist_expire %> +mirrorlist_expire=<%= @config.mirrorlist_expire %> +<% end %> +<% if @config.priority %> +priority=<%= @config.priority %> +<% end %> +<% if @config.proxy %> +proxy=<%= @config.proxy %> +<% end %> +<% if @config.proxy_username %> +proxy_username=<%= @config.proxy_username %> +<% end %> +<% if @config.proxy_password %> +proxy_password=<%= @config.proxy_password %> +<% end %> +<% if @config.username %> +username=<%= @config.username %> +<% end %> +<% if @config.password %> +password=<%= @config.password %> +<% end %> +<% if @config.repo_gpgcheck %> +repo_gpgcheck=1 +<% end %> +<% if @config.max_retries %> +retries=<%= @config.max_retries %> +<% end %> +<% if @config.report_instanceid %> +report_instanceid=<%= @config.report_instanceid %> +<% end %> +<% if @config.skip_if_unavailable %> +skip_if_unavailable=1 +<% end %> +<% if @config.sslcacert %> +sslcacert=<%= @config.sslcacert %> +<% end %> +<% if @config.sslclientcert %> +sslclientcert=<%= @config.sslclientcert %> +<% end %> +<% if @config.sslclientkey %> +sslclientkey=<%= @config.sslclientkey %> +<% end %> +<% unless @config.sslverify.nil? %> +sslverify=<%= ( @config.sslverify ) ? 'true' : 'false' %> +<% end %> +<% if @config.timeout %> +timeout=<%= @config.timeout %> +<% end %> +<% if @config.options -%> +<% @config.options.each do |key, value| -%> +<%= key %>=<%= + case value + when Array + value.join("\n ") + when TrueClass + '1' + when FalseClass + '0' + else + value + end %> +<% end -%> +<% end -%> diff --git a/lib/chef/provider/systemd_unit.rb b/lib/chef/provider/systemd_unit.rb index b96a336765..4aa4cd0aa2 100644 --- a/lib/chef/provider/systemd_unit.rb +++ b/lib/chef/provider/systemd_unit.rb @@ -193,6 +193,7 @@ class Chef f.group "root" f.mode "0644" f.content new_resource.to_ini + f.verify systemd_analyze_cmd if systemd_analyze_path end.run_action(action) end @@ -224,8 +225,8 @@ class Chef @systemctl_opts ||= if new_resource.user { - "user" => new_resource.user, - "environment" => { + :user => new_resource.user, + :environment => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{node['etc']['passwd'][new_resource.user]['uid']}/bus", }, } @@ -233,6 +234,14 @@ class Chef {} end end + + def systemd_analyze_cmd + @systemd_analyze_cmd ||= "#{systemd_analyze_path} verify %{path}" + end + + def systemd_analyze_path + @systemd_analyze_path ||= which("systemd-analyze") + end end end end diff --git a/lib/chef/provider/template_finder.rb b/lib/chef/provider/template_finder.rb index 67342a86ea..1e8b925071 100644 --- a/lib/chef/provider/template_finder.rb +++ b/lib/chef/provider/template_finder.rb @@ -40,7 +40,7 @@ class Chef cookbook.preferred_filename_on_disk_location(@node, :templates, template_name) end - protected + protected def template_source_name(name, options) if options[:source] diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb index 85bd674d8d..2bc4cc10bc 100644 --- a/lib/chef/provider/user.rb +++ b/lib/chef/provider/user.rb @@ -147,10 +147,6 @@ class Chef end end - def remove_user - raise NotImplementedError - end - def action_manage if @user_exists && compare_user converge_by("manage user #{@new_resource.username}") do @@ -160,10 +156,6 @@ class Chef end end - def manage_user - raise NotImplementedError - end - def action_modify if compare_user converge_by("modify user #{@new_resource.username}") do @@ -184,14 +176,6 @@ class Chef end end - def check_lock - raise NotImplementedError - end - - def lock_user - raise NotImplementedError - end - def action_unlock if check_lock() == true converge_by("unlock user #{@new_resource.username}") do @@ -203,9 +187,29 @@ class Chef end end + def create_user + raise NotImplementedError + end + + def remove_user + raise NotImplementedError + end + + def manage_user + raise NotImplementedError + end + + def lock_user + raise NotImplementedError + end + def unlock_user raise NotImplementedError end + + def check_lock + raise NotImplementedError + end end end end diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb index 3f168b8da3..8ac229ae4d 100644 --- a/lib/chef/provider/user/aix.rb +++ b/lib/chef/provider/user/aix.rb @@ -14,11 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +require "chef/provider/user/useradd" + class Chef class Provider class User class Aix < Chef::Provider::User::Useradd - provides :user, platform: %w{aix} + provides :user, os: "aix" + provides :aix_user UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]] @@ -66,7 +69,7 @@ class Chef shell_out!("chuser account_locked=false #{new_resource.username}") end - private + private def add_password if @current_resource.password != @new_resource.password && @new_resource.password diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index e933bf9dc0..821fa8e8a7 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -48,6 +48,7 @@ class Chef attr_accessor :authentication_authority attr_accessor :password_shadow_conversion_algorithm + provides :dscl_user provides :user, os: "darwin" def define_resource_requirements diff --git a/lib/chef/provider/user/linux.rb b/lib/chef/provider/user/linux.rb new file mode 100644 index 0000000000..f09d86e98c --- /dev/null +++ b/lib/chef/provider/user/linux.rb @@ -0,0 +1,128 @@ +# +# Copyright:: Copyright 2016, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require "chef/provider/user" + +class Chef + class Provider + class User + class Linux < Chef::Provider::User + provides :linux_user + provides :user, os: "linux" + + def create_user + shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username)) + end + + def manage_user + shell_out!(*clean_array("usermod", universal_options, usermod_options, new_resource.username)) + end + + def remove_user + shell_out!(*clean_array("userdel", userdel_options, new_resource.username)) + end + + def lock_user + shell_out!(*clean_array("usermod", "-L", new_resource.username)) + end + + def unlock_user + shell_out!(*clean_array("usermod", "-U", new_resource.username)) + end + + # common to usermod and useradd + def universal_options + opts = [] + opts << "-c" << new_resource.comment if should_set?(:comment) + opts << "-g" << new_resource.gid if should_set?(:gid) + opts << "-p" << new_resource.password if should_set?(:password) + opts << "-s" << new_resource.shell if should_set?(:shell) + opts << "-u" << new_resource.uid if should_set?(:uid) + opts << "-d" << new_resource.home if updating_home? + opts << "-o" if new_resource.non_unique + opts + end + + def usermod_options + opts = [] + if updating_home? + if new_resource.manage_home + opts << "-m" + end + end + opts + end + + def useradd_options + opts = [] + opts << "-r" if new_resource.system + if new_resource.manage_home + opts << "-m" + else + opts << "-M" + end + opts + end + + def userdel_options + opts = [] + opts << "-r" if new_resource.manage_home + opts << "-f" if new_resource.force + opts + end + + def should_set?(sym) + current_resource.send(sym).to_s != new_resource.send(sym).to_s && new_resource.send(sym) + end + + def updating_home? + return false unless new_resource.home + return true unless current_resource.home + new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath + end + + def check_lock + # there's an old bug in rhel (https://bugzilla.redhat.com/show_bug.cgi?id=578534) + # which means that both 0 and 1 can be success. + passwd_s = shell_out("passwd", "-S", new_resource.username, returns: [ 0, 1 ]) + + # checking "does not exist" has to come before exit code handling since centos and ubuntu differ in exit codes + if passwd_s.stderr =~ /does not exist/ + if whyrun_mode? + return false + else + raise Chef::Exceptions::User, "User #{new_resource.username} does not exist when checking lock status for #{new_resource}" + end + end + + # now raise if we didn't get a 0 or 1 (see above) + passwd_s.error! + + # now the actual output parsing + @locked = nil + status_line = passwd_s.stdout.split(" ") + @locked = false if status_line[1] =~ /^[PN]/ + @locked = true if status_line[1] =~ /^L/ + + raise Chef::Exceptions::User, "Cannot determine if user #{new_resource.username} is locked for #{new_resource}" if @locked.nil? + + # FIXME: should probably go on the current_resource + @locked + end + end + end + end +end diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb index 949a21790b..a1d7671c28 100644 --- a/lib/chef/provider/user/pw.rb +++ b/lib/chef/provider/user/pw.rb @@ -22,7 +22,8 @@ class Chef class Provider class User class Pw < Chef::Provider::User - provides :user, platform: %w{freebsd} + provides :pw_user + provides :user, os: "freebsd" def load_current_resource super diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb index 1f0cbb6054..8d3df9e68b 100644 --- a/lib/chef/provider/user/solaris.rb +++ b/lib/chef/provider/user/solaris.rb @@ -24,7 +24,8 @@ class Chef class Provider class User class Solaris < Chef::Provider::User::Useradd - provides :user, platform: %w{omnios solaris2} + provides :solaris_user + provides :user, os: %w{omnios solaris2} UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]] attr_writer :password_file @@ -70,7 +71,7 @@ class Chef shell_out!("passwd", "-u", new_resource.username) end - private + private def manage_password if @current_resource.password != @new_resource.password && @new_resource.password diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb index 3fef8d3642..68b62812a7 100644 --- a/lib/chef/provider/user/useradd.rb +++ b/lib/chef/provider/user/useradd.rb @@ -23,7 +23,7 @@ class Chef class Provider class User class Useradd < Chef::Provider::User - provides :user + # MAJOR XXX: this should become the base class of all Useradd providers instead of the linux implementation UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]] @@ -116,15 +116,15 @@ class Chef update_options(field, option, opts) end if updating_home? + opts << "-d" << new_resource.home if managing_home_dir? Chef::Log.debug("#{new_resource} managing the users home directory") - opts << "-d" << new_resource.home << "-m" + opts << "-m" else Chef::Log.debug("#{new_resource} setting home to #{new_resource.home}") - opts << "-d" << new_resource.home end end - opts << "-o" if new_resource.non_unique || new_resource.supports[:non_unique] + opts << "-o" if new_resource.non_unique opts end end @@ -141,6 +141,7 @@ class Chef def useradd_options opts = [] opts << "-r" if new_resource.system + opts << "-M" unless managing_home_dir? opts end diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb index 9545b1fd59..b086a1e32b 100644 --- a/lib/chef/provider/user/windows.rb +++ b/lib/chef/provider/user/windows.rb @@ -26,7 +26,7 @@ class Chef class Provider class User class Windows < Chef::Provider::User - + provides :windows_user provides :user, os: "windows" def initialize(new_resource, run_context) diff --git a/lib/chef/provider/windows_script.rb b/lib/chef/provider/windows_script.rb index 2de127addf..3b0202790c 100644 --- a/lib/chef/provider/windows_script.rb +++ b/lib/chef/provider/windows_script.rb @@ -33,8 +33,11 @@ class Chef super( new_resource, run_context ) @script_extension = script_extension - target_architecture = new_resource.architecture.nil? ? - node_windows_architecture(run_context.node) : new_resource.architecture + target_architecture = if new_resource.architecture.nil? + node_windows_architecture(run_context.node) + else + new_resource.architecture + end @is_wow64 = wow64_architecture_override_required?(run_context.node, target_architecture) diff --git a/lib/chef/provider/yum_repository.rb b/lib/chef/provider/yum_repository.rb new file mode 100644 index 0000000000..09ff2c5512 --- /dev/null +++ b/lib/chef/provider/yum_repository.rb @@ -0,0 +1,121 @@ +# +# Author:: Thom May (<thom@chef.io>) +# Copyright:: Copyright (c) 2016 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/resource" +require "chef/dsl/declare_resource" +require "chef/mixin/shell_out" +require "chef/mixin/which" +require "chef/http/simple" +require "chef/provider/noop" + +class Chef + class Provider + class YumRepository < Chef::Provider + use_inline_resources + + extend Chef::Mixin::Which + + provides :yum_repository do + which "yum" + end + + def whyrun_supported?; true; end + + def load_current_resource; end + + action :create do + declare_resource(:template, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do + if template_available?(new_resource.source) + source new_resource.source + else + source ::File.expand_path("../support/yum_repo.erb", __FILE__) + local true + end + sensitive new_resource.sensitive + variables(config: new_resource) + mode new_resource.mode + if new_resource.make_cache + notifies :run, "execute[yum clean metadata #{new_resource.repositoryid}]", :immediately if new_resource.clean_metadata || new_resource.clean_headers + notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately + notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately + end + end + + declare_resource(:execute, "yum clean metadata #{new_resource.repositoryid}") do + command "yum clean metadata --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + action :nothing + end + + # get the metadata for this repo only + declare_resource(:execute, "yum-makecache-#{new_resource.repositoryid}") do + command "yum -q -y makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + action :nothing + only_if { new_resource.enabled } + end + + # reload internal Chef yum cache + declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do + block { Chef::Provider::Package::Yum::YumCache.instance.reload } + action :nothing + end + end + + action :delete do + declare_resource(:file, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do + action :delete + notifies :run, "execute[yum clean all #{new_resource.repositoryid}]", :immediately + notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately + end + + declare_resource(:execute, "yum clean all #{new_resource.repositoryid}") do + command "yum clean all --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + only_if "yum repolist | grep -P '^#{new_resource.repositoryid}([ \t]|$)'" + action :nothing + end + + declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do + block { Chef::Provider::Package::Yum::YumCache.instance.reload } + action :nothing + end + end + + action :makecache do + declare_resource(:execute, "yum-makecache-#{new_resource.repositoryid}") do + command "yum -q -y makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}" + action :run + only_if { new_resource.enabled } + end + + declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do + block { Chef::Provider::Package::Yum::YumCache.instance.reload } + action :run + end + end + + alias_method :action_add, :action_create + alias_method :action_remove, :action_delete + + def template_available?(path) + !path.nil? && run_context.has_template_in_cookbook?(new_resource.cookbook_name, path) + end + + end + end +end + +Chef::Provider::Noop.provides :yum_repository diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index 14c47df939..affa5ca2c1 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -58,6 +58,7 @@ require "chef/provider/systemd_unit" require "chef/provider/template" require "chef/provider/user" require "chef/provider/whyrun_safe_ruby_block" +require "chef/provider/yum_repository" require "chef/provider/env/windows" @@ -101,12 +102,13 @@ require "chef/provider/service/macosx" require "chef/provider/service/aixinit" require "chef/provider/service/aix" +require "chef/provider/user/aix" require "chef/provider/user/dscl" +require "chef/provider/user/linux" require "chef/provider/user/pw" +require "chef/provider/user/solaris" require "chef/provider/user/useradd" require "chef/provider/user/windows" -require "chef/provider/user/solaris" -require "chef/provider/user/aix" require "chef/provider/group/aix" require "chef/provider/group/dscl" diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb index 3cc5634dc8..77d82f83ab 100644 --- a/lib/chef/recipe.rb +++ b/lib/chef/recipe.rb @@ -18,16 +18,7 @@ # require "chef/dsl/recipe" -require "chef/dsl/data_query" -require "chef/dsl/platform_introspection" -require "chef/dsl/include_recipe" -require "chef/dsl/registry_helper" -require "chef/dsl/reboot_pending" -require "chef/dsl/audit" -require "chef/dsl/powershell" - require "chef/mixin/from_file" - require "chef/mixin/deprecation" class Chef diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 479eb0a7e2..d11fa1c80c 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -497,7 +497,7 @@ class Chef state_properties = self.class.state_properties state_properties.each do |property| if property.identity? || property.is_set?(self) - state[property.name] = send(property.name) + state[property.name] = property.sensitive? ? "*sensitive value suppressed*" : send(property.name) end end state @@ -1276,15 +1276,15 @@ class Chef # resolve_resource_reference on each in turn, causing them to # resolve lazy/forward references. def resolve_notification_references - run_context.before_notifications(self).each { |n| + run_context.before_notifications(self).each do |n| n.resolve_resource_reference(run_context.resource_collection) - } - run_context.immediate_notifications(self).each { |n| + end + run_context.immediate_notifications(self).each do |n| n.resolve_resource_reference(run_context.resource_collection) - } - run_context.delayed_notifications(self).each {|n| + end + run_context.delayed_notifications(self).each do |n| n.resolve_resource_reference(run_context.resource_collection) - } + end end # Helper for #notifies @@ -1575,8 +1575,6 @@ class Chef end end - private - def self.remove_canonical_dsl if @resource_name remaining = Chef.resource_handler_map.delete_canonical(@resource_name, self) diff --git a/lib/chef/resource/conditional.rb b/lib/chef/resource/conditional.rb index cdb9f13c45..452718cae8 100644 --- a/lib/chef/resource/conditional.rb +++ b/lib/chef/resource/conditional.rb @@ -103,7 +103,15 @@ class Chef end def evaluate_block - @block.call + @block.call.tap do |rv| + if rv.is_a?(String) && !rv.empty? + # This is probably a mistake: + # not_if { "command" } + sanitized_rv = @parent_resource.sensitive ? "a string" : rv.inspect + Chef::Log.warn("#{@positivity} block for #{@parent_resource} returned #{sanitized_rv}, did you mean to run a command?" + + (@parent_resource.sensitive ? "" : " If so use '#{@positivity} #{sanitized_rv}' in your code.")) + end + end end def short_description diff --git a/lib/chef/resource/freebsd_package.rb b/lib/chef/resource/freebsd_package.rb index 540774e864..a94dd0a928 100644 --- a/lib/chef/resource/freebsd_package.rb +++ b/lib/chef/resource/freebsd_package.rb @@ -45,7 +45,7 @@ class Chef def ships_with_pkgng? # It was not until __FreeBSD_version 1000017 that pkgng became # the default binary package manager. See '/usr/ports/Mk/bsd.port.mk'. - node.automatic[:os_version].to_i >= 1000017 + node[:os_version].to_i >= 1000017 end def assign_provider diff --git a/lib/chef/resource/launchd.rb b/lib/chef/resource/launchd.rb index f3c378a6a8..8dca90ef0e 100644 --- a/lib/chef/resource/launchd.rb +++ b/lib/chef/resource/launchd.rb @@ -68,7 +68,7 @@ class Chef property :hard_resource_limits, Hash property :inetd_compatibility, Hash property :init_groups, [ TrueClass, FalseClass ] - property :keep_alive, [ TrueClass, FalseClass ] + property :keep_alive, [ TrueClass, FalseClass, Hash ] property :launch_only_once, [ TrueClass, FalseClass ] property :ld_group, String property :limit_load_from_hosts, Array diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb index 012fa278f1..a07ce8b24b 100644 --- a/lib/chef/resource/user.rb +++ b/lib/chef/resource/user.rb @@ -21,6 +21,7 @@ require "chef/resource" class Chef class Resource class User < Chef::Resource + resource_name :user_resource_abstract_base_class # this prevents magickal class name DSL wiring identity_attr :username state_attrs :uid, :gid, :home @@ -42,8 +43,8 @@ class Chef @force = false @non_unique = false @supports = { - :manage_home => false, - :non_unique => false, + manage_home: false, + non_unique: false, } @iterations = 27855 @salt = nil @@ -154,7 +155,6 @@ class Chef :kind_of => [ TrueClass, FalseClass ] ) end - end end end diff --git a/lib/chef/resource/user/aix_user.rb b/lib/chef/resource/user/aix_user.rb new file mode 100644 index 0000000000..7c07db2e25 --- /dev/null +++ b/lib/chef/resource/user/aix_user.rb @@ -0,0 +1,31 @@ +# +# Copyright:: Copyright 2016, 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/resource/user" + +class Chef + class Resource + class User + class AixUser < Chef::Resource::User + resource_name :aix_user + + provides :aix_user + provides :user, os: "aix" + end + end + end +end diff --git a/lib/chef/resource/user/dscl_user.rb b/lib/chef/resource/user/dscl_user.rb new file mode 100644 index 0000000000..61517d8b44 --- /dev/null +++ b/lib/chef/resource/user/dscl_user.rb @@ -0,0 +1,31 @@ +# +# Copyright:: Copyright 2016, 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/resource/user" + +class Chef + class Resource + class User + class DsclUser < Chef::Resource::User + resource_name :dscl_user + + provides :dscl_user + provides :user, os: "darwin" + end + end + end +end diff --git a/lib/chef/resource/user/linux_user.rb b/lib/chef/resource/user/linux_user.rb new file mode 100644 index 0000000000..23d6129373 --- /dev/null +++ b/lib/chef/resource/user/linux_user.rb @@ -0,0 +1,51 @@ +# +# Copyright:: Copyright 2016, 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/resource/user" + +class Chef + class Resource + class User + class LinuxUser < Chef::Resource::User + resource_name :linux_user + + provides :linux_user + provides :user, os: "linux" + + def initialize(name, run_context = nil) + super + @supports = { + manage_home: true, + non_unique: true, + } + @manage_home = false + end + + def supports(args = {}) + Chef.log_deprecation "setting supports on the linux_user resource is deprecated" + # setting is deliberately disabled + super({}) + end + + def supports=(args) + # setting is deliberately disabled + supports({}) + end + end + end + end +end diff --git a/lib/chef/resource/user/pw_user.rb b/lib/chef/resource/user/pw_user.rb new file mode 100644 index 0000000000..873be19d59 --- /dev/null +++ b/lib/chef/resource/user/pw_user.rb @@ -0,0 +1,31 @@ +# +# Copyright:: Copyright 2016, 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/resource/user" + +class Chef + class Resource + class User + class PwUser < Chef::Resource::User + resource_name :pw_user + + provides :pw_user + provides :user, os: "freebsd" + end + end + end +end diff --git a/lib/chef/resource/user/solaris_user.rb b/lib/chef/resource/user/solaris_user.rb new file mode 100644 index 0000000000..bb897228b9 --- /dev/null +++ b/lib/chef/resource/user/solaris_user.rb @@ -0,0 +1,31 @@ +# +# Copyright:: Copyright 2016, 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/resource/user" + +class Chef + class Resource + class User + class SolarisUser < Chef::Resource::User + resource_name :solaris_user + + provides :solaris_user + provides :user, os: %w{omnios solaris2} + end + end + end +end diff --git a/lib/chef/resource/user/windows_user.rb b/lib/chef/resource/user/windows_user.rb new file mode 100644 index 0000000000..d1a249fb50 --- /dev/null +++ b/lib/chef/resource/user/windows_user.rb @@ -0,0 +1,31 @@ +# +# Copyright:: Copyright 2016, 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/resource/user" + +class Chef + class Resource + class User + class WindowsUser < Chef::Resource::User + resource_name :windows_user + + provides :windows_user + provides :user, os: "windows" + end + end + end +end diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb index 1f749b93c0..9d69897f5f 100644 --- a/lib/chef/resource/yum_package.rb +++ b/lib/chef/resource/yum_package.rb @@ -27,18 +27,19 @@ class Chef # Install a specific arch property :arch, [ String, Array ] - property :flush_cache, Hash, default: { before: false, after: false }, coerce: proc { |v| - # TODO these append rather than set. This is probably wrong behavior, but we're preserving it until we know - if v.is_a?(Array) - v.each { |arg| flush_cache[arg] = true } - flush_cache - elsif v.any? - v - else - # TODO calling flush_cache({}) does a get instead of a set. This is probably wrong behavior, but we're preserving it until we know - flush_cache - end - } + # the {} on the proc here is because rspec chokes if it's do...end + property :flush_cache, + Hash, + default: { before: false, after: false }, + coerce: proc { |v| + if v.is_a?(Array) + v.each_with_object({}) { |arg, obj| obj[arg] = true } + elsif v.any? + v + else + { before: v, after: v } + end + } property :allow_downgrade, [ true, false ], default: false property :yum_binary, String diff --git a/lib/chef/resource/yum_repository.rb b/lib/chef/resource/yum_repository.rb new file mode 100644 index 0000000000..4b3c6edfb7 --- /dev/null +++ b/lib/chef/resource/yum_repository.rb @@ -0,0 +1,76 @@ +# +# Author:: Thom May (<thom@chef.io>) +# Copyright:: Copyright (c) 2016 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/resource" + +class Chef + class Resource + class YumRepository < Chef::Resource + resource_name :yum_repository + provides :yum_repository + + # http://linux.die.net/man/5/yum.conf + property :baseurl, String, regex: /.*/ + property :cost, String, regex: /^\d+$/ + property :clean_headers, [TrueClass, FalseClass], default: false # deprecated + property :clean_metadata, [TrueClass, FalseClass], default: true + property :description, String, regex: /.*/, default: "Ye Ole Rpm Repo" + property :enabled, [TrueClass, FalseClass], default: true + property :enablegroups, [TrueClass, FalseClass] + property :exclude, String, regex: /.*/ + property :failovermethod, String, equal_to: %w{priority roundrobin} + property :fastestmirror_enabled, [TrueClass, FalseClass] + property :gpgcheck, [TrueClass, FalseClass] + property :gpgkey, [String, Array], regex: /.*/ + property :http_caching, String, equal_to: %w{packages all none} + property :include_config, String, regex: /.*/ + property :includepkgs, String, regex: /.*/ + property :keepalive, [TrueClass, FalseClass] + property :make_cache, [TrueClass, FalseClass], default: true + property :max_retries, [String, Integer] + property :metadata_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/, /never/] + property :mirrorexpire, String, regex: /.*/ + property :mirrorlist, String, regex: /.*/ + property :mirror_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/] + property :mirrorlist_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/] + property :mode, default: "0644" + property :priority, String, regex: /^(\d?[0-9]|[0-9][0-9])$/ + property :proxy, String, regex: /.*/ + property :proxy_username, String, regex: /.*/ + property :proxy_password, String, regex: /.*/ + property :username, String, regex: /.*/ + property :password, String, regex: /.*/ + property :repo_gpgcheck, [TrueClass, FalseClass] + property :report_instanceid, [TrueClass, FalseClass] + property :repositoryid, String, regex: /.*/, name_attribute: true + property :sensitive, [TrueClass, FalseClass], default: false + property :skip_if_unavailable, [TrueClass, FalseClass] + property :source, String, regex: /.*/ + property :sslcacert, String, regex: /.*/ + property :sslclientcert, String, regex: /.*/ + property :sslclientkey, String, regex: /.*/ + property :sslverify, [TrueClass, FalseClass] + property :timeout, String, regex: /^\d+$/ + + property :options, Hash + + default_action :create + allowed_actions :create, :remove, :make_cache, :add + end + end +end diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb index 1641fe60f2..57c57dd8c3 100644 --- a/lib/chef/resource_builder.rb +++ b/lib/chef/resource_builder.rb @@ -31,7 +31,7 @@ class Chef attr_reader :resource # FIXME (ruby-2.1 syntax): most of these are mandatory - def initialize(type:nil, name:nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil) + def initialize(type: nil, name: nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil) @type = type @name = name @created_at = created_at diff --git a/lib/chef/resource_collection/stepable_iterator.rb b/lib/chef/resource_collection/stepable_iterator.rb index d1165764ca..958ffa28cb 100644 --- a/lib/chef/resource_collection/stepable_iterator.rb +++ b/lib/chef/resource_collection/stepable_iterator.rb @@ -100,9 +100,7 @@ class Chef end def iterate - while @position < size && !paused? - step - end + step while @position < size && !paused? collection end diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb index 4135483441..8422870e2a 100644 --- a/lib/chef/resource_reporter.rb +++ b/lib/chef/resource_reporter.rb @@ -26,11 +26,11 @@ require "chef/event_dispatch/base" class Chef class ResourceReporter < EventDispatch::Base - class ResourceReport < Struct.new(:new_resource, - :current_resource, - :action, - :exception, - :elapsed_time) + ResourceReport = Struct.new(:new_resource, + :current_resource, + :action, + :exception, + :elapsed_time) do def self.new_with_current_state(new_resource, action, current_resource) report = new diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index af9c918f55..2afd47a8f4 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -82,9 +82,16 @@ require "chef/resource/smartos_package" require "chef/resource/template" require "chef/resource/timestamped_deploy" require "chef/resource/user" +require "chef/resource/user/aix_user" +require "chef/resource/user/dscl_user" +require "chef/resource/user/linux_user" +require "chef/resource/user/pw_user" +require "chef/resource/user/solaris_user" +require "chef/resource/user/windows_user" require "chef/resource/whyrun_safe_ruby_block" require "chef/resource/windows_package" require "chef/resource/yum_package" +require "chef/resource/yum_repository" require "chef/resource/lwrp_base" require "chef/resource/bff_package" require "chef/resource/zypper_package" diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index 7ef476c44b..5d29f766c9 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -658,9 +658,11 @@ ERROR_MESSAGE }.map { |x| x.to_sym } # Verify that we didn't miss any methods - missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE - if !missing_methods.empty? - raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}." + unless @__skip_method_checking # hook specifically for compat_resource + missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE + if !missing_methods.empty? + raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}." + end end end end diff --git a/lib/chef/run_context/cookbook_compiler.rb b/lib/chef/run_context/cookbook_compiler.rb index bdf3a1251c..b2a8d236a3 100644 --- a/lib/chef/run_context/cookbook_compiler.rb +++ b/lib/chef/run_context/cookbook_compiler.rb @@ -137,13 +137,14 @@ class Chef @events.recipe_load_start(run_list_expansion.recipes.size) run_list_expansion.recipes.each do |recipe| begin + path = resolve_recipe(recipe) @run_context.load_recipe(recipe) + @events.recipe_file_loaded(path, recipe) rescue Chef::Exceptions::RecipeNotFound => e @events.recipe_not_found(e) raise rescue Exception => e - path = resolve_recipe(recipe) - @events.recipe_file_load_failed(path, e) + @events.recipe_file_load_failed(path, e, recipe) raise end end diff --git a/lib/chef/run_list.rb b/lib/chef/run_list.rb index 4dea938423..3ac5fab07b 100644 --- a/lib/chef/run_list.rb +++ b/lib/chef/run_list.rb @@ -47,13 +47,13 @@ class Chef end def role_names - @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.role? ; memo } + @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.role?; memo } end alias :roles :role_names def recipe_names - @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.recipe? ; memo } + @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.recipe?; memo } end alias :recipes :recipe_names diff --git a/lib/chef/search/query.rb b/lib/chef/search/query.rb index ebf13bec36..bea8205935 100644 --- a/lib/chef/search/query.rb +++ b/lib/chef/search/query.rb @@ -21,6 +21,7 @@ require "chef/exceptions" require "chef/server_api" require "uri" +require "addressable/uri" class Chef class Search @@ -29,7 +30,7 @@ class Chef attr_accessor :rest attr_reader :config - def initialize(url = nil, config:Chef::Config) + def initialize(url = nil, config: Chef::Config) @config = config @url = url end @@ -134,19 +135,21 @@ WARNDEP args_h end - def escape(s) - s && URI.escape(s.to_s) + QUERY_PARAM_VALUE = Addressable::URI::CharacterClasses::QUERY + "\\&\\;" + + def escape_value(s) + s && Addressable::URI.encode_component(s.to_s, QUERY_PARAM_VALUE) end def create_query_string(type, query, rows, start, sort) - qstr = "search/#{type}?q=#{escape(query)}" - qstr += "&sort=#{escape(sort)}" if sort - qstr += "&start=#{escape(start)}" if start - qstr += "&rows=#{escape(rows)}" if rows + qstr = "search/#{type}?q=#{escape_value(query)}" + qstr += "&sort=#{escape_value(sort)}" if sort + qstr += "&start=#{escape_value(start)}" if start + qstr += "&rows=#{escape_value(rows)}" if rows qstr end - def call_rest_service(type, query:"*:*", rows:nil, start:0, sort:"X_CHEF_id_CHEF_X asc", filter_result:nil) + def call_rest_service(type, query: "*:*", rows: nil, start: 0, sort: "X_CHEF_id_CHEF_X asc", filter_result: nil) query_string = create_query_string(type, query, rows, start, sort) if filter_result diff --git a/lib/chef/shell/ext.rb b/lib/chef/shell/ext.rb index 40d7e10d2e..0c10309521 100644 --- a/lib/chef/shell/ext.rb +++ b/lib/chef/shell/ext.rb @@ -39,13 +39,13 @@ module Shell # irb breaks if you prematurely define IRB::JobMangager # so these methods need to be defined at the latest possible time. unless jobs.respond_to?(:select_session_by_context) - def jobs.select_session_by_context(&block) + def jobs.select_session_by_context(&block) # rubocop:disable Lint/NestedMethodDefinition @jobs.select { |job| block.call(job[1].context.main) } end end unless jobs.respond_to?(:session_select) - def jobs.select_shell_session(target_context) + def jobs.select_shell_session(target_context) # rubocop:disable Lint/NestedMethodDefinition session = if target_context.kind_of?(Class) select_session_by_context { |main| main.kind_of?(target_context) } else diff --git a/lib/chef/shell/shell_session.rb b/lib/chef/shell/shell_session.rb index 0a8cba5be3..a458286157 100644 --- a/lib/chef/shell/shell_session.rb +++ b/lib/chef/shell/shell_session.rb @@ -126,7 +126,7 @@ module Shell end def shorten_node_inspect - def @node.inspect + def @node.inspect # rubocop:disable Lint/NestedMethodDefinition "<Chef::Node:0x#{self.object_id.to_s(16)} @name=\"#{self.name}\">" end end diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb index 739a463ad5..8b492d483a 100644 --- a/lib/chef/util/dsc/configuration_generator.rb +++ b/lib/chef/util/dsc/configuration_generator.rb @@ -48,7 +48,7 @@ class Chef::Util::DSC configuration_document_location = find_configuration_document(configuration_name) if ! configuration_document_location - raise RuntimeError, "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script" + raise "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script" end configuration_document = get_configuration_document(configuration_document_location) diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb index 6ab380c071..e300266b1e 100644 --- a/lib/chef/util/powershell/cmdlet.rb +++ b/lib/chef/util/powershell/cmdlet.rb @@ -62,8 +62,11 @@ class Chef json_depth = @output_format_options[:depth] end - json_command = @json_format ? " | convertto-json -compress -depth #{json_depth} "\ - "> #{streams[:json].path}" : "" + json_command = if @json_format + " | convertto-json -compress -depth #{json_depth} > #{streams[:json].path}" + else + "" + end redirections = "4> '#{streams[:verbose].path}'" command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\ "-command \"trap [Exception] {write-error -exception "\ diff --git a/lib/chef/util/selinux.rb b/lib/chef/util/selinux.rb index 1aac7eeeca..edca589034 100644 --- a/lib/chef/util/selinux.rb +++ b/lib/chef/util/selinux.rb @@ -78,7 +78,7 @@ class Chef when 0 return true else - raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}" + raise "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}" end else # We assume selinux is not enabled if selinux utils are not diff --git a/lib/chef/version.rb b/lib/chef/version.rb index dbf514e6ca..1789bf46ad 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -21,7 +21,7 @@ class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.13.16" + VERSION = "12.14.60" end # diff --git a/lib/chef/version_class.rb b/lib/chef/version_class.rb index 35a9f3282f..f26368902d 100644 --- a/lib/chef/version_class.rb +++ b/lib/chef/version_class.rb @@ -32,10 +32,15 @@ class Chef "#{@major}.#{@minor}.#{@patch}" end - def <=>(v) + def <=>(other) [:major, :minor, :patch].each do |method| - ans = (self.send(method) <=> v.send(method)) - return ans if ans != 0 + version = self.send(method) + begin + ans = (version <=> other.send(method)) + rescue NoMethodError # if the other thing isn't a version object, return nil + return nil + end + return ans unless ans == 0 end 0 end diff --git a/lib/chef/version_constraint.rb b/lib/chef/version_constraint.rb index d4fa5df775..f10325f946 100644 --- a/lib/chef/version_constraint.rb +++ b/lib/chef/version_constraint.rb @@ -57,8 +57,8 @@ class Chef "#{@op} #{@raw_version}" end - def eql?(o) - o.class == self.class && @op == o.op && @version == o.version + def eql?(other) + other.class == self.class && @op == other.op && @version == other.version end alias_method :==, :eql? diff --git a/lib/chef/win32/api/error.rb b/lib/chef/win32/api/error.rb index dc83f9cb2b..12ccdb5ee9 100644 --- a/lib/chef/win32/api/error.rb +++ b/lib/chef/win32/api/error.rb @@ -194,12 +194,12 @@ class Chef ERROR_INVALID_EXE_SIGNATURE = 191 ERROR_EXE_MARKED_INVALID = 192 ERROR_BAD_EXE_FORMAT = 193 - ERROR_ITERATED_DATA_EXCEEDS_64k = 194 + ERROR_ITERATED_DATA_EXCEEDS_64k = 194 # rubocop:disable Style/ConstantName ERROR_INVALID_MINALLOCSIZE = 195 ERROR_DYNLINK_FROM_INVALID_RING = 196 ERROR_IOPL_NOT_ENABLED = 197 ERROR_INVALID_SEGDPL = 198 - ERROR_AUTODATASEG_EXCEEDS_64k = 199 + ERROR_AUTODATASEG_EXCEEDS_64k = 199 # rubocop:disable Style/ConstantName ERROR_RING2SEG_MUST_BE_MOVABLE = 200 ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 201 ERROR_INFLOOP_IN_RELOC_CHAIN = 202 diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb index bec00f638a..abf0dd83ec 100644 --- a/lib/chef/win32/api/net.rb +++ b/lib/chef/win32/api/net.rb @@ -45,7 +45,7 @@ class Chef USE_FORCE = 1 USE_LOTS_OF_FORCE = 2 #every windows API should support this flag - NERR_Success = 0 + NERR_Success = 0 # rubocop:disable Style/ConstantName ERROR_MORE_DATA = 234 ffi_lib "netapi32" diff --git a/lib/chef/win32/eventlog.rb b/lib/chef/win32/eventlog.rb index 4254b8ead3..eae0ae4abf 100644 --- a/lib/chef/win32/eventlog.rb +++ b/lib/chef/win32/eventlog.rb @@ -26,6 +26,6 @@ if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?) end require "win32/eventlog" - Chef::Win32EventLogLoaded = true + Chef::Win32EventLogLoaded = true # rubocop:disable Style/ConstantName end end diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb index 2a8f453432..1009f8c5a9 100644 --- a/lib/chef/win32/file.rb +++ b/lib/chef/win32/file.rb @@ -39,7 +39,7 @@ class Chef # returns nil as per MRI. # def self.link(old_name, new_name) - raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) + raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) || ::File.symlink?(old_name) # TODO do a check for CreateHardLinkW and # raise NotImplemented exception on older Windows old_name = encode_path(old_name) @@ -56,7 +56,7 @@ class Chef # returns nil as per MRI. # def self.symlink(old_name, new_name) - # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) + # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) || ::File.symlink?(old_name) # TODO do a check for CreateSymbolicLinkW and # raise NotImplemented exception on older Windows flags = ::File.directory?(old_name) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0 @@ -75,7 +75,7 @@ class Chef def self.symlink?(file_name) is_symlink = false path = encode_path(file_name) - if ::File.exists?(file_name) + if ::File.exists?(file_name) || ::File.symlink?(file_name) if (GetFileAttributesW(path) & FILE_ATTRIBUTE_REPARSE_POINT) > 0 file_search_handle(file_name) do |handle, find_data| if find_data[:dw_reserved_0] == IO_REPARSE_TAG_SYMLINK @@ -93,7 +93,7 @@ class Chef # will raise a NotImplementedError, as per MRI. # def self.readlink(link_name) - raise Errno::ENOENT, link_name unless ::File.exists?(link_name) + raise Errno::ENOENT, link_name unless ::File.exists?(link_name) || ::File.symlink?(link_name) symlink_file_handle(link_name) do |handle| # Go to DeviceIoControl to get the symlink information # http://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb index 0454b17d49..09db2af89d 100644 --- a/lib/chef/win32/net.rb +++ b/lib/chef/win32/net.rb @@ -207,7 +207,7 @@ class Chef def self.members_to_lgrmi3(members) buf = FFI::MemoryPointer.new(LOCALGROUP_MEMBERS_INFO_3, members.size) - members.size.times.collect do |i| + Array.new(members.size) do |i| member_info = LOCALGROUP_MEMBERS_INFO_3.new( buf + i * LOCALGROUP_MEMBERS_INFO_3.size) member_info[:lgrmi3_domainandname] = FFI::MemoryPointer.from_string(wstring(members[i])) diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb index 9951b931c9..f6b88c60ce 100644 --- a/lib/chef/win32/security/sid.rb +++ b/lib/chef/win32/security/sid.rb @@ -279,7 +279,7 @@ class Chef status = NetUserEnum(servername, level, filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle) if status == NERR_Success || status == ERROR_MORE_DATA - entriesread.read_long.times.collect do |i| + Array.new(entriesread.read_long) do |i| user_info = USER_INFO_3.new(bufptr.read_pointer + i * USER_INFO_3.size) # Check if the account is the Administrator account # RID for the Administrator account is always 500 and it's privilage is set to USER_PRIV_ADMIN |