diff options
author | Thom May <thom@may.lt> | 2018-06-18 17:50:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-18 17:50:07 +0100 |
commit | d5907bf00781fd82adac6182cc398f2dc0975900 (patch) | |
tree | 61108d0403077f2c54358c8fde0040206c04a478 | |
parent | e07f9e716008251f82ea3e5f255a250c28109363 (diff) | |
parent | 7a12fa4b66d18b79233c2c29de5cead0ea0c1af9 (diff) | |
download | chef-d5907bf00781fd82adac6182cc398f2dc0975900.tar.gz |
Merge pull request #7375 from coderanger/silence-deprecations
Silence deprecation warnings
-rw-r--r-- | RELEASE_NOTES.md | 49 | ||||
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 5 | ||||
-rw-r--r-- | lib/chef/chef_class.rb | 24 | ||||
-rw-r--r-- | lib/chef/deprecated.rb | 282 | ||||
-rw-r--r-- | lib/chef/formatters/base.rb | 15 | ||||
-rw-r--r-- | lib/chef/formatters/doc.rb | 16 | ||||
-rw-r--r-- | lib/chef/log.rb | 18 | ||||
-rw-r--r-- | lib/chef/node_map.rb | 19 | ||||
-rw-r--r-- | spec/integration/knife/common_options_spec.rb | 2 | ||||
-rw-r--r-- | spec/unit/chef_class_spec.rb | 120 | ||||
-rw-r--r-- | spec/unit/deprecated_spec.rb | 30 | ||||
-rw-r--r-- | spec/unit/node_map_spec.rb | 14 |
12 files changed, 357 insertions, 237 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d9747c80df..85181821c6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,54 @@ This file holds "in progress" release notes for the current release under development and is intended for consumption by the Chef Documentation team. Please see <https://docs.chef.io/release_notes.html> for the official Chef release notes. +## Silencing deprecation warnings + +While deprecation warnings have been great for the Chef community to ensure +cookbooks are kept up-to-date and to prepare for major version upgrades, sometimes +you just can't fix a deprecation right now. This is often compounded by the +recommendation to enable `treat_deprecation_warnings_as_errors` mode in your +Test Kitchen integration tests, which doesn't understand the difference between +deprecations from community cookbooks and those from your own code. + +Two new options are provided for silencing deprecation warnings: `silence_deprecation_warnings` +and inline `chef:silence_deprecation` comments. + +The `silence_deprecation_warnings` configuration value can be set in your +`client.rb` or `solo.rb` config file, either to `true` to silence all deprecation +warnings or to an array of deprecations to silence. You can specify which to +silence either by the deprecation key name (e.g. `"internal_api"`), the numeric +deprecation ID (e.g. `25` or `"CHEF-25"`), or by specifying the filename and +line number where the deprecation is being raised from (e.g. `"default.rb:67"`). + +An example of setting the `silence_deprecation_warnings` option in your `client.rb` +or `solo.rb`: + +```ruby +silence_deprecation_warnings %w{deploy_resource chef-23 recipes/install.rb:22} +``` + +or in your `kitchen.yml`: + +```yaml +provisioner: + name: chef_solo + solo_rb: + treat_deprecation_warnings_as_errors: true + silence_deprecation_warnings: + - deploy_resource + - chef-23 + - recipes/install.rb:22 +``` + +You can also silence deprecations using a comment on the line that is raising +the warning: + +```ruby +erl_call 'something' do # chef:silence_deprecation +``` + +We advise caution in the use of this feature, as excessive or prolonged silencing +can lead to difficulty upgrading when the next major release of Chef comes out. + # Chef Client Release Notes 14.2: ## `ssh-agent` support for user keys diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 82f25f4139..ed8fab9ee4 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -715,6 +715,11 @@ module ChefConfig ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS") end + # Which deprecations warnings to silence. Can be set to `true` to silence + # all warnings, or an array of strings like either `"deprecation_type"` or + # `"filename.rb:lineno"`. + default :silence_deprecation_warnings, [] + # Whether the resource count should be updated for log resource # on running chef-client default :count_log_resource_updates, true diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb index bfea51609a..95b03f1c5c 100644 --- a/lib/chef/chef_class.rb +++ b/lib/chef/chef_class.rb @@ -200,13 +200,14 @@ class Chef # # Emit a deprecation message. # - # @param [Symbol] type The message to send. This should refer to a class + # @param type [Symbol] The message to send. This should refer to a class # defined in Chef::Deprecated - # @param message An explicit message to display, rather than the generic one - # associated with the deprecation. - # @param location The location. Defaults to the caller who called you (since - # generally the person who triggered the check is the one that needs to be - # fixed). + # @param message [String, nil] An explicit message to display, rather than + # the generic one associated with the deprecation. + # @param location [String, nil] The location. Defaults to the caller who + # called you (since generally the person who triggered the check is the one + # that needs to be fixed). + # @return [void] # # @example # Chef.deprecated(:my_deprecation, message: "This is deprecated!") @@ -220,11 +221,18 @@ class Chef # run. If we are not yet in a run, print to `Chef::Log`. if run_context && run_context.events run_context.events.deprecation(deprecation, location) - else - Chef::Log.deprecation(deprecation, location) + elsif !deprecation.silenced? + Chef::Log.deprecation(deprecation.to_s) end end + # Log a generic deprecation warning that doesn't have a specific class in + # Chef::Deprecated. + # + # This should generally not be used, as the user will not be given a link + # to get more infomration on fixing the deprecation warning. + # + # @see #deprecated def log_deprecation(message, location = nil) location ||= Chef::Log.caller_location Chef.deprecated(:generic, message, location) diff --git a/lib/chef/deprecated.rb b/lib/chef/deprecated.rb index 904578ff0b..d9a8df7b83 100644 --- a/lib/chef/deprecated.rb +++ b/lib/chef/deprecated.rb @@ -24,276 +24,196 @@ class Chef class << self include Chef::Mixin::ConvertToClassName - def create(type, message = nil, location = nil) - Chef::Deprecated.const_get(convert_to_class_name(type.to_s)).send(:new, message, location) + def create(type, message, location) + Chef::Deprecated.const_get(convert_to_class_name(type.to_s)).new(message, location) end end class Base BASE_URL = "https://docs.chef.io/deprecations_" - attr_accessor :message, :location + attr_reader :message, :location def initialize(msg = nil, location = nil) - @message = msg if msg - @location = location if location + @message = msg + @location = location end def link "Please see #{url} for further details and information on how to correct this problem." end + # Render the URL for the deprecation documentation page. + # + # @return [String] def url - "#{BASE_URL}#{target}" - end - - # We know that the only time this gets called is by Chef::Log.deprecation, - # so special case - def <<(location) - @location = location - end - - def inspect - "#{message} (CHEF-#{id})#{location}.\n#{link}" - end - - def id - raise NotImplementedError, "subclasses of Chef::Deprecated::Base should define #id with a unique number" - end - - def target - raise NotImplementedError, "subclasses of Chef::Deprecated::Base should define #target" + "#{BASE_URL}#{self.class.doc_page}" + end + + # Render the user-visible message for this deprecation. + # + # @return [String] + def to_s + "Deprecation CHEF-#{self.class.deprecation_id} from #{location}\n\n #{message}\n\n#{link}" + end + + # Check if this deprecation has been silenced. + # + # @return [Boolean] + def silenced? + # Check if all warnings have been silenced. + return true if Chef::Config[:silence_deprecation_warnings] == true + # Check if this warning has been silenced by the config. + return true if Chef::Config[:silence_deprecation_warnings].any? do |silence_spec| + # Just in case someone uses a symbol in the config by mistake. + silence_spec = silence_spec.to_s + # Check for a silence by deprecation name, or by location. + self.class.deprecation_key == silence_spec || self.class.deprecation_id.to_s == silence_spec || "chef-#{self.class.deprecation_id}" == silence_spec.downcase || location.include?(silence_spec) + end + # check if this warning has been silenced by inline comment. + return true if location =~ /^(.*?):(\d+):in/ && begin + # Don't buffer the whole file in memory, so read it one line at a time. + line_no = $2.to_i + location_file = ::File.open($1) + (line_no - 1).times { location_file.readline } # Read all the lines we don't care about. + relevant_line = location_file.readline + relevant_line.match?(/#.*chef:silence_deprecation($|[^:]|:#{self.class.deprecation_key})/) + end + false + end + + class << self + attr_reader :deprecation_id, :doc_page + + # Return the deprecation key as would be used with {Chef::Deprecated.create}. + # + # @return [String] + def deprecation_key + Chef::Mixin::ConvertToClassName.convert_to_snake_case(name, "Chef::Deprecated") + end + + # Set the ID and documentation page path for this deprecation. + # + # Used in subclasses to set the data for each type of deprecation. + # + # @example + # class MyDeprecation < Base + # target 123, "my_deprecation.html" + # end + # @param id [Integer] Deprecation ID number. This must be unique among + # all deprecations. + # @param page [String, nil] Optional documentation page path. If not + # specified, the deprecation key is used. + # @return [void] + def target(id, page = nil) + @deprecation_id = id + @doc_page = page || "#{deprecation_key}.html" + end end end class InternalApi < Base - def id - 0 - end - - def target - "internal_api.html" - end + target 0 end class JsonAutoInflate < Base - def id - 1 - end - - def target - "json_auto_inflate.html" - end + target 1 end class ExitCode < Base - def id - 2 - end - - def target - "exit_code.html" - end + target 2 end # id 3 has been deleted class Attributes < Base - def id - 4 - end - - def target - "attributes.html" - end + target 4 end class CustomResource < Base - def id - 5 - end - - def target - "custom_resource_cleanups.html" - end + target 5, "custom_resource_cleanups.html" end class EasyInstall < Base - def id - 6 - end - - def target - "easy_install.html" - end + target 6 end class VerifyFile < Base - def id - 7 - end - - def target - "verify_file.html" - end + target 7 end class SupportsProperty < Base - def id - 8 - end - - def target - "supports_property.html" - end + target 8 end class ChefRest < Base - def id - 9 - end - - def target - "chef_rest.html" - end + target 9 end class DnfPackageAllowDowngrade < Base - def id - 10 - end - - def target - "dnf_package_allow_downgrade.html" - end + target 10 end class PropertyNameCollision < Base - def id - 11 - end - - def target - "property_name_collision.html" - end + target 11 end class LaunchdHashProperty < Base - def id - 12 - end - - def target - "launchd_hash_property.html" - end + target 12 end class ChefPlatformMethods < Base - def id - 13 - end - - def target - "chef_platform_methods.html" - end + target 13 end class RunCommand < Base - def id - 14 - end - - def target - "run_command.html" - end + target 14 end class PackageMisc < Base - def id - 15 - end - - def target - "package_misc.html" - end + target 15 end class MultiresourceMatch < Base - def id - 16 - end - - def target - "multiresource_match.html" - end + target 16 end class UseInlineResources < Base - def id - 17 - end - - def target - "use_inline_resources.html" - end + target 17 end class LocalListen < Base - def id - 18 - end - - def target - "local_listen.html" - end + target 18 end class NamespaceCollisions < Base - def id - 19 - end - - def target - "namespace_collisions.html" - end + target 19 end class DeployResource < Base - def id - 21 - end - - def target - "deploy_resource.html" - end + target 21 end class ErlResource < Base - def id - 22 - end - - def target - "erl_resource.html" - end + target 22 end class FreebsdPkgProvider < Base - def id - 23 - end + target 23 + end - def target - "freebsd_pkg_provider.html" - end + class MapCollision < Base + target 25 end # id 3694 was deleted # Returned when using the deprecated option on a property class Property < Base - def inspect - "#{message}\n#{location}" + target 24 + + def to_s + "Deprecated resource property used from #{location}\n\n #{message}\n\nPlease consult the resource documentation for more information." end end @@ -302,8 +222,8 @@ class Chef "https://docs.chef.io/chef_deprecations_client.html" end - def inspect - "#{message}\nThis is a generic error message and should be updated to have a proper deprecation class. #{location}\nPlease see #{url} for an overview of Chef deprecations." + def to_s + "Deprecation from #{location}\n\n #{message}\n\n#{link}" end end diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb index 0b27b55048..997577aa7b 100644 --- a/lib/chef/formatters/base.rb +++ b/lib/chef/formatters/base.rb @@ -212,14 +212,13 @@ class Chef file_load_failed(path, exception) end - def deprecation(message, location = caller(2..2)[0]) - out = if is_structured_deprecation?(message) - message.inspect - else - "#{message} at #{location}" - end - - Chef::Log.deprecation(out) + # Log a deprecation warning object. + # + # @param deprecation [Chef::Deprecated::Base] Deprecation object to log. + # In previous versions, this could be a string. Don't do that anymore. + # @param location [Object] Unused, present only for compatbility. + def deprecation(deprecation, _location = nil) + Chef::Log.deprecation(deprecation.to_s) unless deprecation.silenced? end def is_structured_deprecation?(deprecation) diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb index 0c51cc2cfb..d47ab73a30 100644 --- a/lib/chef/formatters/doc.rb +++ b/lib/chef/formatters/doc.rb @@ -414,19 +414,15 @@ class Chef end end - def deprecation(message, location = caller(2..2)[0]) + # (see Base#deprecation) + def deprecation(deprecation, _location = nil) if Chef::Config[:treat_deprecation_warnings_as_errors] super + elsif !deprecation.silenced? + # Save non-silenced deprecations to the screen until the end. + deprecations[deprecation.message] ||= { url: deprecation.url, locations: Set.new } + deprecations[deprecation.message][:locations] << deprecation.location end - - # Save deprecations to the screen until the end - if is_structured_deprecation?(message) - url = message.url - message = message.message - end - - deprecations[message] ||= { url: url, locations: Set.new } - deprecations[message][:locations] << location end def indent diff --git a/lib/chef/log.rb b/lib/chef/log.rb index 10c9f0f20d..3f77158579 100644 --- a/lib/chef/log.rb +++ b/lib/chef/log.rb @@ -46,19 +46,21 @@ class Chef # def self.caller_location # Pick the first caller that is *not* part of the Chef gem, that's the - # thing the user wrote. + # thing the user wrote. Or failing that, the most recent caller. chef_gem_path = File.expand_path("../..", __FILE__) - caller(0..20).find { |c| !c.start_with?(chef_gem_path) } + caller(0..20).find { |c| !c.start_with?(chef_gem_path) } || caller(0..1)[0] end - def self.deprecation(msg = nil, location = caller(2..2)[0], &block) - if msg - msg << " at #{Array(location).join("\n")}" - msg = msg.join("") if msg.respond_to?(:join) - end + # Log a deprecation warning. + # + # If the treat_deprecation_warnings_as_errors config option is set, this + # will raise an exception instead. + # + # @param msg [String] Deprecation message to display. + def self.deprecation(msg, &block) if Chef::Config[:treat_deprecation_warnings_as_errors] error(msg, &block) - raise Chef::Exceptions::DeprecatedFeatureError.new(msg.inspect) + raise Chef::Exceptions::DeprecatedFeatureError.new(msg) else warn(msg, &block) end diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb index 634786af93..c0066bfce5 100644 --- a/lib/chef/node_map.rb +++ b/lib/chef/node_map.rb @@ -37,6 +37,21 @@ # class Chef class NodeMap + COLLISION_WARNING_14 = <<-EOH.gsub(/\s+/, " ").strip +%{type_caps} %{key} has been loaded from a cookbook. The %{type} %{key} is now +included in Chef and will take precedence over the existing cookbook %{type} in the +next major release of Chef (15.0, April 2019). You may be able to remove this cookbook dependency from +your runlist if you do not use other recipes/resources/libraries from the cookbook. +Alternatively there may be a newer version of this cookbook without the %{key} %{type}. +EOH + + COLLISION_WARNING_15 = <<-EOH.gsub(/\s+/, " ").strip +%{type_caps} %{key} attempted to load from a cookbook. The %{type} %{key} is now +included in Chef and takes precedence over the existing cookbook %{type} +which will be ignored. You may be able to remove this cookbook dependency from +your runlist if you do not use other recipes/resources/libraries from the cookbook. +Alternatively there may be a newer version of this cookbook without the %{key} %{type}. +EOH # # Set a key/value pair on the map with a filter. The filter must be true @@ -85,9 +100,9 @@ class Chef klass.superclass.to_s end # For now, only log the warning. - Chef.log_deprecation("Trying to register #{type_of_thing} #{key} on top of existing Chef core #{type_of_thing}. Check if a new version of the cookbook is available.") + Chef.deprecated(:map_collision, COLLISION_WARNING_14 % { type: type_of_thing, key: key, type_caps: type_of_thing.capitalize }) # In 15.0, uncomment this and remove the log above. - # Chef.log_deprecation("Rejecting attempt to register #{type_of_thing} #{key} on top of existing Chef core #{type_of_thing}. Check if a new version of the cookbook is available.") + # Chef.deprecated(:map_collision, COLLISION_WARNING_15 % {type: type_of_thing, key: key, type_caps: type_of_thing.capitalize})) # return end diff --git a/spec/integration/knife/common_options_spec.rb b/spec/integration/knife/common_options_spec.rb index 6b6b83aafe..da1409456b 100644 --- a/spec/integration/knife/common_options_spec.rb +++ b/spec/integration/knife/common_options_spec.rb @@ -28,7 +28,7 @@ describe "knife common options", :workstation do Chef::Config.treat_deprecation_warnings_as_errors(false) end - let(:local_listen_warning) { /\Awarn:.*local.*listen.*$/i } + let(:local_listen_warning) { /\Awarn:.*local.*listen.*$/im } when_the_repository "has a node" do before { file "nodes/x.json", {} } diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb index 21987c01ab..2f370388fa 100644 --- a/spec/unit/chef_class_spec.rb +++ b/spec/unit/chef_class_spec.rb @@ -107,4 +107,124 @@ describe "Chef class" do end.to raise_error(Chef::Exceptions::InvalidEventType) end end + + describe "Deprecation system" do + context "with treat_deprecation_warnings_as_errors false" do + before { Chef::Config[:treat_deprecation_warnings_as_errors] = false } + + it "displays a simple deprecation warning" do + expect(Chef::Log).to receive(:warn).with(%r{spec/unit/chef_class_spec\.rb.*?I'm a little teapot.*?Please see}m) + Chef.deprecated(:generic, "I'm a little teapot.") + end + + it "allows silencing all warnings" do + Chef::Config[:silence_deprecation_warnings] = true + expect(Chef::Log).to_not receive(:warn) + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:internal_api, "Short and stout.") + Chef.deprecated(:generic, "This is my handle.") + end + + it "allows silencing specific types" do + Chef::Config[:silence_deprecation_warnings] = [:internal_api] + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:internal_api, "Short and stout.") + Chef.deprecated(:generic, "This is my handle.") + end + + it "allows silencing specific IDs" do + Chef::Config[:silence_deprecation_warnings] = [0] + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:internal_api, "Short and stout.") + Chef.deprecated(:generic, "This is my handle.") + end + + it "allows silencing specific IDs using the CHEF- syntax" do + Chef::Config[:silence_deprecation_warnings] = ["CHEF-0"] + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:internal_api, "Short and stout.") + Chef.deprecated(:generic, "This is my handle.") + end + + it "allows silencing specific IDs using the chef- syntax" do + Chef::Config[:silence_deprecation_warnings] = ["chef-0"] + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:internal_api, "Short and stout.") + Chef.deprecated(:generic, "This is my handle.") + end + + it "allows silencing specific lines" do + Chef::Config[:silence_deprecation_warnings] = ["chef_class_spec.rb:#{__LINE__ + 4}"] + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:generic, "Short and stout.") + Chef.deprecated(:internal_api, "This is my handle.") + end + + it "allows silencing all via inline comments" do + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:generic, "Short and stout.") # chef:silence_deprecation + Chef.deprecated(:internal_api, "This is my handle.") + end + + it "allows silencing specific types via inline comments" do + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:generic, "Short and stout.") # chef:silence_deprecation:generic + Chef.deprecated(:internal_api, "This is my handle.") + end + + it "does not silence via inline comments when the types don't match" do + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/Short and stout/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:internal_api, "Short and stout.") # chef:silence_deprecation:generic + Chef.deprecated(:internal_api, "This is my handle.") + end + + it "allows silencing all via inline comments with other stuff in the comment" do + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my handle/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:generic, "Short and stout.") # rubocop:something chef:silence_deprecation other stuff + Chef.deprecated(:internal_api, "This is my handle.") + end + + it "handles multiple silence configurations at the same time" do + Chef::Config[:silence_deprecation_warnings] = ["exit_code", "chef_class_spec.rb:#{__LINE__ + 6}"] + expect(Chef::Log).to receive(:warn).with(/I'm a little teapot/).once + expect(Chef::Log).to receive(:warn).with(/This is my spout/).once + expect(Chef::Log).to receive(:warn).with(/Hear me shout/).once + Chef.deprecated(:generic, "I'm a little teapot.") + Chef.deprecated(:generic, "Short and stout.") # chef:silence_deprecation + Chef.deprecated(:internal_api, "This is my handle.") + Chef.deprecated(:internal_api, "This is my spout.") + Chef.deprecated(:exit_code, "When I get all steamed up.") + Chef.deprecated(:generic, "Hear me shout.") + end + end + + context "with treat_deprecation_warnings_as_errors true" do + # This is already turned on globally for Chef's unit tests, but just for clarity do it here too. + before { Chef::Config[:treat_deprecation_warnings_as_errors] = true } + + it "displays a simple deprecation error" do + expect(Chef::Log).to receive(:error).with(%r{spec/unit/chef_class_spec\.rb.*?I'm a little teapot.*?Please see}m) + expect { Chef.deprecated(:generic, "I'm a little teapot.") }.to raise_error(/I'm a little teapot./) + end + end + end end diff --git a/spec/unit/deprecated_spec.rb b/spec/unit/deprecated_spec.rb index 4eba764b63..9c60080cef 100644 --- a/spec/unit/deprecated_spec.rb +++ b/spec/unit/deprecated_spec.rb @@ -20,40 +20,46 @@ require "chef/deprecated" describe Chef::Deprecated do class TestDeprecation < Chef::Deprecated::Base - def id; 999; end - - def target; "test.html"; end - - def link; "#{Chef::Deprecated::Base::BASE_URL}test.html"; end + target 999, "test.html" end context "loading a deprecation class" do it "loads the correct class" do - expect(Chef::Deprecated.create(:test_deprecation)).to be_an_instance_of(TestDeprecation) + expect(Chef::Deprecated.create(:test_deprecation, nil, nil)).to be_an_instance_of(TestDeprecation) end - it "optionally sets a message" do - deprecation = Chef::Deprecated.create(:test_deprecation, "A test message") + it "sets a message" do + deprecation = Chef::Deprecated.create(:test_deprecation, "A test message", nil) expect(deprecation.message).to eql("A test message") end - it "optionally sets the location" do + it "sets the location" do deprecation = Chef::Deprecated.create(:test_deprecation, nil, "A test location") expect(deprecation.location).to eql("A test location") end end context "formatting deprecation warnings" do - let(:base_url) { Chef::Deprecated::Base::BASE_URL } let(:message) { "A test message" } let(:location) { "the location" } it "displays the full URL" do - expect(TestDeprecation.new().url).to eql("#{base_url}test.html") + expect(TestDeprecation.new().url).to eql("https://docs.chef.io/deprecations_test.html") end it "formats a complete deprecation message" do - expect(TestDeprecation.new(message, location).inspect).to eql("#{message} (CHEF-999)#{location}.\nhttps://docs.chef.io/deprecations_test.html") + expect(TestDeprecation.new(message, location).to_s).to eql("Deprecation CHEF-999 from the location\n\n A test message\n\nPlease see https://docs.chef.io/deprecations_test.html for further details and information on how to correct this problem.") + end + end + + it "has no overlapping deprecation IDs" do + id_map = {} + ObjectSpace.each_object(Class).select { |cls| cls < Chef::Deprecated::Base }.each do |cls| + (id_map[cls.deprecation_id] ||= []) << cls + end + collisions = id_map.select { |k, v| v.size != 1 } + unless collisions.empty? + raise "Found deprecation ID collisions:\n#{collisions.map { |k, v| "* #{k} #{v.map(&:name).join(', ')}" }.join("\n")}" end end end diff --git a/spec/unit/node_map_spec.rb b/spec/unit/node_map_spec.rb index 7e4980219a..253486438b 100644 --- a/spec/unit/node_map_spec.rb +++ b/spec/unit/node_map_spec.rb @@ -213,7 +213,7 @@ describe Chef::NodeMap do describe "locked mode" do context "while unlocked" do it "allows setting the same key twice" do - expect(Chef).to_not receive(:log_deprecation) + expect(Chef).to_not receive(:deprecated) node_map.set(:foo, FooResource) node_map.set(:foo, BarResource) expect(node_map.get(node, :foo)).to eql(BarResource) @@ -223,7 +223,7 @@ describe Chef::NodeMap do context "while locked" do # Uncomment the commented `expect`s in 15.0. it "rejects setting the same key twice" do - expect(Chef).to receive(:log_deprecation).with("Trying to register resource foo on top of existing Chef core resource. Check if a new version of the cookbook is available.") + expect(Chef).to receive(:deprecated).with(:map_collision, /resource foo/) node_map.set(:foo, FooResource) node_map.lock! node_map.set(:foo, BarResource) @@ -231,7 +231,7 @@ describe Chef::NodeMap do end it "allows setting the same key twice when the first has allow_cookbook_override" do - expect(Chef).to_not receive(:log_deprecation) + expect(Chef).to_not receive(:deprecated) node_map.set(:foo, FooResource, allow_cookbook_override: true) node_map.lock! node_map.set(:foo, BarResource) @@ -239,7 +239,7 @@ describe Chef::NodeMap do end it "allows setting the same key twice when the first has allow_cookbook_override with a future version" do - expect(Chef).to_not receive(:log_deprecation) + expect(Chef).to_not receive(:deprecated) node_map.set(:foo, FooResource, allow_cookbook_override: "< 100") node_map.lock! node_map.set(:foo, BarResource) @@ -247,7 +247,7 @@ describe Chef::NodeMap do end it "rejects setting the same key twice when the first has allow_cookbook_override with a past version" do - expect(Chef).to receive(:log_deprecation).with("Trying to register resource foo on top of existing Chef core resource. Check if a new version of the cookbook is available.") + expect(Chef).to receive(:deprecated).with(:map_collision, /resource foo/) node_map.set(:foo, FooResource, allow_cookbook_override: "< 1") node_map.lock! node_map.set(:foo, BarResource) @@ -255,7 +255,7 @@ describe Chef::NodeMap do end it "allows setting the same key twice when the second has __core_override__" do - expect(Chef).to_not receive(:log_deprecation) + expect(Chef).to_not receive(:deprecated) node_map.set(:foo, FooResource) node_map.lock! node_map.set(:foo, BarResource, __core_override__: true) @@ -263,7 +263,7 @@ describe Chef::NodeMap do end it "rejects setting the same key twice for a provider" do - expect(Chef).to receive(:log_deprecation).with("Trying to register provider foo on top of existing Chef core provider. Check if a new version of the cookbook is available.") + expect(Chef).to receive(:deprecated).with(:map_collision, /provider foo/) node_map.set(:foo, FooProvider) node_map.lock! node_map.set(:foo, BarProvider) |