summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNoah Kantrowitz <noah@coderanger.net>2016-08-02 16:36:05 -0700
committerNoah Kantrowitz <noah@coderanger.net>2016-08-02 16:36:05 -0700
commit0368df838c36fd5a54c75007aae3c2e28cbdba1b (patch)
treed89eac235ed3890c4628c59505299627a44508ca /lib
parent16fbf0a9a81daa1e3418eca251e59b4545ae0b88 (diff)
parent767a45530b373bbd4818b93ab1efe5cd3c7da5ed (diff)
downloadchef-0368df838c36fd5a54c75007aae3c2e28cbdba1b.tar.gz
Merge branch 'master' into configoption
Diffstat (limited to 'lib')
-rw-r--r--lib/chef/application.rb4
-rw-r--r--lib/chef/application/client.rb4
-rw-r--r--lib/chef/application/solo.rb11
-rw-r--r--lib/chef/audit/audit_reporter.rb6
-rw-r--r--lib/chef/audit/runner.rb2
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb1
-rw-r--r--lib/chef/chef_fs/file_system/multiplexed_dir.rb2
-rw-r--r--lib/chef/chef_fs/file_system/repository/acls_dir.rb6
-rw-r--r--lib/chef/chef_fs/file_system/repository/base_file.rb3
-rw-r--r--lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb1
-rw-r--r--lib/chef/chef_fs/file_system/repository/directory.rb13
-rw-r--r--lib/chef/chef_fs/file_system_cache.rb80
-rw-r--r--lib/chef/data_collector.rb119
-rw-r--r--lib/chef/data_collector/messages.rb37
-rw-r--r--lib/chef/data_collector/messages/helpers.rb4
-rw-r--r--lib/chef/data_collector/resource_report.rb33
-rw-r--r--lib/chef/decorator/unchain.rb59
-rw-r--r--lib/chef/dsl/cheffish.rb1
-rw-r--r--lib/chef/dsl/declare_resource.rb10
-rw-r--r--lib/chef/exceptions.rb5
-rw-r--r--lib/chef/http.rb10
-rw-r--r--lib/chef/knife/bootstrap.rb9
-rw-r--r--lib/chef/knife/cookbook_create.rb4
-rw-r--r--lib/chef/knife/cookbook_site_download.rb9
-rw-r--r--lib/chef/knife/cookbook_site_install.rb21
-rw-r--r--lib/chef/knife/cookbook_site_list.rb9
-rw-r--r--lib/chef/knife/cookbook_site_search.rb9
-rw-r--r--lib/chef/knife/cookbook_site_share.rb29
-rw-r--r--lib/chef/knife/cookbook_site_show.rb17
-rw-r--r--lib/chef/knife/cookbook_site_unshare.rb9
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb12
-rw-r--r--lib/chef/knife/supermarket_download.rb33
-rw-r--r--lib/chef/knife/supermarket_install.rb33
-rw-r--r--lib/chef/knife/supermarket_list.rb33
-rw-r--r--lib/chef/knife/supermarket_search.rb33
-rw-r--r--lib/chef/knife/supermarket_share.rb33
-rw-r--r--lib/chef/knife/supermarket_show.rb33
-rw-r--r--lib/chef/knife/supermarket_unshare.rb33
-rw-r--r--lib/chef/node.rb45
-rw-r--r--lib/chef/node/attribute.rb193
-rw-r--r--lib/chef/node/attribute_collections.rb139
-rw-r--r--lib/chef/node/common_api.rb129
-rw-r--r--lib/chef/node/immutable_collections.rb29
-rw-r--r--lib/chef/property.rb8
-rw-r--r--lib/chef/provider.rb9
-rw-r--r--lib/chef/provider/batch.rb2
-rw-r--r--lib/chef/provider/cron.rb2
-rw-r--r--lib/chef/provider/file.rb8
-rw-r--r--lib/chef/provider/package/aix.rb6
-rw-r--r--lib/chef/provider/package/openbsd.rb2
-rw-r--r--lib/chef/provider/package/rubygems.rb12
-rw-r--r--lib/chef/provider/package/windows/exe.rb7
-rw-r--r--lib/chef/provider/package/zypper.rb14
-rw-r--r--lib/chef/provider/powershell_script.rb2
-rw-r--r--lib/chef/provider/remote_directory.rb2
-rw-r--r--lib/chef/resource.rb53
-rw-r--r--lib/chef/resource_builder.rb13
-rw-r--r--lib/chef/run_context.rb57
-rw-r--r--lib/chef/shell.rb2
-rw-r--r--lib/chef/version.rb2
60 files changed, 1057 insertions, 449 deletions
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index fa740e068a..c377ffdb36 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -29,6 +29,10 @@ require "tmpdir"
require "rbconfig"
require "chef/application/exit_code"
require "yaml"
+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/
class Chef
class Application
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index cec47ac071..71bb300971 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -27,6 +27,7 @@ require "chef/handler/error_report"
require "chef/workstation_config_loader"
require "chef/mixin/shell_out"
require "chef-config/mixin/dot_d"
+require "mixlib/archive"
class Chef::Application::Client < Chef::Application
include Chef::Mixin::ShellOut
@@ -342,8 +343,7 @@ class Chef::Application::Client < Chef::Application
FileUtils.mkdir_p(Chef::Config.chef_repo_path)
tarball_path = File.join(Chef::Config.chef_repo_path, "recipes.tgz")
fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
- result = shell_out!("tar zxvf #{tarball_path} -C #{Chef::Config.chef_repo_path}")
- Chef::Log.debug "#{result.stdout}"
+ Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
end
end
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index d38e97e82b..8bf381a975 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -29,6 +29,7 @@ require "fileutils"
require "chef/mixin/shell_out"
require "pathname"
require "chef-config/mixin/dot_d"
+require "mixlib/archive"
class Chef::Application::Solo < Chef::Application
include Chef::Mixin::ShellOut
@@ -249,6 +250,13 @@ class Chef::Application::Solo < Chef::Application
ARGV[dash_r] = "--recipe-url"
end
+ # For back compat reasons, we need to ensure that we try and use the cache_path as a repo first
+ Chef::Log.debug "Current chef_repo_path is #{Chef::Config.chef_repo_path}"
+
+ if !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
+ Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Chef::Config[:cache_path])
+ end
+
Chef::Config[:local_mode] = true
else
configure_legacy_mode!
@@ -274,8 +282,7 @@ class Chef::Application::Solo < Chef::Application
FileUtils.mkdir_p(recipes_path)
tarball_path = File.join(recipes_path, "recipes.tgz")
fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
- result = shell_out!("tar zxvf #{tarball_path} -C #{recipes_path}")
- Chef::Log.debug "#{result.stdout}"
+ Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
end
# json_attribs shuld be fetched after recipe_url tarball is unpacked.
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/audit/runner.rb b/lib/chef/audit/runner.rb
index 100a72d2e1..837346381c 100644
--- a/lib/chef/audit/runner.rb
+++ b/lib/chef/audit/runner.rb
@@ -165,7 +165,7 @@ class Chef
add_example_group_methods
run_context.audits.each do |name, group|
ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block)
- RSpec.world.register(ctl_grp)
+ RSpec.world.record(ctl_grp)
end
end
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/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
index d3951edd51..21abc012f8 100644
--- a/lib/chef/chef_fs/file_system/multiplexed_dir.rb
+++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
@@ -41,7 +41,7 @@ class Chef
child_entry = dir.child(name)
if child_entry.exists?
if result
- Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}") unless seen[child.name].path_for_printing == child.path_for_printing
+ Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}")
else
result = child_entry
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 dae467993a..328cf92b03 100644
--- a/lib/chef/chef_fs/file_system/repository/directory.rb
+++ b/lib/chef/chef_fs/file_system/repository/directory.rb
@@ -16,6 +16,8 @@
# limitations under the License.
#
+require "chef/chef_fs/file_system_cache"
+
class Chef
module ChefFS
module FileSystem
@@ -68,9 +70,11 @@ class Chef
end
def children
- dir_ls.sort.
+ return FileSystemCache.instance.children(file_path) if FileSystemCache.instance.exist?(file_path)
+ children = dir_ls.sort.
map { |child_name| make_child_entry(child_name) }.
select { |new_child| new_child.fs_entry_valid? && can_have_child?(new_child.name, new_child.dir?) }
+ FileSystemCache.instance.set_children(file_path, children)
rescue Errno::ENOENT => e
raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
end
@@ -80,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
@@ -118,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)
@@ -134,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
@@ -145,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
new file mode 100644
index 0000000000..a9d8d8bfe4
--- /dev/null
+++ b/lib/chef/chef_fs/file_system_cache.rb
@@ -0,0 +1,80 @@
+#
+# 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 "singleton"
+require "chef/client"
+
+class Chef
+ module ChefFS
+ class FileSystemCache
+ include Singleton
+
+ def initialize
+ @cache = {}
+
+ Chef::Client.when_run_starts do
+ FileSystemCache.instance.reset!
+ end
+ end
+
+ def reset!
+ @cache = {}
+ end
+
+ def exist?(path)
+ @cache.key?(path)
+ end
+
+ def children(path)
+ @cache[path]["children"]
+ end
+
+ def set_children(path, val)
+ @cache[path] ||= { "children" => [] }
+ @cache[path]["children"] = val
+ 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]
+ else
+ false
+ 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/data_collector.rb b/lib/chef/data_collector.rb
index c8dd354f60..dbb0b3771a 100644
--- a/lib/chef/data_collector.rb
+++ b/lib/chef/data_collector.rb
@@ -22,6 +22,7 @@ require "uri"
require "chef/event_dispatch/base"
require "chef/data_collector/messages"
require "chef/data_collector/resource_report"
+require "ostruct"
class Chef
@@ -51,12 +52,14 @@ class Chef
# and exports its data through a webhook-like mechanism to a configured
# endpoint.
class Reporter < EventDispatch::Base
- attr_reader :completed_resources, :status, :exception, :error_descriptions,
- :expanded_run_list, :run_status, :http,
+ attr_reader :all_resource_reports, :status, :exception, :error_descriptions,
+ :expanded_run_list, :run_context, :run_status, :http,
:current_resource_report, :enabled
def initialize
- @completed_resources = []
+ validate_data_collector_server_url!
+
+ @all_resource_reports = []
@current_resource_loaded = nil
@error_descriptions = {}
@expanded_run_list = {}
@@ -93,6 +96,29 @@ class Chef
send_run_completion(status: "failure")
end
+ # see EventDispatch::Base#converge_start
+ # Upon receipt, we stash the run_context for use at the
+ # end of the run in order to determine what resource+action
+ # combinations have not yet fired so we can report on
+ # unprocessed resources.
+ def converge_start(run_context)
+ @run_context = run_context
+ end
+
+ # see EventDispatch::Base#converge_complete
+ # At the end of the converge, we add any unprocessed resources
+ # to our report list.
+ def converge_complete
+ detect_unprocessed_resources
+ end
+
+ # see EventDispatch::Base#converge_failed
+ # At the end of the converge, we add any unprocessed resources
+ # to our report list
+ def converge_failed(exception)
+ detect_unprocessed_resources
+ end
+
# see EventDispatch::Base#resource_current_state_loaded
# Create a new ResourceReport instance that we'll use to track
# the state of this resource during the run. Nested resources are
@@ -100,13 +126,7 @@ class Chef
# resource, and we only care about tracking top-level resources.
def resource_current_state_loaded(new_resource, action, current_resource)
return if nested_resource?(new_resource)
- update_current_resource_report(
- Chef::DataCollector::ResourceReport.new(
- new_resource,
- action,
- current_resource
- )
- )
+ update_current_resource_report(create_resource_report(new_resource, action, current_resource))
end
# see EventDispatch::Base#resource_up_to_date
@@ -116,19 +136,15 @@ class Chef
end
# see EventDispatch::Base#resource_skipped
- # If this is a top-level resource, we create a ResourceReport instance
- # (because a skipped resource does not trigger the
+ # If this is a top-level resource, we create a ResourceReport
+ # instance (because a skipped resource does not trigger the
# resource_current_state_loaded event), and flag it as skipped.
def resource_skipped(new_resource, action, conditional)
return if nested_resource?(new_resource)
- update_current_resource_report(
- Chef::DataCollector::ResourceReport.new(
- new_resource,
- action
- )
- )
- current_resource_report.skipped(conditional)
+ resource_report = create_resource_report(new_resource, action)
+ resource_report.skipped(conditional)
+ update_current_resource_report(resource_report)
end
# see EventDispatch::Base#resource_updated
@@ -154,13 +170,12 @@ class Chef
end
# see EventDispatch::Base#resource_completed
- # Mark the ResourceReport instance as finished (for timing details)
- # and add it to the list of resources encountered during this run.
+ # Mark the ResourceReport instance as finished (for timing details).
# This marks the end of this resource during this run.
def resource_completed(new_resource)
if current_resource_report && !nested_resource?(new_resource)
current_resource_report.finish
- add_completed_resource(current_resource_report)
+ add_resource_report(current_resource_report)
update_current_resource_report(nil)
end
end
@@ -223,7 +238,8 @@ class Chef
yield
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse,
- Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
+ Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError,
+ Errno::EHOSTDOWN => e
disable_data_collector_reporter
code = if e.respond_to?(:response) && e.response.code
e.response.code.to_s
@@ -266,12 +282,11 @@ class Chef
# we have nothing to report.
return unless run_status
- send_to_data_collector(Chef::DataCollector::Messages.node_update_message(run_status).to_json)
send_to_data_collector(
Chef::DataCollector::Messages.run_end_message(
run_status: run_status,
expanded_run_list: expanded_run_list,
- completed_resources: completed_resources,
+ resources: all_resource_reports,
status: opts[:status],
error_descriptions: error_descriptions
).to_json
@@ -297,8 +312,12 @@ class Chef
Chef::Config[:data_collector][:token]
end
- def add_completed_resource(resource_report)
- @completed_resources << resource_report
+ def add_resource_report(resource_report)
+ @all_resource_reports << OpenStruct.new(
+ resource: resource_report.new_resource,
+ action: resource_report.action,
+ report_data: resource_report.to_hash
+ )
end
def disable_data_collector_reporter
@@ -321,6 +340,38 @@ class Chef
@error_descriptions = discription_hash
end
+ def create_resource_report(new_resource, action, current_resource = nil)
+ Chef::DataCollector::ResourceReport.new(
+ new_resource,
+ action,
+ current_resource
+ )
+ end
+
+ def detect_unprocessed_resources
+ # create a Set containing all resource+action combinations from
+ # the Resource Collection
+ collection_resources = Set.new
+ run_context.resource_collection.all_resources.each do |resource|
+ Array(resource.action).each do |action|
+ collection_resources.add([resource, action])
+ end
+ end
+
+ # Delete from the Set any resource+action combination we have
+ # already processed.
+ all_resource_reports.each do |report|
+ collection_resources.delete([report.resource, report.action])
+ end
+
+ # The items remaining in the Set are unprocessed resource+actions,
+ # so we'll create new resource reports for them which default to
+ # a state of "unprocessed".
+ collection_resources.each do |resource, action|
+ add_resource_report(create_resource_report(resource, action))
+ end
+ end
+
# If we are getting messages about a resource while we are in the middle of
# another resource's update, we assume that the nested resource is just the
# implementation of a provider, and we want to hide it from the reporting
@@ -328,6 +379,20 @@ class Chef
def nested_resource?(new_resource)
@current_resource_report && @current_resource_report.new_resource != new_resource
end
+
+ def validate_data_collector_server_url!
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:server_url] is empty. Please supply a valid URL." if data_collector_server_url.empty?
+
+ begin
+ uri = URI(data_collector_server_url)
+ rescue URI::InvalidURIError
+ raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is not a valid URI."
+ end
+
+ raise Chef::Exceptions::ConfigurationError,
+ "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is a URI with no host. Please supply a valid URL." if uri.host.nil?
+ end
end
end
end
diff --git a/lib/chef/data_collector/messages.rb b/lib/chef/data_collector/messages.rb
index e23262c9e4..8c2a84b580 100644
--- a/lib/chef/data_collector/messages.rb
+++ b/lib/chef/data_collector/messages.rb
@@ -68,17 +68,18 @@ class Chef
"id" => run_status.run_id,
"message_version" => "1.0.0",
"message_type" => "run_converge",
+ "node" => run_status.node,
"node_name" => run_status.node.name,
"organization_name" => organization,
- "resources" => reporter_data[:completed_resources].map(&:for_json),
+ "resources" => reporter_data[:resources].map(&:report_data),
"run_id" => run_status.run_id,
"run_list" => run_status.node.run_list.for_json,
"start_time" => run_status.start_time.utc.iso8601,
"end_time" => run_status.end_time.utc.iso8601,
"source" => collector_source,
"status" => reporter_data[:status],
- "total_resource_count" => reporter_data[:completed_resources].count,
- "updated_resource_count" => reporter_data[:completed_resources].select { |r| r.status == "updated" }.count,
+ "total_resource_count" => reporter_data[:resources].count,
+ "updated_resource_count" => reporter_data[:resources].select { |r| r.report_data["status"] == "updated" }.count,
}
message["error"] = {
@@ -90,36 +91,6 @@ class Chef
message
end
-
- #
- # Message payload that is sent to the DataCollector server at the
- # end of a Chef run.
- #
- # @param run_status [Chef::RunStatus] The RunStatus instance for this node/run.
- #
- # @return [Hash] A hash containing the node object and related metadata.
- #
- def self.node_update_message(run_status)
- {
- "entity_name" => run_status.node.name,
- "entity_type" => "node",
- "entity_uuid" => node_uuid,
- "id" => SecureRandom.uuid,
- "message_version" => "1.1.0",
- "message_type" => "action",
- "organization_name" => organization,
- "recorded_at" => Time.now.utc.iso8601,
- "remote_hostname" => run_status.node["fqdn"],
- "requestor_name" => run_status.node.name,
- "requestor_type" => "client",
- "run_id" => run_status.run_id,
- "service_hostname" => chef_server_fqdn(run_status),
- "source" => collector_source,
- "task" => "update",
- "user_agent" => Chef::HTTP::HTTPRequest::DEFAULT_UA,
- "data" => run_status.node,
- }
- end
end
end
end
diff --git a/lib/chef/data_collector/messages/helpers.rb b/lib/chef/data_collector/messages/helpers.rb
index 3e52f80047..c0c700f847 100644
--- a/lib/chef/data_collector/messages/helpers.rb
+++ b/lib/chef/data_collector/messages/helpers.rb
@@ -148,8 +148,8 @@ class Chef
end
def update_metadata(key, value)
- metadata[key] = value
- Chef::FileCache.store(metadata_filename, metadata.to_json, 0644)
+ updated_metadata = metadata.tap { |x| x[key] = value }
+ Chef::FileCache.store(metadata_filename, updated_metadata.to_json, 0644)
end
def metadata_filename
diff --git a/lib/chef/data_collector/resource_report.rb b/lib/chef/data_collector/resource_report.rb
index 1793fe2c9d..dcaf9c8e44 100644
--- a/lib/chef/data_collector/resource_report.rb
+++ b/lib/chef/data_collector/resource_report.rb
@@ -22,13 +22,14 @@ class Chef
class DataCollector
class ResourceReport
- attr_reader :action, :current_resource, :elapsed_time, :new_resource, :status
- attr_accessor :conditional, :exception
+ attr_reader :action, :elapsed_time, :new_resource, :status
+ attr_accessor :conditional, :current_resource, :exception
def initialize(new_resource, action, current_resource = nil)
@new_resource = new_resource
@action = action
@current_resource = current_resource
+ @status = "unprocessed"
end
def skipped(conditional)
@@ -54,22 +55,32 @@ class Chef
@elapsed_time = new_resource.elapsed_time
end
+ def elapsed_time_in_milliseconds
+ elapsed_time.nil? ? nil : (elapsed_time * 1000).to_i
+ end
+
+ def potentially_changed?
+ %w{updated failed}.include?(status)
+ end
+
def to_hash
hash = {
- "type" => new_resource.resource_name.to_sym,
- "name" => new_resource.name.to_s,
- "id" => new_resource.identity.to_s,
- "after" => new_resource.state_for_resource_reporter,
- "before" => current_resource ? current_resource.state_for_resource_reporter : {},
- "duration" => (elapsed_time * 1000).to_i.to_s,
- "delta" => new_resource.respond_to?(:diff) ? new_resource.diff : "",
- "result" => action.to_s,
- "status" => status,
+ "type" => new_resource.resource_name.to_sym,
+ "name" => new_resource.name.to_s,
+ "id" => new_resource.identity.to_s,
+ "after" => new_resource.state_for_resource_reporter,
+ "before" => current_resource ? current_resource.state_for_resource_reporter : {},
+ "duration" => elapsed_time_in_milliseconds.to_s,
+ "delta" => new_resource.respond_to?(:diff) && potentially_changed? ? new_resource.diff : "",
+ "ignore_failure" => new_resource.ignore_failure,
+ "result" => action.to_s,
+ "status" => status,
}
if new_resource.cookbook_name
hash["cookbook_name"] = new_resource.cookbook_name
hash["cookbook_version"] = new_resource.cookbook_version.version
+ hash["recipe_name"] = new_resource.recipe_name
end
hash["conditional"] = conditional.to_text if status == "skipped"
diff --git a/lib/chef/decorator/unchain.rb b/lib/chef/decorator/unchain.rb
new file mode 100644
index 0000000000..8093c70f4c
--- /dev/null
+++ b/lib/chef/decorator/unchain.rb
@@ -0,0 +1,59 @@
+class Chef
+ class Decorator < SimpleDelegator
+ #
+ # This decorator unchains method call chains and turns them into method calls
+ # with variable args. So this:
+ #
+ # node.set_unless["foo"]["bar"] = "baz"
+ #
+ # Can become:
+ #
+ # node.set_unless("foo", "bar", "baz")
+ #
+ # While this is a decorator it is not a Decorator and does not inherit because
+ # it deliberately does not need or want the method_missing magic. It is not legal
+ # to call anything on the intermediate values and only supports method chaining with
+ # #[] until the chain comes to an end with #[]=, so does not behave like a hash or
+ # array... e.g.
+ #
+ # node.default['foo'].keys is legal
+ # node.set_unless['foo'].keys is not legal now or ever
+ #
+ class Unchain
+ attr_accessor :__path__
+ attr_accessor :__method__
+
+ def initialize(obj, method)
+ @__path__ = []
+ @__method__ = method
+ @delegate_sd_obj = obj
+ end
+
+ def [](key)
+ __path__.push(key)
+ self
+ end
+
+ def []=(key, value)
+ __path__.push(key)
+ @delegate_sd_obj.public_send(__method__, *__path__, value)
+ end
+
+ # unfortunately we have to support method_missing for node.set_unless.foo.bar = 'baz' notation
+ def method_missing(symbol, *args)
+ if symbol == :to_ary
+ merged_attributes.send(symbol, *args)
+ elsif args.empty?
+ Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
+ self[symbol]
+ elsif symbol.to_s =~ /=$/
+ Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")}
+ key_to_set = symbol.to_s[/^(.+)=$/, 1]
+ self[key_to_set] = (args.length == 1 ? args[0] : args)
+ else
+ raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/dsl/cheffish.rb b/lib/chef/dsl/cheffish.rb
index de052bbe5c..03290b3674 100644
--- a/lib/chef/dsl/cheffish.rb
+++ b/lib/chef/dsl/cheffish.rb
@@ -27,6 +27,7 @@ class Chef
chef_acl
chef_client
chef_container
+ chef_data_bag_item
chef_data_bag
chef_environment
chef_group
diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb
index 8d76ddfb31..86227a0f9d 100644
--- a/lib/chef/dsl/declare_resource.rb
+++ b/lib/chef/dsl/declare_resource.rb
@@ -71,7 +71,15 @@ class Chef
# delete_resource!(:template, '/x/y.txy')
#
def delete_resource!(type, name, run_context: self.run_context)
- run_context.resource_collection.delete("#{type}[#{name}]")
+ run_context.resource_collection.delete("#{type}[#{name}]").tap do |resource|
+ # Purge any pending notifications too. This will not raise an exception
+ # if there are no notifications.
+ if resource
+ run_context.before_notification_collection.delete(resource.declared_key)
+ run_context.immediate_notification_collection.delete(resource.declared_key)
+ run_context.delayed_notification_collection.delete(resource.declared_key)
+ end
+ end
end
# Lookup a resource in the resource collection by name and delete it. Returns
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index ea90d80cd8..43759568a7 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -106,7 +106,12 @@ class Chef
# for back compat, need to raise an error that inherits from ArgumentError
class CookbookNotFoundInRepo < ArgumentError; end
class RecipeNotFound < ArgumentError; end
+ # AttributeNotFound really means the attribute file could not be found
class AttributeNotFound < RuntimeError; end
+ # NoSuchAttribute is raised on access by node.read!("foo", "bar") when node["foo"]["bar"] does not exist.
+ class NoSuchAttribute < RuntimeError; end
+ # AttributeTypeMismatch is raised by node.write!("foo", "bar", "baz") when e.g. node["foo"] = "bar" (overwriting String with Hash)
+ class AttributeTypeMismatch < RuntimeError; end
class MissingCookbookDependency < StandardError; end # CHEF-5120
class InvalidCommandOption < RuntimeError; end
class CommandTimeout < RuntimeError; end
diff --git a/lib/chef/http.rb b/lib/chef/http.rb
index c6afa97d23..3e69f58383 100644
--- a/lib/chef/http.rb
+++ b/lib/chef/http.rb
@@ -232,11 +232,11 @@ class Chef
# PERFORMANCE CRITICAL: *MUST* lazy require here otherwise we load up webrick
# via chef-zero and that hits DNS (at *require* time) which may timeout,
# when for most knife/chef-client work we never need/want this loaded.
- Thread.exclusive {
- unless defined?(SocketlessChefZeroClient)
- require "chef/http/socketless_chef_zero_client"
- end
- }
+
+ unless defined?(SocketlessChefZeroClient)
+ require "chef/http/socketless_chef_zero_client"
+ end
+
SocketlessChefZeroClient.new(base_url)
else
BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy)
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index f5dc29371f..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",
@@ -224,6 +232,7 @@ class Chef
unless valid_values.include?(v)
raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
end
+ v
}
option :node_verify_api_cert,
diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb
index 950de380f8..ccb78bb7a6 100644
--- a/lib/chef/knife/cookbook_create.rb
+++ b/lib/chef/knife/cookbook_create.rb
@@ -56,6 +56,10 @@ class Chef
:description => "Email address of cookbook maintainer"
def run
+ Chef::Log.deprecation <<EOF
+This command is being deprecated in favor of `chef generate cookbook` and will soon return an error.
+Please use `chef generate cookbook` instead of this command.
+EOF
self.config = Chef::Config.merge!(config)
if @name_args.length < 1
show_usage
diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb
index 7d0e21791d..43677cfa78 100644
--- a/lib/chef/knife/cookbook_site_download.rb
+++ b/lib/chef/knife/cookbook_site_download.rb
@@ -37,6 +37,13 @@ class Chef
:long => "--force",
:description => "Force download deprecated version"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
if current_cookbook_deprecated?
message = "DEPRECATION: This cookbook has been deprecated. "
@@ -59,7 +66,7 @@ class Chef
private
def cookbooks_api_url
- "https://supermarket.chef.io/api/v1/cookbooks"
+ "#{config[:supermarket_site]}/api/v1/cookbooks"
end
def current_cookbook_data
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index 45f3061d87..43d015dcc4 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -19,6 +19,7 @@
require "chef/knife"
require "chef/exceptions"
require "shellwords"
+require "mixlib/archive"
class Chef
class Knife
@@ -59,6 +60,13 @@ class Chef
:boolean => true,
:default => false
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
attr_reader :cookbook_name
attr_reader :vendor_path
@@ -134,6 +142,7 @@ class Chef
def download_cookbook_to(download_path)
downloader = Chef::Knife::CookbookSiteDownload.new
downloader.config[:file] = download_path
+ downloader.config[:supermarket_site] = config[:supermarket_site]
downloader.name_args = name_args
downloader.run
downloader
@@ -141,17 +150,7 @@ class Chef
def extract_cookbook(upstream_file, version)
ui.info("Uncompressing #{@cookbook_name} version #{version}.")
- extract_command = "tar zxvf \"#{convert_path upstream_file}\""
- if Chef::Platform.windows?
- tar_version = shell_out("tar --version").stdout.tr("\n", " ")
- if tar_version =~ /GNU tar/
- Chef::Log.debug("GNU tar detected, adding --force-local")
- extract_command << " --force-local"
- else
- Chef::Log.debug("non-GNU tar detected, not adding --force-local")
- end
- end
- shell_out!(extract_command, :cwd => @install_path)
+ Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path, perms: false)
end
def clear_existing_files(cookbook_path)
diff --git a/lib/chef/knife/cookbook_site_list.rb b/lib/chef/knife/cookbook_site_list.rb
index abe48bf340..3bdef8abe5 100644
--- a/lib/chef/knife/cookbook_site_list.rb
+++ b/lib/chef/knife/cookbook_site_list.rb
@@ -30,6 +30,13 @@ class Chef
:long => "--with-uri",
:description => "Show corresponding URIs"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
if config[:with_uri]
cookbooks = Hash.new
@@ -41,7 +48,7 @@ class Chef
end
def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
- cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}"
+ cookbooks_url = "#{config[:supermarket_site]}/api/v1/cookbooks?items=#{items}&start=#{start}"
cr = noauth_rest.get(cookbooks_url)
cr["items"].each do |cookbook|
cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_search.rb b/lib/chef/knife/cookbook_site_search.rb
index ba4b873efc..d401844217 100644
--- a/lib/chef/knife/cookbook_site_search.rb
+++ b/lib/chef/knife/cookbook_site_search.rb
@@ -24,12 +24,19 @@ class Chef
banner "knife cookbook site search QUERY (options)"
category "cookbook site"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
output(search_cookbook(name_args[0]))
end
def search_cookbook(query, items = 10, start = 0, cookbook_collection = {})
- cookbooks_url = "https://supermarket.chef.io/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
+ cookbooks_url = "#{config[:supermarket_site]}/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
cr = noauth_rest.get(cookbooks_url)
cr["items"].each do |cookbook|
cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index 6f37568f5f..d55d6c123a 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -50,6 +50,13 @@ class Chef
:default => false,
:description => "Don't take action, only print what files will be uploaded to Supermarket."
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
@@ -106,23 +113,17 @@ class Chef
end
def get_category(cookbook_name)
- begin
- data = noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
- if !data["category"] && data["error_code"]
- ui.fatal("Received an error from Supermarket: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.")
- exit(1)
- else
- data["category"]
- end
- rescue => e
- ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
- Chef::Log.debug("\n#{e.backtrace.join("\n")}")
- exit(1)
- end
+ data = noauth_rest.get("#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}")
+ data["category"]
+ rescue => e
+ return "Other" if e.kind_of?(Net::HTTPServerException) && e.response.code == "404"
+ ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
+ Chef::Log.debug("\n#{e.backtrace.join("\n")}")
+ exit(1)
end
def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename)
- uri = "https://supermarket.chef.io/api/v1/cookbooks"
+ uri = "#{config[:supermarket_site]}/api/v1/cookbooks"
category_string = Chef::JSONCompat.to_json({ "category" => cookbook_category })
diff --git a/lib/chef/knife/cookbook_site_show.rb b/lib/chef/knife/cookbook_site_show.rb
index c0280cb318..ce153ca5a1 100644
--- a/lib/chef/knife/cookbook_site_show.rb
+++ b/lib/chef/knife/cookbook_site_show.rb
@@ -24,21 +24,32 @@ class Chef
banner "knife cookbook site show COOKBOOK [VERSION] (options)"
category "cookbook site"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
output(format_for_display(get_cookbook_data))
end
+ def supermarket_uri
+ "#{config[:supermarket_site]}/api/v1"
+ end
+
def get_cookbook_data
case @name_args.length
when 1
- noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
+ noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}")
when 2
- noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}")
+ noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}")
end
end
def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
- cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}"
+ cookbooks_url = "#{supermarket_uri}/cookbooks?items=#{items}&start=#{start}"
cr = noauth_rest.get(cookbooks_url)
cr["items"].each do |cookbook|
cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb
index 310f6ac41d..bdabff0b94 100644
--- a/lib/chef/knife/cookbook_site_unshare.rb
+++ b/lib/chef/knife/cookbook_site_unshare.rb
@@ -30,6 +30,13 @@ class Chef
banner "knife cookbook site unshare COOKBOOK"
category "cookbook site"
+ option :supermarket_site,
+ :short => "-m SUPERMARKET_SITE",
+ :long => "--supermarket-site SUPERMARKET_SITE",
+ :description => "Supermarket Site",
+ :default => "https://supermarket.chef.io",
+ :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
def run
@cookbook_name = @name_args[0]
if @cookbook_name.nil?
@@ -41,7 +48,7 @@ class Chef
confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
begin
- rest.delete "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}"
+ rest.delete "#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}"
rescue Net::HTTPServerException => e
raise e unless e.message =~ /Forbidden/
ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it."
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index 48d2cb9e77..b2670f196b 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -54,7 +54,7 @@ class Chef
end
def client_d
- @cliend_d ||= client_d_content
+ @client_d ||= client_d_content
end
def encrypted_data_bag_secret
@@ -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/supermarket_download.rb b/lib/chef/knife/supermarket_download.rb
new file mode 100644
index 0000000000..5657558591
--- /dev/null
+++ b/lib/chef/knife/supermarket_download.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_download"
+
+class Chef
+ class Knife
+ class SupermarketDownload < Knife::CookbookSiteDownload
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket download COOKBOOK [VERSION] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_install.rb b/lib/chef/knife/supermarket_install.rb
new file mode 100644
index 0000000000..7642e68181
--- /dev/null
+++ b/lib/chef/knife/supermarket_install.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_install"
+
+class Chef
+ class Knife
+ class SupermarketInstall < Knife::CookbookSiteInstall
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket install COOKBOOK [VERSION] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_list.rb b/lib/chef/knife/supermarket_list.rb
new file mode 100644
index 0000000000..f2bc98bd0e
--- /dev/null
+++ b/lib/chef/knife/supermarket_list.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_list"
+
+class Chef
+ class Knife
+ class SupermarketList < Knife::CookbookSiteList
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket list (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_search.rb b/lib/chef/knife/supermarket_search.rb
new file mode 100644
index 0000000000..3206b0cb80
--- /dev/null
+++ b/lib/chef/knife/supermarket_search.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_search"
+
+class Chef
+ class Knife
+ class SupermarketSearch < Knife::CookbookSiteSearch
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket search QUERY (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_share.rb b/lib/chef/knife/supermarket_share.rb
new file mode 100644
index 0000000000..3109b9e794
--- /dev/null
+++ b/lib/chef/knife/supermarket_share.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_share"
+
+class Chef
+ class Knife
+ class SupermarketShare < Knife::CookbookSiteShare
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket share COOKBOOK [CATEGORY] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_show.rb b/lib/chef/knife/supermarket_show.rb
new file mode 100644
index 0000000000..2ad122143f
--- /dev/null
+++ b/lib/chef/knife/supermarket_show.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_show"
+
+class Chef
+ class Knife
+ class SupermarketShow < Knife::CookbookSiteShow
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket show COOKBOOK [VERSION] (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/knife/supermarket_unshare.rb b/lib/chef/knife/supermarket_unshare.rb
new file mode 100644
index 0000000000..fd48e172ce
--- /dev/null
+++ b/lib/chef/knife/supermarket_unshare.rb
@@ -0,0 +1,33 @@
+#
+# Author:: Christopher Webber (<cwebber@chef.io>)
+# Copyright:: Copyright (c) 2014 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/knife"
+require "chef/knife/cookbook_site_unshare"
+
+class Chef
+ class Knife
+ class SupermarketUnshare < Knife::CookbookSiteUnshare
+ # Handle the subclassing (knife doesn't do this :()
+ dependency_loaders.concat(superclass.dependency_loaders)
+ options.merge!(superclass.options)
+
+ banner "knife supermarket unshare COOKBOOK (options)"
+ category "supermarket"
+ end
+ end
+end
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 8d77becbf0..54faab6d3e 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -43,6 +43,8 @@ class Chef
def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key?
def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override
def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override!
+ def_delegators :attributes, :default_unless, :normal_unless, :override_unless, :set_unless
+ def_delegators :attributes, :read, :read!, :write, :write!, :unlink, :unlink!
attr_accessor :recipe_list, :run_state, :override_runlist
@@ -196,35 +198,18 @@ class Chef
# might be missing
def normal
attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
attributes.normal
end
- alias_method :set, :normal
-
- # Set a normal attribute of this node, auto-vivifying any mashes that are
- # missing, but if the final value already exists, don't set it
- def normal_unless
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = true
- attributes.normal
+ def set
+ Chef.log_deprecation("node.set is deprecated and will be removed in Chef 14, please use node.default/node.override (or node.normal only if you really need persistence)")
+ normal
end
- alias_method :set_unless, :normal_unless
-
# Set a default of this node, but auto-vivify any Mashes that might
# be missing
def default
attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
- attributes.default
- end
-
- # Set a default attribute of this node, auto-vivifying any mashes that are
- # missing, but if the final value already exists, don't set it
- def default_unless
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = true
attributes.default
end
@@ -232,15 +217,6 @@ class Chef
# might be missing
def override
attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
- attributes.override
- end
-
- # Set an override attribute of this node, auto-vivifying any mashes that
- # are missing, but if the final value already exists, don't set it
- def override_unless
- attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = true
attributes.override
end
@@ -262,7 +238,6 @@ class Chef
def automatic_attrs
attributes.top_level_breadcrumb = nil
- attributes.set_unless_value_present = false
attributes.automatic
end
@@ -290,8 +265,14 @@ class Chef
end
# Only works for attribute fetches, setting is no longer supported
- def method_missing(symbol, *args)
- attributes.send(symbol, *args)
+ # XXX: this should be deprecated
+ def method_missing(method, *args, &block)
+ attributes.public_send(method, *args, &block)
+ end
+
+ # Fix respond_to + method so that it works with method_missing delegation
+ def respond_to_missing?(method, include_private = false)
+ attributes.respond_to?(method, false)
end
# Returns true if this Node expects a given recipe, false if not.
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index ab97cf99bf..f5fe89251d 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -19,6 +19,7 @@
require "chef/node/immutable_collections"
require "chef/node/attribute_collections"
+require "chef/decorator/unchain"
require "chef/mixin/deep_merge"
require "chef/log"
@@ -132,6 +133,7 @@ class Chef
:take,
:take_while,
:to_a,
+ :to_h,
:to_hash,
:to_set,
:value?,
@@ -187,8 +189,6 @@ class Chef
attr_accessor :deep_merge_cache
def initialize(normal, default, override, automatic)
- @set_unless_present = false
-
@default = VividMash.new(self, default)
@env_default = VividMash.new(self, {})
@role_default = VividMash.new(self, {})
@@ -214,15 +214,13 @@ class Chef
# attribute you're interested in. For example, to debug where the value
# of `node[:network][:default_interface]` is coming from, use:
# debug_value(:network, :default_interface).
- # The return value is an Array of Arrays. The first element is
- # `["set_unless_enabled?", Boolean]`, which describes whether the
- # attribute collection is in "set_unless" mode. The rest of the Arrays
+ # The return value is an Array of Arrays. The Arrays
# are pairs of `["precedence_level", value]`, where precedence level is
# the component, such as role default, normal, etc. and value is the
# attribute value set at that precedence level. If there is no value at
# that precedence level, +value+ will be the symbol +:not_present+.
def debug_value(*args)
- components = COMPONENTS.map do |component|
+ COMPONENTS.map do |component|
ivar = instance_variable_get(component)
value = args.inject(ivar) do |so_far, key|
if so_far == :not_present
@@ -235,12 +233,6 @@ class Chef
end
[component.to_s.sub(/^@/, ""), value]
end
- [["set_unless_enabled?", @set_unless_present]] + components
- end
-
- # Enables or disables `||=`-like attribute setting. See, e.g., Node#set_unless
- def set_unless_value_present=(setting)
- @set_unless_present = setting
end
# Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate
@@ -321,94 +313,134 @@ class Chef
# clears attributes from all precedence levels
def rm(*args)
- reset(args[0])
- # just easier to compute our retval, rather than collect+merge sub-retvals
- ret = args.inject(merged_attributes) do |attr, arg|
- if attr.nil? || !attr.respond_to?(:[])
- nil
- else
- begin
- attr[arg]
- rescue TypeError
- raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)"
- end
- end
+ with_deep_merged_return_value(self, *args) do
+ rm_default(*args)
+ rm_normal(*args)
+ rm_override(*args)
end
- rm_default(*args)
- rm_normal(*args)
- rm_override(*args)
- ret
end
- # does <level>['foo']['bar'].delete('baz')
- def remove_from_precedence_level(level, *args, key)
- multimash = level.element(*args)
- multimash.nil? ? nil : multimash.delete(key)
- end
-
- private :remove_from_precedence_level
-
# clears attributes from all default precedence levels
#
- # equivalent to: force_default!['foo']['bar'].delete('baz')
+ # similar to: force_default!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_default(*args)
- reset(args[0])
- remove_from_precedence_level(force_default!(autovivify: false), *args)
+ with_deep_merged_return_value(combined_default, *args) do
+ default.unlink(*args)
+ role_default.unlink(*args)
+ env_default.unlink(*args)
+ force_default.unlink(*args)
+ end
end
# clears attributes from normal precedence
#
# equivalent to: normal!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_normal(*args)
- reset(args[0])
- remove_from_precedence_level(normal!(autovivify: false), *args)
+ normal.unlink(*args)
end
# clears attributes from all override precedence levels
#
# equivalent to: force_override!['foo']['bar'].delete('baz')
+ # - does not autovivify
+ # - does not trainwreck if interior keys do not exist
def rm_override(*args)
- reset(args[0])
- remove_from_precedence_level(force_override!(autovivify: false), *args)
+ with_deep_merged_return_value(combined_override, *args) do
+ override.unlink(*args)
+ role_override.unlink(*args)
+ env_override.unlink(*args)
+ force_override.unlink(*args)
+ end
+ end
+
+ def with_deep_merged_return_value(obj, *path, last)
+ hash = obj.read(*path)
+ return nil unless hash.is_a?(Hash)
+ ret = hash[last]
+ yield
+ ret
end
+ private :with_deep_merged_return_value
+
#
# Replacing attributes without merging
#
# sets default attributes without merging
- def default!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @default, [], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def default!(*args)
+ return Decorator::Unchain.new(self, :default!) unless args.length > 0
+ write(:default, *args)
end
# sets normal attributes without merging
- def normal!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @normal, [], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def normal!(*args)
+ return Decorator::Unchain.new(self, :normal!) unless args.length > 0
+ write(:normal, *args)
end
# sets override attributes without merging
- def override!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @override, [], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def override!(*args)
+ return Decorator::Unchain.new(self, :override!) unless args.length > 0
+ write(:override, *args)
end
# clears from all default precedence levels and then sets force_default
- def force_default!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts)
+ #
+ # - this API autovivifies (and cannot trainwreck)
+ def force_default!(*args)
+ return Decorator::Unchain.new(self, :force_default!) unless args.length > 0
+ value = args.pop
+ rm_default(*args)
+ write(:force_default, *args, value)
end
# clears from all override precedence levels and then sets force_override
- def force_override!(opts = {})
- # FIXME: do not flush whole cache
- reset
- MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts)
+ def force_override!(*args)
+ return Decorator::Unchain.new(self, :force_override!) unless args.length > 0
+ value = args.pop
+ rm_override(*args)
+ write(:force_override, *args, value)
+ end
+
+ # method-style access to attributes
+
+ def read(*path)
+ merged_attributes.read(*path)
+ end
+
+ def read!(*path)
+ merged_attributes.read!(*path)
+ end
+
+ def exist?(*path)
+ merged_attributes.exist?(*path)
+ end
+
+ def write(level, *args, &block)
+ self.send(level).write(*args, &block)
+ end
+
+ def write!(level, *args, &block)
+ self.send(level).write!(*args, &block)
+ end
+
+ def unlink(level, *path)
+ self.send(level).unlink(*path)
+ end
+
+ def unlink!(level, *path)
+ self.send(level).unlink!(*path)
end
#
@@ -420,9 +452,9 @@ class Chef
#
def merged_attributes(*path)
- # immutablize(
+ # immutablize(
merge_all(path)
- # )
+ # )
end
def combined_override(*path)
@@ -433,6 +465,27 @@ class Chef
immutablize(merge_defaults(path))
end
+ def normal_unless(*args)
+ return Decorator::Unchain.new(self, :normal_unless) unless args.length > 0
+ write(:normal, *args) if read(*args[0...-1]).nil?
+ end
+
+ def default_unless(*args)
+ return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
+ write(:default, *args) if read(*args[0...-1]).nil?
+ end
+
+ def override_unless(*args)
+ return Decorator::Unchain.new(self, :override_unless) unless args.length > 0
+ write(:override, *args) if read(*args[0...-1]).nil?
+ end
+
+ def set_unless(*args)
+ Chef.log_deprecation("node.set_unless is deprecated and will be removed in Chef 14, please use node.default_unless/node.override_unless (or node.normal_unless if you really need persistence)")
+ return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
+ write(:normal, *args) if read(*args[0...-1]).nil?
+ end
+
def [](key)
if deep_merge_cache.has_key?(key.to_s)
# return the cache of the deep merged values by top-level key
@@ -461,13 +514,17 @@ class Chef
alias :each_attribute :each
def method_missing(symbol, *args)
- if args.empty?
+ if symbol == :to_ary
+ merged_attributes.send(symbol, *args)
+ elsif args.empty?
+ Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
if key?(symbol)
self[symbol]
else
raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
end
elsif symbol.to_s =~ /=$/
+ Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")}
key_to_set = symbol.to_s[/^(.+)=$/, 1]
self[key_to_set] = (args.length == 1 ? args[0] : args)
else
@@ -485,10 +542,6 @@ class Chef
}.join(", ") << ">"
end
- def set_unless?
- @set_unless_present
- end
-
private
# Helper method for merge_all/merge_defaults/merge_overrides.
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index 68f3a69756..b739ea8490 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -16,15 +16,15 @@
# limitations under the License.
#
+require "chef/node/common_api"
+
class Chef
class Node
-
# == AttrArray
# AttrArray is identical to Array, except that it keeps a reference to the
# "root" (Chef::Node::Attribute) object, and will trigger a cache
# invalidation on that object when mutated.
class AttrArray < Array
-
MUTATOR_METHODS = [
:<<,
:[]=,
@@ -62,8 +62,9 @@ class Chef
# Node::Attribute object.
MUTATOR_METHODS.each do |mutator|
define_method(mutator) do |*args, &block|
+ ret = super(*args, &block)
root.reset_cache(root.top_level_breadcrumb)
- super(*args, &block)
+ ret
end
end
@@ -96,14 +97,12 @@ class Chef
# in the creation of a new VividMash for that key. (This only works when
# using the element reference method, `[]` -- other methods, such as
# #fetch, work as normal).
- # * It supports a set_unless flag (via the root Attribute object) which
- # allows `||=` style behavior (`||=` does not work with
- # auto-vivification). This is only implemented for #[]=; methods such as
- # #store work as normal.
# * attr_accessor style element set and get are supported via method_missing
class VividMash < Mash
attr_reader :root
+ include CommonAPI
+
# Methods that mutate a VividMash. Each of them is overridden so that it
# also invalidates the cached merged_attributes on the root Attribute
# object.
@@ -148,12 +147,9 @@ class Chef
def []=(key, value)
root.top_level_breadcrumb ||= key
- if set_unless? && key?(key) && !self[key].nil?
- self[key]
- else
- root.reset_cache(root.top_level_breadcrumb)
- super
- end
+ ret = super
+ root.reset_cache(root.top_level_breadcrumb)
+ ret
end
alias :attribute? :has_key?
@@ -176,10 +172,6 @@ class Chef
end
end
- def set_unless?
- @root.set_unless?
- end
-
def convert_key(key)
super
end
@@ -206,118 +198,5 @@ class Chef
end
end
-
- # == MultiMash
- # This is a Hash-like object that contains multiple VividMashes in it. Its
- # purpose is so that the user can descend into the mash and delete a subtree
- # from all of the Mash objects (used to delete all values in a subtree from
- # default, force_default, role_default and env_default at the same time). The
- # assignment operator strictly does assignment (does no merging) and works
- # by deleting the subtree and then assigning to the last mash which passed in
- # the initializer.
- #
- # A lot of the complexity of this class comes from the fact that at any key
- # value some or all of the mashes may walk off their ends and become nil or
- # true or something. The schema may change so that one precidence leve may
- # be 'true' object and another may be a VividMash. It is also possible that
- # one or many of them may transition from VividMashes to Hashes or Arrays.
- #
- # It also supports the case where you may be deleting a key using node.rm
- # in which case if intermediate keys all walk off into nil then you don't want
- # to be autovivifying keys as you go. On the other hand you may be using
- # node.force_default! in which case you'll wind up with a []= operator at the
- # end and you want autovivification, so we conditionally have to support either
- # operation.
- #
- # @todo: can we have an autovivify class that decorates a class that doesn't
- # autovivify or something so that the code is less awful?
- #
- class MultiMash
- attr_reader :root
- attr_reader :mashes
- attr_reader :opts
- attr_reader :primary_mash
-
- # Initialize with an array of mashes. For the delete return value to work
- # properly the mashes must come from the same attribute level (i.e. all
- # override or all default, but not a mix of both).
- def initialize(root, primary_mash, mashes, opts = {})
- @root = root
- @primary_mash = primary_mash
- @mashes = mashes
- @opts = opts
- @opts[:autovivify] = true if @opts[:autovivify].nil?
- end
-
- def [](key)
- # handle the secondary mashes
- new_mashes = []
- mashes.each do |mash|
- new_mash = safe_evalute_key(mash, key)
- # secondary mashes never autovivify so once they fall into nil, we just stop tracking them
- new_mashes.push(new_mash) unless new_mash.nil?
- end
-
- new_primary_mash = safe_evalute_key(primary_mash, key)
-
- if new_primary_mash.nil? && @opts[:autovivify]
- primary_mash[key] = VividMash.new(root)
- new_primary_mash = primary_mash[key]
- end
-
- MultiMash.new(root, new_primary_mash, new_mashes, opts)
- end
-
- def []=(key, value)
- if primary_mash.nil?
- # This theoretically should never happen since node#force_default! setter methods will autovivify and
- # node#rm methods do not end in #[]= operators.
- raise TypeError, "No autovivification was specified initially on a method chain ending in assignment"
- end
- ret = delete(key)
- primary_mash[key] = value
- ret
- end
-
- # mash.element('foo', 'bar') is the same as mash['foo']['bar']
- def element(key = nil, *subkeys)
- return self if key.nil?
- submash = self[key]
- subkeys.empty? ? submash : submash.element(*subkeys)
- end
-
- def delete(key)
- # the return value is a deep merge which is correct semantics when
- # merging between attributes on the same level (this would be incorrect
- # if passed both override and default attributes which would need hash_only
- # merging).
- ret = mashes.inject(Mash.new) do |merged, mash|
- Chef::Mixin::DeepMerge.merge(merged, mash)
- end
- ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash)
- mashes.each do |mash|
- mash.delete(key) if mash.respond_to?(:delete)
- end
- primary_mash.delete(key) if primary_mash.respond_to?(:delete)
- ret[key]
- end
-
- private
-
- def safe_evalute_key(mash, key)
- if mash.respond_to?(:[])
- if mash.respond_to?(:has_key?)
- if mash.has_key?(key)
- return mash[key] if mash[key].respond_to?(:[])
- end
- elsif !mash[key].nil?
- return mash[key] if mash[key].respond_to?(:[])
- end
- end
- return nil
- end
-
- end
-
end
end
diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb
new file mode 100644
index 0000000000..ce2c6b6878
--- /dev/null
+++ b/lib/chef/node/common_api.rb
@@ -0,0 +1,129 @@
+#--
+# 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.
+#
+
+class Chef
+ class Node
+ # shared API between VividMash and ImmutableMash, writer code can be
+ # 'shared' to keep it logically in this file by adding them to the
+ # block list in ImmutableMash.
+ module CommonAPI
+ # method-style access to attributes
+
+ def valid_container?(obj, key)
+ return obj.is_a?(Hash) || (obj.is_a?(Array) && key.is_a?(Fixnum))
+ end
+
+ private :valid_container?
+
+ # - autovivifying / autoreplacing writer
+ # - non-container-ey intermediate objects are replaced with hashes
+ def write(*args, &block)
+ root.top_level_breadcrumb = nil if respond_to?(:root)
+ value = block_given? ? yield : args.pop
+ last = args.pop
+ prev_memo = prev_key = nil
+ chain = args.inject(self) do |memo, key|
+ if !valid_container?(memo, key)
+ prev_memo[prev_key] = {}
+ memo = prev_memo[prev_key]
+ end
+ prev_memo = memo
+ prev_key = key
+ memo[key]
+ end
+ if !valid_container?(chain, last)
+ prev_memo[prev_key] = {}
+ chain = prev_memo[prev_key]
+ end
+ chain[last] = value
+ end
+
+ # this autovivifies, but can throw NoSuchAttribute when trying to access #[] on
+ # something that is not a container ("schema violation" issues).
+ #
+ def write!(*args, &block)
+ root.top_level_breadcrumb = nil if respond_to?(:root)
+ value = block_given? ? yield : args.pop
+ last = args.pop
+ obj = args.inject(self) do |memo, key|
+ raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(memo, key)
+ memo[key]
+ end
+ raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(obj, last)
+ obj[last] = value
+ end
+
+ # FIXME:(?) does anyone need a non-autovivifying writer for attributes that throws exceptions?
+
+ # return true or false based on if the attribute exists
+ def exist?(*path)
+ root.top_level_breadcrumb = nil if respond_to?(:root)
+ path.inject(self) do |memo, key|
+ return false unless valid_container?(memo, key)
+ if memo.is_a?(Hash)
+ if memo.key?(key)
+ memo[key]
+ else
+ return false
+ end
+ elsif memo.is_a?(Array)
+ if memo.length > key
+ memo[key]
+ else
+ return false
+ end
+ end
+ end
+ return true
+ end
+
+ # this is a safe non-autovivifying reader that returns nil if the attribute does not exist
+ def read(*path)
+ begin
+ read!(*path)
+ rescue Chef::Exceptions::NoSuchAttribute
+ nil
+ end
+ end
+
+ # non-autovivifying reader that throws an exception if the attribute does not exist
+ def read!(*path)
+ raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+ root.top_level_breadcrumb = nil if respond_to?(:root)
+ path.inject(self) do |memo, key|
+ memo[key]
+ end
+ end
+
+ # FIXME:(?) does anyone really like the autovivifying reader that we have and wants the same behavior? readers that write? ugh...
+
+ def unlink(*path, last)
+ root.top_level_breadcrumb = nil if respond_to?(:root)
+ hash = path.empty? ? self : read(*path)
+ return nil unless hash.is_a?(Hash) || hash.is_a?(Array)
+ root.top_level_breadcrumb ||= last
+ hash.delete(last)
+ end
+
+ def unlink!(*path)
+ raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+ unlink(*path)
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index b5fd86fa72..d4623ace2a 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -1,3 +1,21 @@
+#--
+# Copyright:: Copyright 2012-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/node/common_api"
class Chef
class Node
@@ -124,6 +142,7 @@ class Chef
class ImmutableMash < Mash
include Immutablize
+ include CommonAPI
alias :internal_set :[]=
private :internal_set
@@ -144,6 +163,10 @@ class Chef
:replace,
:select!,
:shift,
+ :write,
+ :write!,
+ :unlink,
+ :unlink!,
]
def initialize(mash_data)
@@ -167,13 +190,15 @@ class Chef
end
def method_missing(symbol, *args)
- if args.empty?
+ if symbol == :to_ary
+ super
+ elsif args.empty?
if key?(symbol)
self[symbol]
else
raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
end
- # This will raise a ImmutableAttributeModification error:
+ # This will raise a ImmutableAttributeModification error:
elsif symbol.to_s =~ /=$/
key_to_set = symbol.to_s[/^(.+)=$/, 1]
self[key_to_set] = (args.length == 1 ? args[0] : args)
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index 45ab4dd522..0589cb4c54 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -531,8 +531,6 @@ class Chef
end
end
- protected
-
#
# The options this Property will use for get/set behavior and validation.
#
@@ -583,6 +581,7 @@ class Chef
(options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false))
end
+ # @api private
def get_value(resource)
if instance_variable_name
resource.instance_variable_get(instance_variable_name)
@@ -591,6 +590,7 @@ class Chef
end
end
+ # @api private
def set_value(resource, value)
if instance_variable_name
resource.instance_variable_set(instance_variable_name, value)
@@ -599,6 +599,7 @@ class Chef
end
end
+ # @api private
def value_is_set?(resource)
if instance_variable_name
resource.instance_variable_defined?(instance_variable_name)
@@ -607,6 +608,7 @@ class Chef
end
end
+ # @api private
def reset_value(resource)
if instance_variable_name
if value_is_set?(resource)
@@ -617,6 +619,8 @@ class Chef
end
end
+ private
+
def exec_in_resource(resource, proc, *args)
if resource
if proc.arity > args.size
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 03b546c09d..7cfddba0cb 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -423,9 +423,9 @@ class Chef
module DeprecatedLWRPClass
def const_missing(class_name)
- if deprecated_constants[class_name.to_sym]
+ if Chef::Provider.deprecated_constants[class_name.to_sym]
Chef.log_deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed. Use Chef::ProviderResolver.new(node, resource, action) instead.")
- deprecated_constants[class_name.to_sym]
+ Chef::Provider.deprecated_constants[class_name.to_sym]
else
raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
end
@@ -438,13 +438,12 @@ class Chef
if Chef::Provider.const_defined?(class_name, false)
Chef::Log.warn "Chef::Provider::#{class_name} already exists! Cannot create deprecation class for #{provider_class}"
else
- deprecated_constants[class_name.to_sym] = provider_class
+ Chef::Provider.deprecated_constants[class_name.to_sym] = provider_class
end
end
- private
-
def deprecated_constants
+ raise "Deprecated constants should be called only on Chef::Provider" unless self == Chef::Provider
@deprecated_constants ||= {}
end
end
diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb
index bb294afd3f..0d857aaa79 100644
--- a/lib/chef/provider/batch.rb
+++ b/lib/chef/provider/batch.rb
@@ -29,7 +29,7 @@ class Chef
end
def command
- basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+ basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
interpreter_path = Chef::Util::PathHelper.join(basepath, interpreter)
diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb
index 36b67ab6a5..c7487cf42f 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -237,7 +237,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/file.rb b/lib/chef/provider/file.rb
index 7f85085eeb..bb0762ceb7 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -154,6 +154,7 @@ class Chef
do_contents_changes
do_acl_changes
do_selinux
+ do_resolv_conf_fixup
load_resource_attributes_from_file(@new_resource)
end
@@ -445,6 +446,13 @@ 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/package/aix.rb b/lib/chef/provider/package/aix.rb
index a1709c4af7..728f181055 100644
--- a/lib/chef/provider/package/aix.rb
+++ b/lib/chef/provider/package/aix.rb
@@ -55,7 +55,11 @@ class Chef
ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}")
ret.stdout.each_line do |line|
case line
- when /#{@new_resource.package_name}:/
+ when /:#{@new_resource.package_name}:/
+ fields = line.split(":")
+ @new_resource.version(fields[2])
+ when /^#{@new_resource.package_name}:/
+ Chef::Log.warn("You are installing a bff package by product name. For idempotent installs, please install individual filesets")
fields = line.split(":")
@new_resource.version(fields[2])
end
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index 2120b9aa48..8043c01693 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -127,7 +127,7 @@ class Chef
end
def pkg_path
- ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/"
+ ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node["kernel"]["name"]}/#{node["kernel"]["release"]}/packages/#{node["kernel"]["machine"]}/"
end
end
diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb
index eb5a87099f..0aeec951b1 100644
--- a/lib/chef/provider/package/rubygems.rb
+++ b/lib/chef/provider/package/rubygems.rb
@@ -431,17 +431,23 @@ class Chef
end
def current_version
- #raise 'todo'
+ # rubygems 2.6.3 ensures that gem lists are sorted newest first
+ pos = if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.6.3")
+ :first
+ else
+ :last
+ end
+
# If one or more matching versions are installed, the newest of them
# is the current version
if !matching_installed_versions.empty?
- gemspec = matching_installed_versions.last
+ gemspec = matching_installed_versions.send(pos)
logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}" }
gemspec
# If no version matching the requirements exists, the latest installed
# version is the current version.
elsif !all_installed_versions.empty?
- gemspec = all_installed_versions.last
+ gemspec = all_installed_versions.send(pos)
logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
gemspec
else
diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb
index 70c9879845..211845c073 100644
--- a/lib/chef/provider/package/windows/exe.rb
+++ b/lib/chef/provider/package/windows/exe.rb
@@ -78,12 +78,9 @@ class Chef
private
def uninstall_command(uninstall_string)
- uninstall_string.delete!('"')
+ uninstall_string = "\"#{uninstall_string}\"" if ::File.exist?(uninstall_string)
uninstall_string = [
- %q{/d"},
- ::File.dirname(uninstall_string),
- %q{" },
- ::File.basename(uninstall_string),
+ uninstall_string,
expand_options(new_resource.options),
" ",
unattended_flags,
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index 5ee1dbea8e..e20a7332f7 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -38,15 +38,15 @@ class Chef
status = shell_out_with_timeout!("zypper --non-interactive info #{package_name}")
status.stdout.each_line do |line|
case line
- when /^Version: (.+)$/
- candidate_version = $1
- Chef::Log.debug("#{new_resource} version #{$1}")
- when /^Installed: Yes$/
+ when /^Version *: (.+) *$/
+ candidate_version = $1.strip
+ Chef::Log.debug("#{new_resource} version #{candidate_version}")
+ when /^Installed *: Yes *$/
is_installed = true
Chef::Log.debug("#{new_resource} is installed")
- when /^Status: out-of-date \(version (.+) installed\)$/
- current_version = $1
- Chef::Log.debug("#{new_resource} out of date version #{$1}")
+ when /^Status *: out-of-date \(version (.+) installed\) *$/
+ current_version = $1.strip
+ Chef::Log.debug("#{new_resource} out of date version #{current_version}")
end
end
current_version = candidate_version if is_installed
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index 6365f6a171..ab85ec35ac 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -36,7 +36,7 @@ class Chef
end
def command
- basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+ basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
# Powershell.exe is always in "v1.0" folder (for backwards compatibility)
interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter)
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb
index e3bc579107..15b71c43bd 100644
--- a/lib/chef/provider/remote_directory.rb
+++ b/lib/chef/provider/remote_directory.rb
@@ -209,6 +209,8 @@ class Chef
def cookbook_file_resource(target_path, relative_source_path)
res = Chef::Resource::CookbookFile.new(target_path, run_context)
res.cookbook_name = resource_cookbook
+ # Set the sensitivity level
+ res.sensitive(new_resource.sensitive)
res.source(::File.join(source, relative_source_path))
if Chef::Platform.windows? && files_rights
files_rights.each_pair do |permission, *args|
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 2633187690..479eb0a7e2 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -260,6 +260,18 @@ class Chef
end
#
+ # Token class to hold an unresolved subscribes call with an associated
+ # run context.
+ #
+ # @api private
+ # @see Resource#subscribes
+ class UnresolvedSubscribes < self
+ # The full key ise given as the name in {Resource#subscribes}
+ alias_method :to_s, :name
+ alias_method :declared_key, :name
+ end
+
+ #
# Subscribes to updates from other resources, causing a particular action to
# run on *this* resource when the other resource is updated.
#
@@ -326,7 +338,7 @@ class Chef
resources = [resources].flatten
resources.each do |resource|
if resource.is_a?(String)
- resource = Chef::Resource.new(resource, run_context)
+ resource = UnresolvedSubscribes.new(resource, run_context)
end
if resource.run_context.nil?
resource.run_context = run_context
@@ -1530,23 +1542,6 @@ class Chef
end
# @api private
- def self.register_deprecated_lwrp_class(resource_class, class_name)
- if Chef::Resource.const_defined?(class_name, false)
- Chef::Log.warn "#{class_name} already exists! Deprecation class overwrites #{resource_class}"
- Chef::Resource.send(:remove_const, class_name)
- end
-
- if !Chef::Config[:treat_deprecation_warnings_as_errors]
- Chef::Resource.const_set(class_name, resource_class)
- deprecated_constants[class_name.to_sym] = resource_class
- end
- end
-
- def self.deprecated_constants
- @deprecated_constants ||= {}
- end
-
- # @api private
def lookup_provider_constant(name, action = :nothing)
begin
self.class.provider_base.const_get(convert_to_class_name(name.to_s))
@@ -1559,6 +1554,27 @@ class Chef
end
end
+ module DeprecatedLWRPClass
+
+ # @api private
+ def register_deprecated_lwrp_class(resource_class, class_name)
+ if Chef::Resource.const_defined?(class_name, false)
+ Chef::Log.warn "#{class_name} already exists! Deprecation class overwrites #{resource_class}"
+ Chef::Resource.send(:remove_const, class_name)
+ end
+
+ if !Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Resource.const_set(class_name, resource_class)
+ Chef::Resource.deprecated_constants[class_name.to_sym] = resource_class
+ end
+ end
+
+ def deprecated_constants
+ raise "Deprecated constants should be called only on Chef::Resource" unless self == Chef::Resource
+ @deprecated_constants ||= {}
+ end
+ end
+
private
def self.remove_canonical_dsl
@@ -1569,6 +1585,7 @@ class Chef
end
end
end
+ extend DeprecatedLWRPClass
end
end
diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb
index 138e401d5c..1641fe60f2 100644
--- a/lib/chef/resource_builder.rb
+++ b/lib/chef/resource_builder.rb
@@ -104,7 +104,11 @@ class Chef
end
def is_trivial_resource?(resource)
- identicalish_resources?(resource_class.new(name, run_context), resource)
+ trivial_resource = resource_class.new(name, run_context)
+ # force un-lazy the name property on the created trivial resource
+ name_property = resource_class.properties.find { |sym, p| p.name_property? }
+ trivial_resource.send(name_property[0]) unless name_property.nil?
+ identicalish_resources?(trivial_resource, resource)
end
# this is an equality test specific to checking for 3694 cloning warnings
@@ -124,9 +128,10 @@ class Chef
end
def emit_cloned_resource_warning
- Chef::Log.warn("Cloning resource attributes for #{resource} from prior resource (CHEF-3694)")
- Chef::Log.warn("Previous #{prior_resource}: #{prior_resource.source_line}") if prior_resource.source_line
- Chef::Log.warn("Current #{resource}: #{resource.source_line}") if resource.source_line
+ message = "Cloning resource attributes for #{resource} from prior resource (CHEF-3694)"
+ message << "\nPrevious #{prior_resource}: #{prior_resource.source_line}" if prior_resource.source_line
+ message << "\nCurrent #{resource}: #{resource.source_line}" if resource.source_line
+ Chef.log_deprecation(message)
end
def emit_harmless_cloning_debug
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index 29c936a932..7ef476c44b 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -194,12 +194,10 @@ class Chef
# @param [Chef::Resource::Notification] The notification to add.
#
def notifies_before(notification)
- nr = notification.notifying_resource
- if nr.instance_of?(Chef::Resource)
- before_notification_collection[nr.name] << notification
- else
- before_notification_collection[nr.declared_key] << notification
- end
+ # Note for the future, notification.notifying_resource may be an instance
+ # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+ # with a string value.
+ before_notification_collection[notification.notifying_resource.declared_key] << notification
end
#
@@ -208,12 +206,10 @@ class Chef
# @param [Chef::Resource::Notification] The notification to add.
#
def notifies_immediately(notification)
- nr = notification.notifying_resource
- if nr.instance_of?(Chef::Resource)
- immediate_notification_collection[nr.name] << notification
- else
- immediate_notification_collection[nr.declared_key] << notification
- end
+ # Note for the future, notification.notifying_resource may be an instance
+ # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+ # with a string value.
+ immediate_notification_collection[notification.notifying_resource.declared_key] << notification
end
#
@@ -222,12 +218,10 @@ class Chef
# @param [Chef::Resource::Notification] The notification to add.
#
def notifies_delayed(notification)
- nr = notification.notifying_resource
- if nr.instance_of?(Chef::Resource)
- delayed_notification_collection[nr.name] << notification
- else
- delayed_notification_collection[nr.declared_key] << notification
- end
+ # Note for the future, notification.notifying_resource may be an instance
+ # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+ # with a string value.
+ delayed_notification_collection[notification.notifying_resource.declared_key] << notification
end
#
@@ -245,50 +239,29 @@ class Chef
#
# Get the list of before notifications sent by the given resource.
#
- # TODO seriously, this is actually wrong. resource.name is not unique,
- # you need the type as well.
- #
# @return [Array[Notification]]
#
def before_notifications(resource)
- if resource.instance_of?(Chef::Resource)
- return before_notification_collection[resource.name]
- else
- return before_notification_collection[resource.declared_key]
- end
+ return before_notification_collection[resource.declared_key]
end
#
# Get the list of immediate notifications sent by the given resource.
#
- # TODO seriously, this is actually wrong. resource.name is not unique,
- # you need the type as well.
- #
# @return [Array[Notification]]
#
def immediate_notifications(resource)
- if resource.instance_of?(Chef::Resource)
- return immediate_notification_collection[resource.name]
- else
- return immediate_notification_collection[resource.declared_key]
- end
+ return immediate_notification_collection[resource.declared_key]
end
#
# Get the list of delayed (end of run) notifications sent by the given
# resource.
#
- # TODO seriously, this is actually wrong. resource.name is not unique,
- # you need the type as well.
- #
# @return [Array[Notification]]
#
def delayed_notifications(resource)
- if resource.instance_of?(Chef::Resource)
- return delayed_notification_collection[resource.name]
- else
- return delayed_notification_collection[resource.declared_key]
- end
+ return delayed_notification_collection[resource.declared_key]
end
#
diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb
index aad5c49d00..26683cc25d 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -148,7 +148,7 @@ module Shell
end
def self.greeting
- " #{Etc.getlogin}@#{Shell.session.node.fqdn}"
+ " #{Etc.getlogin}@#{Shell.session.node["fqdn"]}"
rescue NameError, ArgumentError
""
end
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index 7c38be31b3..cc29ef1740 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.12.2"
+ VERSION = "12.13.21"
end
#