diff options
37 files changed, 1269 insertions, 226 deletions
diff --git a/.expeditor/release.omnibus.yml b/.expeditor/release.omnibus.yml index 440ce1ce34..1819c0d207 100644 --- a/.expeditor/release.omnibus.yml +++ b/.expeditor/release.omnibus.yml @@ -9,6 +9,7 @@ fips-platforms: builder-to-testers-map: aix-7.1-powerpc: - aix-7.1-powerpc + - aix-7.2-powerpc debian-8-x86_64: - debian-8-x86_64 - debian-9-x86_64 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c4e357704..9b06039592 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,24 @@ <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ --> -<!-- latest_release 15.2.24 --> -## [v15.2.24](https://github.com/chef/chef/tree/v15.2.24) (2019-08-13) +<!-- latest_release 15.2.33 --> +## [v15.2.33](https://github.com/chef/chef/tree/v15.2.33) (2019-08-29) #### Merged Pull Requests -- Begin signing MSI's with renewed Windows Signing Cert [#8813](https://github.com/chef/chef/pull/8813) ([schisamo](https://github.com/schisamo)) +- Update InSpec to 4.12 and Train to 3.0 [#8854](https://github.com/chef/chef/pull/8854) ([tas50](https://github.com/tas50)) <!-- latest_release --> <!-- release_rollup since=15.2.20 --> ### Changes not yet released to stable #### Merged Pull Requests +- Update InSpec to 4.12 and Train to 3.0 [#8854](https://github.com/chef/chef/pull/8854) ([tas50](https://github.com/tas50)) <!-- 15.2.33 --> +- remove app_server_support spec file [#8852](https://github.com/chef/chef/pull/8852) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 15.2.32 --> +- Fix node[:cookbooks] attribute [#8846](https://github.com/chef/chef/pull/8846) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 15.2.31 --> +- Fail on interval runs on windows as interval runs on Windows don't entirely work [#6777](https://github.com/chef/chef/pull/6777) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 15.2.30 --> +- ifconfig: fix regex matching interface name with hyphen [#8756](https://github.com/chef/chef/pull/8756) ([dheerajd-msys](https://github.com/dheerajd-msys)) <!-- 15.2.29 --> +- Add AIX 7.2 platform [#8832](https://github.com/chef/chef/pull/8832) ([jaymalasinha](https://github.com/jaymalasinha)) <!-- 15.2.28 --> +- Fix crash when showing error about missing profile [#8828](https://github.com/chef/chef/pull/8828) ([andrewdotn](https://github.com/andrewdotn)) <!-- 15.2.27 --> +- Add unified_mode for resources [#8668](https://github.com/chef/chef/pull/8668) ([lamont-granquist](https://github.com/lamont-granquist)) <!-- 15.2.26 --> +- Update omnibus build deps to the latest [#8823](https://github.com/chef/chef/pull/8823) ([tas50](https://github.com/tas50)) <!-- 15.2.25 --> - Begin signing MSI's with renewed Windows Signing Cert [#8813](https://github.com/chef/chef/pull/8813) ([schisamo](https://github.com/schisamo)) <!-- 15.2.24 --> - Updated knife cookbook metadata from file command banner [#8812](https://github.com/chef/chef/pull/8812) ([samshinde](https://github.com/samshinde)) <!-- 15.2.23 --> - Update the link to our release cadence information [#8807](https://github.com/chef/chef/pull/8807) ([tas50](https://github.com/tas50)) <!-- 15.2.22 --> diff --git a/Dockerfile b/Dockerfile index cd3fdbce8e..437511e9cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,11 +15,17 @@ # applied so 15.0.260 would be tagged as "latest", "15" and "15.0", as well as "15.0.260". FROM busybox -MAINTAINER Chef Software, Inc. <docker@chef.io> +LABEL maintainer="Chef Software, Inc. <docker@chef.io>" +ARG EXPEDITOR_CHANNEL ARG CHANNEL=stable +ARG EXPEDITOR_VERSION ARG VERSION=15.2.20 +# Allow the build arg below to be controlled by either build arguments +ENV VERSION ${EXPEDITOR_VERSION:-${VERSION}} +ENV CHANNEL ${EXPEDITOR_CHANNEL:-${CHANNEL}} + RUN wget "http://packages.chef.io/files/${CHANNEL}/chef/${VERSION}/el/6/chef-${VERSION}-1.el6.x86_64.rpm" -O /tmp/chef-client.rpm && \ rpm2cpio /tmp/chef-client.rpm | cpio -idmv && \ rm -rf /tmp/chef-client.rpm diff --git a/Gemfile.lock b/Gemfile.lock index 4ffd3f8348..197cbdb6a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,10 +8,10 @@ GIT GIT remote: https://github.com/chef/ohai.git - revision: f76c5f583736a5731e2fb6e92c21dc8419655d95 + revision: 15315e3e36f07062111faede26f2667ead8c723f branch: master specs: - ohai (15.2.5) + ohai (15.2.7) chef-config (>= 12.8, < 16) ffi (~> 1.9) ffi-yajl (~> 2.2) @@ -27,11 +27,11 @@ GIT PATH remote: . specs: - chef (15.2.24) + chef (15.2.33) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 15.2.24) + chef-config (= 15.2.33) chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) ed25519 (~> 1.2) @@ -54,14 +54,15 @@ PATH plist (~> 3.2) proxifier (~> 1.0) syslog-logger (~> 1.6) - train-core (~> 2.0, >= 2.0.12) + train-core (~> 3.0) + train-winrm tty-screen (~> 0.6) uuidtools (~> 2.1.5) - chef (15.2.24-universal-mingw32) + chef (15.2.33-universal-mingw32) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 15.2.24) + chef-config (= 15.2.33) chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) ed25519 (~> 1.2) @@ -85,7 +86,8 @@ PATH plist (~> 3.2) proxifier (~> 1.0) syslog-logger (~> 1.6) - train-core (~> 2.0, >= 2.0.12) + train-core (~> 3.0) + train-winrm tty-screen (~> 0.6) uuidtools (~> 2.1.5) win32-api (~> 1.5.3) @@ -103,13 +105,13 @@ PATH PATH remote: chef-bin specs: - chef-bin (15.2.24) - chef (= 15.2.24) + chef-bin (15.2.33) + chef (= 15.2.33) PATH remote: chef-config specs: - chef-config (15.2.24) + chef-config (15.2.33) addressable fuzzyurl mixlib-config (>= 2.2.12, < 4.0) @@ -175,7 +177,7 @@ GEM htmlentities (4.3.4) httpclient (2.8.3) iniparse (1.4.4) - inspec-core (4.10.4) + inspec-core (4.12.0) addressable (~> 2.4) faraday (>= 0.9.0) faraday_middleware (~> 0.12.2) @@ -197,11 +199,11 @@ GEM term-ansicolor thor (~> 0.20) tomlrb (~> 1.2) - train-core (~> 2.0) + train-core (~> 3.0) tty-prompt (~> 0.17) tty-table (~> 0.10) - inspec-core-bin (4.10.4) - inspec-core (= 4.10.4) + inspec-core-bin (4.12.0) + inspec-core (= 4.12.0) ipaddress (0.8.3) iso8601 (0.12.1) jaro_winkler (1.5.3) @@ -312,7 +314,7 @@ GEM simplecov-html (0.10.2) slop (3.6.0) sslshake (1.3.0) - strings (0.1.5) + strings (0.1.6) strings-ansi (~> 0.1) unicode-display_width (~> 1.5) unicode_utils (~> 1.4) @@ -325,16 +327,17 @@ GEM thor (0.20.3) tins (1.21.1) tomlrb (1.2.8) - train-core (2.1.19) + train-core (3.0.3) json (>= 1.8, < 3.0) mixlib-shellout (>= 2.0, < 4.0) net-scp (>= 1.2, < 3.0) net-ssh (>= 2.9, < 6.0) + train-winrm (0.2.3) winrm (~> 2.0) winrm-fs (~> 1.0) - tty-box (0.4.0) + tty-box (0.4.1) pastel (~> 0.7.2) - strings (~> 0.1.5) + strings (~> 0.1.6) tty-cursor (~> 0.7) tty-color (0.5.0) tty-cursor (0.7.0) @@ -356,7 +359,7 @@ GEM unicode-display_width (1.6.0) unicode_utils (1.4.0) uuidtools (2.1.5) - webmock (3.6.2) + webmock (3.7.0) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -1 +1 @@ -15.2.24
\ No newline at end of file +15.2.33
\ No newline at end of file diff --git a/chef-bin/lib/chef-bin/version.rb b/chef-bin/lib/chef-bin/version.rb index d4d90d38e2..d27bbc5507 100644 --- a/chef-bin/lib/chef-bin/version.rb +++ b/chef-bin/lib/chef-bin/version.rb @@ -21,7 +21,7 @@ module ChefBin CHEFBIN_ROOT = File.expand_path("../..", __FILE__) - VERSION = "15.2.24".freeze + VERSION = "15.2.33".freeze end # diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index d136a8f79c..b0583b7a06 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -875,6 +875,7 @@ module ChefConfig # # NOTE: CHANGING THIS SETTING MAY CAUSE CORRUPTION, DATA LOSS AND # INSTABILITY. + # default :file_atomic_update, true # There are 3 possible values for this configuration setting. @@ -882,19 +883,28 @@ module ChefConfig # false => file staging is done via tempfiles under ENV['TMP'] # :auto => file staging will try using destination directory if possible and # will fall back to ENV['TMP'] if destination directory is not usable. + # default :file_staging_uses_destdir, :auto # Exit if another run is in progress and the chef-client is unable to # get the lock before time expires. If nil, no timeout is enforced. (Exits # immediately if 0.) + # default :run_lock_timeout, nil # Number of worker threads for syncing cookbooks in parallel. Increasing # this number can result in gateway errors from the server (namely 503 and 504). # If you are seeing this behavior while using the default setting, reducing # the number of threads will help. + # default :cookbook_sync_threads, 10 + # True if all resources by default default to unified mode, with all resources + # applying in "compile" mode, with no "converge" mode. False is backwards compatible + # setting for Chef 11-15 behavior. This will break forward notifications. + # + default :resource_unified_mode_default, false + # At the beginning of the Chef Client run, the cookbook manifests are downloaded which # contain URLs for every file in every relevant cookbook. Most of the files # (recipes, resources, providers, libraries, etc) are immediately synchronized @@ -920,9 +930,9 @@ module ChefConfig default :no_lazy_load, true # A whitelisted array of attributes you want sent over the wire when node - # data is saved. - # The default setting is nil, which collects all data. Setting to [] will not - # collect any data for save. + # data is saved. The default setting is nil, which collects all data. Setting + # to [] will not collect any data for save. + # default :automatic_attribute_whitelist, nil default :default_attribute_whitelist, nil default :normal_attribute_whitelist, nil diff --git a/chef-config/lib/chef-config/mixin/credentials.rb b/chef-config/lib/chef-config/mixin/credentials.rb index 3882924d1a..bb4d55f4bc 100644 --- a/chef-config/lib/chef-config/mixin/credentials.rb +++ b/chef-config/lib/chef-config/mixin/credentials.rb @@ -92,7 +92,7 @@ module ChefConfig # raise an error. return if profile == "default" - raise ChefConfig::ConfigurationError, "Profile #{profile} doesn't exist. Please add it to #{credentials_file}." + raise ChefConfig::ConfigurationError, "Profile #{profile} doesn't exist. Please add it to #{credentials_file_path}." end apply_credentials(config[profile], profile) end diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index 1363c33edc..ef7014daf3 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "15.2.24".freeze + VERSION = "15.2.33".freeze end # diff --git a/chef.gemspec b/chef.gemspec index a89cf4ceb2..e78237b31d 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -16,7 +16,8 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.5.0" s.add_dependency "chef-config", "= #{Chef::VERSION}" - s.add_dependency "train-core", "~> 2.0", ">= 2.0.12" + s.add_dependency "train-core", "~> 3.0" + s.add_dependency "train-winrm" s.add_dependency "license-acceptance", "~> 1.0", ">= 1.0.5" s.add_dependency "mixlib-cli", ">= 2.1.1", "< 3.0" diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb index fea3d844ce..c5bff9874e 100644 --- a/lib/chef/application/base.rb +++ b/lib/chef/application/base.rb @@ -340,6 +340,13 @@ class Chef::Application::Base < Chef::Application private + def windows_interval_error_message + "Windows #{Chef::Dist::PRODUCT} interval runs are not supported in #{Chef::Dist::PRODUCT} 15 and later." + + "\nConfiguration settings:" + + ("\n interval = #{Chef::Config[:interval]} seconds" if Chef::Config[:interval]).to_s + + "\nPlease manage #{Chef::Dist::PRODUCT} as a scheduled task instead." + end + def unforked_interval_error_message "Unforked #{Chef::Dist::PRODUCT} interval runs are disabled by default." + "\nConfiguration settings:" + diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index 0fc5ca7711..890ecbd385 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -128,8 +128,12 @@ class Chef::Application::Client < Chef::Application::Base Chef::Config[:client_fork] = !!Chef::Config[:interval] end - if !Chef::Config[:client_fork] && Chef::Config[:interval] && !Chef::Platform.windows? - Chef::Application.fatal!(unforked_interval_error_message) + if Chef::Config[:interval] + if Chef::Platform.windows? + Chef::Application.fatal!(windows_interval_error_message) + elsif !Chef::Config[:client_fork] + Chef::Application.fatal!(unforked_interval_error_message) + end end if Chef::Config[:json_attribs] diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index bce95c2841..da9ec7f566 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -102,7 +102,13 @@ class Chef::Application::Solo < Chef::Application::Base Chef::Config[:client_fork] = !!Chef::Config[:interval] end - Chef::Application.fatal!(unforked_interval_error_message) if !Chef::Config[:client_fork] && Chef::Config[:interval] + if Chef::Config[:interval] + if Chef::Platform.windows? + Chef::Application.fatal!(windows_interval_error_message) + elsif !Chef::Config[:client_fork] + Chef::Application.fatal!(unforked_interval_error_message) + end + end if Chef::Config[:recipe_url] cookbooks_path = Array(Chef::Config[:cookbook_path]).detect { |e| Pathname.new(e).cleanpath.to_s =~ %r{/cookbooks/*$} } diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 7687e7d89c..40d20cc3ac 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -509,5 +509,17 @@ class Chef super "Conflicting requirements for gem '#{gem_name}': Both #{value1.inspect} and #{value2.inspect} given for option #{option.inspect}" end end + + class UnifiedModeImmediateSubscriptionEarlierResource < RuntimeError + def initialize(notification) + super "immediate subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode" + end + end + + class UnifiedModeBeforeSubscriptionEarlierResource < RuntimeError + def initialize(notification) + super "before subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode" + end + end end end diff --git a/lib/chef/node.rb b/lib/chef/node.rb index ec20fbee86..1e5d3a8d59 100644 --- a/lib/chef/node.rb +++ b/lib/chef/node.rb @@ -87,8 +87,6 @@ class Chef # after the run_context has been set on the node, go through the cookbook_collection # and setup the node[:cookbooks] attribute so that it is published in the node object def set_cookbook_attribute - return unless run_context.cookbook_collection - run_context.cookbook_collection.each do |cookbook_name, cookbook| automatic_attrs[:cookbooks][cookbook_name][:version] = cookbook.version end diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb index 4afb4d7d60..bb5e2e199b 100644 --- a/lib/chef/policy_builder/expand_node_object.rb +++ b/lib/chef/policy_builder/expand_node_object.rb @@ -75,7 +75,6 @@ class Chef # def setup_run_context(specific_recipes = nil, run_context = nil) run_context ||= Chef::RunContext.new - run_context.events = events run_context.node = node @@ -93,6 +92,7 @@ class Chef cookbook_collection.validate! cookbook_collection.install_gems(events) + run_context.cookbook_collection = cookbook_collection # TODO: move this into the cookbook_compilation_start hook diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb index 70a2e44635..7eb9de042e 100644 --- a/lib/chef/policy_builder/policyfile.rb +++ b/lib/chef/policy_builder/policyfile.rb @@ -177,16 +177,17 @@ class Chef # # @return [Chef::RunContext] def setup_run_context(specific_recipes = nil, run_context = nil) + run_context ||= Chef::RunContext.new + run_context.node = node + run_context.events = events + Chef::Cookbook::FileVendor.fetch_from_remote(api_service) sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync) cookbook_collection.validate! cookbook_collection.install_gems(events) - run_context ||= Chef::RunContext.new - run_context.node = node run_context.cookbook_collection = cookbook_collection - run_context.events = events setup_chef_class(run_context) diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb index 6d1985bbbb..fb5697fd0c 100644 --- a/lib/chef/provider.rb +++ b/lib/chef/provider.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Christopher Walters (<cw@chef.io>) -# Copyright:: Copyright 2008-2016, 2009-2018, Chef Software Inc. +# Copyright:: Copyright 2008-2016, 2009-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -238,8 +238,10 @@ class Chef def compile_and_converge_action(&block) old_run_context = run_context @run_context = run_context.create_child + @run_context.resource_collection.unified_mode = new_resource.class.unified_mode + runner = Chef::Runner.new(@run_context) return_value = instance_eval(&block) - Chef::Runner.new(run_context).converge + runner.converge return_value ensure if run_context.resource_collection.any?(&:updated?) diff --git a/lib/chef/provider/ifconfig.rb b/lib/chef/provider/ifconfig.rb index 93db827e97..71b64169c2 100644 --- a/lib/chef/provider/ifconfig.rb +++ b/lib/chef/provider/ifconfig.rb @@ -109,18 +109,20 @@ class Chef # RX errors 0 dropped 0 overruns 0 frame 0 # TX packets 1244218 bytes 977339327 (932.0 MiB) # TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + # + # Permalink for addr_regex : https://rubular.com/r/JrykUpfjRnYeQD @status = shell_out("ifconfig") @status.stdout.each_line do |line| - addr_regex = /^(\w+):?(\d*):?\ .+$/ + addr_regex = /^((\w|-)+):?(\d*):?\ .+$/ if line =~ addr_regex if line.match(addr_regex).nil? @int_name = "nil" - elsif line.match(addr_regex)[2] == "" + elsif line.match(addr_regex)[3] == "" @int_name = line.match(addr_regex)[1] @interfaces[@int_name] = {} @interfaces[@int_name]["mtu"] = (line =~ /mtu (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /mtu/ && @interfaces[@int_name]["mtu"].nil? else - @int_name = "#{line.match(addr_regex)[1]}:#{line.match(addr_regex)[2]}" + @int_name = "#{line.match(addr_regex)[1]}:#{line.match(addr_regex)[3]}" @interfaces[@int_name] = {} @interfaces[@int_name]["mtu"] = (line =~ /mtu (\S+)/ ? Regexp.last_match(1) : "nil") if line =~ /mtu/ && @interfaces[@int_name]["mtu"].nil? end diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 7f0895d6c9..a413e3e8d4 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -182,7 +182,7 @@ class Chef { action: { kind_of: Symbol, equal_to: allowed_actions } } ) # the resource effectively sends a delayed notification to itself - run_context.add_delayed_action(Notification.new(self, action, self)) + run_context.add_delayed_action(Notification.new(self, action, self, run_context.unified_mode)) end end @@ -453,7 +453,6 @@ class Chef # attr_reader :elapsed_time - # # @return [Boolean] If the resource was executed by the runner # attr_accessor :executed_by_runner @@ -985,6 +984,16 @@ class Chef resource_name automatic_name end + # If the resource's action should run in separated compile/converge mode. + # + # @param flag [Boolean] value to set unified_mode to + # @return [Boolean] unified_mode value + def self.unified_mode(flag = nil) + @unified_mode = Chef::Config[:resource_unified_mode_default] if @unified_mode.nil? + @unified_mode = flag unless flag.nil? + !!@unified_mode + end + # # The list of allowed actions for the resource. # @@ -1038,7 +1047,6 @@ class Chef default_action action_name end - # # Define an action on this resource. # # The action is defined as a *recipe* block that will be compiled and then @@ -1076,7 +1084,6 @@ class Chef default_action action if Array(default_action) == [:nothing] end - # # Define a method to load up this resource's properties with the current # actual values. # @@ -1087,7 +1094,6 @@ class Chef define_method(:load_current_value!, &load_block) end - # # Call this in `load_current_value` to indicate that the value does not # exist and that `current_resource` should therefore be `nil`. # @@ -1097,7 +1103,6 @@ class Chef raise Chef::Exceptions::CurrentValueDoesNotExist end - # # Get the current actual value of this resource. # # This does not cache--a new value will be returned each time. @@ -1154,7 +1159,6 @@ class Chef end end - # # Ensure the action class actually gets created. This is called # when the user does `action :x do ... end`. # @@ -1211,22 +1215,27 @@ class Chef # @return [Chef::RunContext] The run context for this Resource. This is # where the context for the current Chef run is stored, including the node # and the resource collection. + # attr_accessor :run_context # @return [Mixlib::Log::Child] The logger for this resources. This is a child # of the run context's logger, if one exists. + # attr_reader :logger # @return [String] The cookbook this resource was declared in. + # attr_accessor :cookbook_name # @return [String] The recipe this resource was declared in. + # attr_accessor :recipe_name # @return [Chef::Provider] The provider this resource was declared in (if # it was declared in an LWRP). When you call methods that do not exist # on this Resource, Chef will try to call the method on the provider # as well before giving up. + # attr_accessor :enclosing_provider # @return [String] The source line where this resource was declared. @@ -1234,6 +1243,7 @@ class Chef # of these formats: # /some/path/to/file.rb:80:in `wombat_tears' # C:/some/path/to/file.rb:80 in 1`wombat_tears' + # attr_accessor :source_line # @return [String] The actual name that was used to create this resource. @@ -1242,37 +1252,40 @@ class Chef # user will expect to see the thing they wrote, not the type that was # returned. May be `nil`, in which case callers should read #resource_name. # See #declared_key. + # attr_accessor :declared_type - # # Iterates over all immediate and delayed notifications, calling # resolve_resource_reference on each in turn, causing them to # resolve lazy/forward references. - def resolve_notification_references + # + def resolve_notification_references(always_raise = false) run_context.before_notifications(self).each do |n| - n.resolve_resource_reference(run_context.resource_collection) + n.resolve_resource_reference(run_context.resource_collection, true) end + run_context.immediate_notifications(self).each do |n| - n.resolve_resource_reference(run_context.resource_collection) + n.resolve_resource_reference(run_context.resource_collection, always_raise) end + run_context.delayed_notifications(self).each do |n| - n.resolve_resource_reference(run_context.resource_collection) + n.resolve_resource_reference(run_context.resource_collection, always_raise) end end # Helper for #notifies def notifies_before(action, resource_spec) - run_context.notifies_before(Notification.new(resource_spec, action, self)) + run_context.notifies_before(Notification.new(resource_spec, action, self, run_context.unified_mode)) end # Helper for #notifies def notifies_immediately(action, resource_spec) - run_context.notifies_immediately(Notification.new(resource_spec, action, self)) + run_context.notifies_immediately(Notification.new(resource_spec, action, self, run_context.unified_mode)) end # Helper for #notifies def notifies_delayed(action, resource_spec) - run_context.notifies_delayed(Notification.new(resource_spec, action, self)) + run_context.notifies_delayed(Notification.new(resource_spec, action, self, run_context.unified_mode)) end class << self @@ -1321,7 +1334,6 @@ class Chef end end - # # This API can be used for backcompat to do: # # chef_version_for_provides "< 14.0" if defined?(:chef_version_for_provides) @@ -1343,7 +1355,6 @@ class Chef @chef_version_for_provides = constraint end - # # Mark this resource as providing particular DSL. # # Resources have an automatic DSL based on their resource_name, equivalent to @@ -1472,7 +1483,6 @@ class Chef @default_description end - # # The cookbook in which this Resource was defined (if any). # # @return Chef::CookbookVersion The cookbook in which this Resource was defined. @@ -1498,7 +1508,6 @@ class Chef provider end - # # Preface an exception message with generic Resource information. # # @param e [StandardError] An exception with `e.message` @@ -1554,7 +1563,6 @@ class Chef klass end - # # Returns the class with the given resource_name. # # NOTE: Chef::Resource.resource_matching_short_name(:package) returns diff --git a/lib/chef/resource/resource_notification.rb b/lib/chef/resource/resource_notification.rb index 7e93fff433..d3b9856332 100644 --- a/lib/chef/resource/resource_notification.rb +++ b/lib/chef/resource/resource_notification.rb @@ -1,6 +1,6 @@ # # Author:: Tyler Ball (<tball@chef.io>) -# Copyright:: Copyright 2014-2016, Chef Software, Inc. +# Copyright:: Copyright 2014-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,12 +26,13 @@ class Chef # @attr [Resource] notifying_resource the Chef resource performing the notification class Notification - attr_accessor :resource, :action, :notifying_resource + attr_accessor :resource, :action, :notifying_resource, :unified_mode - def initialize(resource, action, notifying_resource) + def initialize(resource, action, notifying_resource, unified_mode = false) @resource = resource @action = action&.to_sym @notifying_resource = notifying_resource + @unified_mode = unified_mode end # Is the current notification a duplicate of another notification @@ -52,11 +53,11 @@ class Chef # @param [ResourceCollection] resource_collection # # @return [void] - def resolve_resource_reference(resource_collection) + def resolve_resource_reference(resource_collection, always_raise = false) return resource if resource.is_a?(Chef::Resource) && notifying_resource.is_a?(Chef::Resource) unless resource.is_a?(Chef::Resource) - fix_resource_reference(resource_collection) + fix_resource_reference(resource_collection, always_raise) end unless notifying_resource.is_a?(Chef::Resource) @@ -69,7 +70,7 @@ class Chef # @param [ResourceCollection] resource_collection # # @return [void] - def fix_resource_reference(resource_collection) + def fix_resource_reference(resource_collection, always_raise = false) matching_resource = resource_collection.find(resource) if Array(matching_resource).size > 1 msg = "Notification #{self} from #{notifying_resource} was created with a reference to multiple resources, "\ @@ -79,13 +80,16 @@ class Chef self.resource = matching_resource rescue Chef::Exceptions::ResourceNotFound => e - err = Chef::Exceptions::ResourceNotFound.new(<<~FAIL) - resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \ - but #{resource} cannot be found in the resource collection. #{notifying_resource} is defined in \ - #{notifying_resource.source_line} - FAIL - err.set_backtrace(e.backtrace) - raise err + # in unified mode we allow lazy notifications to resources not yet declared + if !unified_mode || always_raise + err = Chef::Exceptions::ResourceNotFound.new(<<~FAIL) + resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \ + but #{resource} cannot be found in the resource collection. #{notifying_resource} is defined in \ + #{notifying_resource.source_line} + FAIL + err.set_backtrace(e.backtrace) + raise err + end rescue Chef::Exceptions::InvalidResourceSpecification => e err = Chef::Exceptions::InvalidResourceSpecification.new(<<~F) Resource #{notifying_resource} is configured to notify resource #{resource} with action #{action}, \ diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb index 3b6ff4297e..da675fc4de 100644 --- a/lib/chef/resource_collection.rb +++ b/lib/chef/resource_collection.rb @@ -32,6 +32,8 @@ class Chef include ResourceCollectionSerialization extend Forwardable + attr_accessor :unified_mode + attr_reader :resource_set, :resource_list attr_accessor :run_context @@ -41,6 +43,7 @@ class Chef @run_context = run_context @resource_set = ResourceSet.new @resource_list = ResourceList.new + @unified_mode = false end # @param resource [Chef::Resource] The resource to insert @@ -57,6 +60,9 @@ class Chef else resource_set.insert_as(resource) end + if unified_mode + run_context.runner.run_all_actions(resource) + end end def delete(key) diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index eb211dc5a5..d849679680 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -26,12 +26,16 @@ require_relative "recipe" require_relative "run_context/cookbook_compiler" require_relative "event_dispatch/events_output_stream" require_relative "train_transport" +require_relative "exceptions" require "forwardable" unless defined?(Forwardable) +require "set" unless defined?(Set) class Chef # Value object that loads and tracks the context of a Chef run class RunContext + extend Forwardable + # # Global state # @@ -62,7 +66,7 @@ class Chef # # @return [Chef::CookbookCollection] # - attr_accessor :cookbook_collection + attr_reader :cookbook_collection # # Resource Definitions for this run. Populated when the files in @@ -72,14 +76,12 @@ class Chef # attr_reader :definitions - # # Event dispatcher for this run. # # @return [Chef::EventDispatch::Dispatcher] # attr_accessor :events - # # Hash of factoids for a reboot request. # # @return [Hash] @@ -90,7 +92,6 @@ class Chef # Scoped state # - # # The parent run context. # # @return [Chef::RunContext] The parent run context, or `nil` if this is the @@ -98,7 +99,6 @@ class Chef # attr_reader :parent_run_context - # # The root run context. # # @return [Chef::RunContext] The root run context. @@ -109,7 +109,6 @@ class Chef rc end - # # The collection of resources intended to be converged (and able to be # notified). # @@ -119,8 +118,12 @@ class Chef # attr_reader :resource_collection - attr_accessor :action_collection + # Handle to the global action_collection of executed actions for reporting / data_collector /etc + # + # @return [Chef::ActionCollection # + attr_accessor :action_collection + # Pointer back to the Chef::Runner that created this # attr_accessor :runner @@ -129,7 +132,6 @@ class Chef # Notification handling # - # # A Hash containing the before notifications triggered by resources # during the converge phase of the chef run. # @@ -138,7 +140,6 @@ class Chef # attr_reader :before_notification_collection - # # A Hash containing the immediate notifications triggered by resources # during the converge phase of the chef run. # @@ -147,7 +148,6 @@ class Chef # attr_reader :immediate_notification_collection - # # A Hash containing the delayed (end of run) notifications triggered by # resources during the converge phase of the chef run. # @@ -156,7 +156,6 @@ class Chef # attr_reader :delayed_notification_collection - # # An Array containing the delayed (end of run) notifications triggered by # resources during the converge phase of the chef run. # @@ -164,7 +163,16 @@ class Chef # attr_reader :delayed_actions + # A Set keyed by the string name, of all the resources that are updated. We do not + # track actions or individual resource objects, since this matches the behavior of + # the notification collections which are keyed by Strings. + # + attr_reader :updated_resources + + # @return [Boolean] If the resource_collection is in unified_mode (no separate converge phase) # + def_delegator :resource_collection, :unified_mode + # A child of the root Chef::Log logging object. # # @return Mixlib::Log::Child A child logger @@ -180,17 +188,16 @@ class Chef # @param events [EventDispatch::Dispatcher] The event dispatcher for this # run. # - def initialize(node = nil, cookbook_collection = {}, events = nil, logger = nil) + def initialize(node = nil, cookbook_collection = nil, events = nil, logger = nil) @events = events @logger = logger || Chef::Log.with_child - @cookbook_collection = cookbook_collection self.node = node if node + self.cookbook_collection = cookbook_collection if cookbook_collection @definitions = {} @loaded_recipes_hash = {} @loaded_attributes_hash = {} @reboot_info = {} @cookbook_compiler = nil - @delayed_actions = [] initialize_child_state end @@ -198,6 +205,10 @@ class Chef def node=(node) @node = node node.run_context = self + end + + def cookbook_collection=(cookbook_collection) + @cookbook_collection = cookbook_collection node.set_cookbook_attribute end @@ -221,6 +232,7 @@ class Chef @immediate_notification_collection = Hash.new { |h, k| h[k] = [] } @delayed_notification_collection = Hash.new { |h, k| h[k] = [] } @delayed_actions = [] + @updated_resources = Set.new end # @@ -232,6 +244,10 @@ class Chef # Note for the future, notification.notifying_resource may be an instance # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes} # with a string value. + if unified_mode && updated_resources.include?(notification.notifying_resource.declared_key) + raise Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource.new(notification) + end + before_notification_collection[notification.notifying_resource.declared_key] << notification end @@ -256,11 +272,13 @@ class Chef # Note for the future, notification.notifying_resource may be an instance # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes} # with a string value. + if unified_mode && updated_resources.include?(notification.notifying_resource.declared_key) + add_delayed_action(notification) + end delayed_notification_collection[notification.notifying_resource.declared_key] << notification end - # - # Adds a delayed action to the +delayed_actions+. + # Adds a delayed action to the delayed_actions collection # def add_delayed_action(notification) if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) } @@ -271,32 +289,45 @@ class Chef end end - # # Get the list of before notifications sent by the given resource. # # @return [Array[Notification]] # def before_notifications(resource) - before_notification_collection[resource.declared_key] + key = resource.is_a?(String) ? resource : resource.declared_key + before_notification_collection[key] end - # # Get the list of immediate notifications sent by the given resource. # # @return [Array[Notification]] # def immediate_notifications(resource) - immediate_notification_collection[resource.declared_key] + key = resource.is_a?(String) ? resource : resource.declared_key + immediate_notification_collection[key] end + # Get the list of immeidate notifications pending to the given resource # + # @return [Array[Notification]] + # + def reverse_immediate_notifications(resource) + immediate_notification_collection.map do |k, v| + v.select do |n| + (n.resource.is_a?(String) && n.resource == resource.declared_key) || + n.resource == resource + end + end.flatten + end + # Get the list of delayed (end of run) notifications sent by the given # resource. # # @return [Array[Notification]] # def delayed_notifications(resource) - delayed_notification_collection[resource.declared_key] + key = resource.is_a?(String) ? resource : resource.declared_key + delayed_notification_collection[key] end # @@ -666,9 +697,9 @@ class Chef rest= rest_clean rest_clean= - unreachable_cookbook? transport transport_connection + unreachable_cookbook? } def initialize(parent_run_context) @@ -681,8 +712,10 @@ class Chef end CHILD_STATE = %w{ - create_child add_delayed_action + before_notification_collection + before_notifications + create_child delayed_actions delayed_notification_collection delayed_notification_collection= @@ -690,21 +723,22 @@ class Chef immediate_notification_collection immediate_notification_collection= immediate_notifications - before_notification_collection - before_notifications include_recipe initialize_child_state load_recipe load_recipe_file notifies_before - notifies_immediately notifies_delayed + notifies_immediately parent_run_context - root_run_context resource_collection resource_collection= + reverse_immediate_notifications + root_run_context runner runner= + unified_mode + updated_resources }.map(&:to_sym) # Verify that we didn't miss any methods diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb index a0ae61fe4c..8c68554af5 100644 --- a/lib/chef/runner.rb +++ b/lib/chef/runner.rb @@ -34,7 +34,7 @@ class Chef def initialize(run_context) @run_context = run_context - run_context.runner = self + @run_context.runner = self end def delayed_actions @@ -45,6 +45,10 @@ class Chef @run_context.events end + def updated_resources + @run_context.updated_resources + end + # Determine the appropriate provider for the given resource, then # execute it. def run_action(resource, action, notification_type = nil, notifying_resource = nil) @@ -73,33 +77,66 @@ class Chef # associated with the resource, but only if it was updated *this time* # we ran an action on it. if resource.updated_by_last_action? + updated_resources.add(resource.declared_key) # track updated resources for unified_mode run_context.immediate_notifications(resource).each do |notification| - Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)") - run_action(notification.resource, notification.action, :immediate, resource) + if notification.resource.is_a?(String) && run_context.unified_mode + Chef::Log.debug("immediate notification from #{resource} to #{notification.resource} is delayed until declaration due to unified_mode") + else + Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (immediate)") + run_action(notification.resource, notification.action, :immediate, resource) + end end run_context.delayed_notifications(resource).each do |notification| - # send the notification to the run_context of the receiving resource - notification.resource.run_context.add_delayed_action(notification) + if notification.resource.is_a?(String) + # for string resources that have not be declared yet in unified mode we only support notifying the current run_context + run_context.add_delayed_action(notification) + else + # send the notification to the run_context of the receiving resource + notification.resource.run_context.add_delayed_action(notification) + end + end + end + end + + # Runs all of the actions on a given resource. This fires notifications and marks + # the resource as having been executed by the runner. + # + # @param resource [Chef::Resource] the resource to run + # + def run_all_actions(resource) + Array(resource.action).each { |action| run_action(resource, action) } + if run_context.unified_mode + run_context.reverse_immediate_notifications(resource).each do |n| + if updated_resources.include?(n.notifying_resource.declared_key) + n.resolve_resource_reference(run_context.resource_collection) + Chef::Log.info("#{resource} sent #{n.action} action to #{n.resource} (immediate at declaration time)") + run_action(n.resource, n.action, :immediate, n.notifying_resource) + end end end + ensure + resource.executed_by_runner = true end - # Iterates over the +resource_collection+ in the +run_context+ calling - # +run_action+ for each resource in turn. + # Iterates over the resource_collection in the run_context calling + # run_action for each resource in turn. + # def converge # Resolve all lazy/forward references in notifications run_context.resource_collection.each(&:resolve_notification_references) # Execute each resource. run_context.resource_collection.execute_each_resource do |resource| - begin - Array(resource.action).each { |action| run_action(resource, action) } - ensure - resource.executed_by_runner = true + unless run_context.resource_collection.unified_mode + run_all_actions(resource) end end + if run_context.resource_collection.unified_mode + run_context.resource_collection.each { |r| r.resolve_notification_references(true) } + end + rescue Exception => e Chef::Log.info "Running queued delayed notifications before re-raising exception" run_delayed_notifications(e) @@ -126,7 +163,8 @@ class Chef def run_delayed_notification(notification) Chef::Log.info( "#{notification.notifying_resource} sending #{notification.action}"\ " action to #{notification.resource} (delayed)") - # Struct of resource/action to call + # notifications may have lazy strings in them to resolve + notification.resolve_resource_reference(run_context.resource_collection) run_action(notification.resource, notification.action, :delayed) true rescue Exception => e diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 7d20e4886f..238143495b 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -23,7 +23,7 @@ require_relative "version_string" class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = Chef::VersionString.new("15.2.24") + VERSION = Chef::VersionString.new("15.2.33") end # diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index e8460b670f..74bd1ddd09 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/chef/omnibus - revision: 33bddeefb10afe23a57ea72807625881ab01990f + revision: c872e61c30d2b3f88ead03bd1254ff96d37059a3 branch: master specs: - omnibus (6.1.0) + omnibus (6.1.3) aws-sdk-s3 (~> 1) chef-sugar (>= 3.3) cleanroom (~> 1.0) @@ -18,7 +18,7 @@ GIT GIT remote: https://github.com/chef/omnibus-software - revision: 501b5e51a26f4fc8a31c458252c361fdd0767efd + revision: 75f8868a794508d6b567162f13924c9d21ce5dfa branch: master specs: omnibus-software (4.0.0) @@ -33,8 +33,8 @@ GEM artifactory (3.0.5) awesome_print (1.8.0) aws-eventstream (1.0.3) - aws-partitions (1.196.0) - aws-sdk-core (3.62.0) + aws-partitions (1.207.0) + aws-sdk-core (3.65.1) aws-eventstream (~> 1.0, >= 1.0.2) aws-partitions (~> 1.0) aws-sigv4 (~> 1.1) @@ -42,7 +42,7 @@ GEM aws-sdk-kms (1.24.0) aws-sdk-core (~> 3, >= 3.61.1) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.46.0) + aws-sdk-s3 (1.47.0) aws-sdk-core (~> 3, >= 3.61.1) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) @@ -65,11 +65,11 @@ GEM solve (~> 4.0) thor (>= 0.20) builder (3.2.3) - chef (15.1.36) + chef (15.2.20) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 15.1.36) + chef-config (= 15.2.20) chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) ed25519 (~> 1.2) @@ -95,11 +95,11 @@ GEM train-core (~> 2.0, >= 2.0.12) tty-screen (~> 0.6) uuidtools (~> 2.1.5) - chef (15.1.36-universal-mingw32) + chef (15.2.20-universal-mingw32) addressable bcrypt_pbkdf (~> 1.0) bundler (>= 1.10) - chef-config (= 15.1.36) + chef-config (= 15.2.20) chef-zero (>= 14.0.11) diff-lcs (~> 1.2, >= 1.2.4) ed25519 (~> 1.2) @@ -137,7 +137,7 @@ GEM win32-service (>= 2.1.2, < 3.0) win32-taskscheduler (~> 2.0) wmi-lite (~> 1.0) - chef-config (15.1.36) + chef-config (15.2.20) addressable fuzzyurl mixlib-config (>= 2.2.12, < 4.0) @@ -182,7 +182,7 @@ GEM iso8601 (0.12.1) jmespath (1.4.0) json (2.2.0) - kitchen-vagrant (1.5.2) + kitchen-vagrant (1.6.0) test-kitchen (>= 1.4, < 3) libyajl2 (1.2.0) license-acceptance (1.0.13) @@ -234,7 +234,7 @@ GEM nori (2.6.0) octokit (4.14.0) sawyer (~> 0.8.0, >= 0.5.3) - ohai (15.1.5) + ohai (15.2.5) chef-config (>= 12.8, < 16) ffi (~> 1.9) ffi-yajl (~> 2.2) @@ -271,7 +271,7 @@ GEM solve (4.0.2) molinillo (~> 0.6) semverse (>= 1.1, < 4.0) - strings (0.1.5) + strings (0.1.6) strings-ansi (~> 0.1) unicode-display_width (~> 1.5) unicode_utils (~> 1.4) @@ -279,7 +279,7 @@ GEM structured_warnings (0.3.0) syslog-logger (1.6.8) systemu (2.6.5) - test-kitchen (2.2.5) + test-kitchen (2.3.1) bcrypt_pbkdf (~> 1.0) ed25519 (~> 1.2) license-acceptance (~> 1.0, >= 1.0.11) @@ -303,9 +303,9 @@ GEM net-ssh (>= 2.9, < 6.0) winrm (~> 2.0) winrm-fs (~> 1.0) - tty-box (0.4.0) + tty-box (0.4.1) pastel (~> 0.7.2) - strings (~> 0.1.5) + strings (~> 0.1.6) tty-cursor (~> 0.7) tty-color (0.5.0) tty-cursor (0.7.0) diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb index 68cfd015ab..b1763da1f0 100644 --- a/spec/integration/client/client_spec.rb +++ b/spec/integration/client/client_spec.rb @@ -369,6 +369,28 @@ describe "chef-client" do end end + when_the_repository "has a cookbook that outputs some node attributes" do + before do + file "cookbooks/x/recipes/default.rb", <<~'EOM' + puts "COOKBOOKS: #{node[:cookbooks]}" + EOM + file "cookbooks/x/metadata.rb", <<~EOM + name 'x' + version '0.0.1' + EOM + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + EOM + end + + it "should have a cookbook attribute" do + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" -o 'x::default' --no-fork", cwd: chef_dir) + result.error! + expect(result.stdout).to include('COOKBOOKS: {"x"=>{"version"=>"0.0.1"}}') + end + end + when_the_repository "has a cookbook that should fail chef_version checks" do before do file "cookbooks/x/recipes/default.rb", "" diff --git a/spec/integration/knife/raw_spec.rb b/spec/integration/knife/raw_spec.rb index 9fd7664ddd..99fb7b5787 100644 --- a/spec/integration/knife/raw_spec.rb +++ b/spec/integration/knife/raw_spec.rb @@ -19,11 +19,11 @@ require "support/shared/integration/integration_helper" require "support/shared/context/config" require "chef/knife/raw" require "chef/knife/show" +require "tiny_server" describe "knife raw", :workstation do include IntegrationSupport include KnifeSupport - include AppServerSupport include_context "default config options" @@ -185,19 +185,29 @@ describe "knife raw", :workstation do end context "When a server returns raw json" do - before :each do - Chef::Config.chef_server_url = "http://localhost:9018" - app = lambda do |env| - [200, { "Content-Type" => "application/json" }, ['{ "x": "y", "a": "b" }'] ] + def start_tiny_server(server_opts = {}) + @server = TinyServer::Manager.new(server_opts) + @server.start + @api = TinyServer::API.instance + @api.clear + + @api.get("/blah", 200, nil, { "Content-Type" => "application/json" }) do + '{ "x": "y", "a": "b" }' end - @raw_server_thread = start_app_server(app, 9018) + end + + def stop_tiny_server + @server.stop + @server = @api = nil + end + + before :each do + Chef::Config.chef_server_url = "http://localhost:9000" + start_tiny_server end after :each do - if @raw_server_thread - @raw_server_thread.kill - @raw_server_thread.join(30) - end + stop_tiny_server end it "knife raw /blah returns the prettified json" do @@ -217,19 +227,29 @@ describe "knife raw", :workstation do end context "When a server returns text" do - before :each do - Chef::Config.chef_server_url = "http://localhost:9018" - app = lambda do |env| - [200, { "Content-Type" => "text" }, ['{ "x": "y", "a": "b" }'] ] + def start_tiny_server(server_opts = {}) + @server = TinyServer::Manager.new(server_opts) + @server.start + @api = TinyServer::API.instance + @api.clear + + @api.get("/blah", 200, nil, { "Content-Type" => "text" }) do + '{ "x": "y", "a": "b" }' end - @raw_server_thread = start_app_server(app, 9018) + end + + def stop_tiny_server + @server.stop + @server = @api = nil + end + + before :each do + Chef::Config.chef_server_url = "http://localhost:9000" + start_tiny_server end after :each do - if @raw_server_thread - @raw_server_thread.kill - @raw_server_thread.join(30) - end + stop_tiny_server end it "knife raw /blah returns the raw text" do diff --git a/spec/integration/knife/redirection_spec.rb b/spec/integration/knife/redirection_spec.rb index 5e5ef27b9a..fe39315fe4 100644 --- a/spec/integration/knife/redirection_spec.rb +++ b/spec/integration/knife/redirection_spec.rb @@ -1,6 +1,6 @@ # # Author:: John Keiser (<jkeiser@chef.io>) -# Copyright:: Copyright 2013-2018, Chef Software Inc. +# Copyright:: Copyright 2013-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,6 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require "tiny_server" require "support/shared/integration/integration_helper" require "support/shared/context/config" require "chef/knife/list" @@ -22,7 +23,21 @@ require "chef/knife/list" describe "redirection", :workstation do include IntegrationSupport include KnifeSupport - include AppServerSupport + + def start_tiny_server(real_chef_server_url, server_opts = {}) + @server = TinyServer::Manager.new(server_opts) + @server.start + @api = TinyServer::API.instance + @api.clear + + @api.get("/roles", 302, nil, { "Content-Type" => "text", "Location" => "#{real_chef_server_url}/roles" }) do + end + end + + def stop_tiny_server + @server.stop + @server = @api = nil + end include_context "default config options" @@ -30,20 +45,14 @@ describe "redirection", :workstation do before { role "x", {} } context "and another server redirects to it with 302" do - before :each do + before(:each) do real_chef_server_url = Chef::Config.chef_server_url - Chef::Config.chef_server_url = "http://localhost:9018" - app = lambda do |env| - [302, { "Content-Type" => "text", "Location" => "#{real_chef_server_url}#{env["PATH_INFO"]}" }, ["302 found"] ] - end - @redirector_server_thread = start_app_server(app, 9018) + Chef::Config.chef_server_url = "http://localhost:9000" + start_tiny_server(real_chef_server_url) end - after :each do - if @redirector_thread - @redirector_thread.kill - @redirector_thread.join(30) - end + after(:each) do + stop_tiny_server end it "knife list /roles returns the role" do diff --git a/spec/integration/knife/serve_spec.rb b/spec/integration/knife/serve_spec.rb index b0cdd8c070..ab293174d4 100644 --- a/spec/integration/knife/serve_spec.rb +++ b/spec/integration/knife/serve_spec.rb @@ -1,6 +1,6 @@ # # Author:: John Keiser (<jkeiser@chef.io>) -# Copyright:: Copyright 2013-2016, Chef Software Inc. +# Copyright:: Copyright 2013-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,6 @@ require "chef/server_api" describe "knife serve", :workstation do include IntegrationSupport include KnifeSupport - include AppServerSupport def with_knife_serve exception = nil diff --git a/spec/integration/recipes/unified_mode_spec.rb b/spec/integration/recipes/unified_mode_spec.rb new file mode 100644 index 0000000000..944319f7bf --- /dev/null +++ b/spec/integration/recipes/unified_mode_spec.rb @@ -0,0 +1,876 @@ +require "support/shared/integration/integration_helper" +require "chef/mixin/shell_out" + +describe "Unified Mode" do + include IntegrationSupport + include Chef::Mixin::ShellOut + + let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) } + + let(:chef_client) { "bundle exec chef-client --minimal-ohai" } + + when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the second block to the first block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + notifies :run, "ruby_block[first block]", :delayed + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the "second block" runs first after "bar" is set + expect(result.stdout).to include("second: bar") + # then the "first block" runs after "baz" in the delayed phase + expect(result.stdout).to include("first: baz") + # nothing else should fire + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with a delayed notification from the first block to the second block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :delayed + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir) + # the first block should fire first + expect(result.stdout).to include("first: foo") + # the second block should fire in delayed phase + expect(result.stdout).to include("second: baz") + # nothing else should fire + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: bar") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the second block to the first block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + notifies :run, "ruby_block[first block]", :immediate + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the second resource should fire first when it is parsed + expect(result.stdout).to include("second: bar") + # the first resource should then immediately fire + expect(result.stdout).to include("first: bar") + # no other resources should fire + expect(result.stdout).not_to include("second: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("first: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to the second block" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :immediate + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir) + # both blocks should run when they're declared + expect(result.stdout).to include("first: foo") + expect(result.stdout).to include("second: bar") + # nothing else should run + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified_mode resource with an immediate notification from the first block to a block that does not exist" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :immediate + end + var = "bar" + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should fail the run" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # both blocks should run when they're declared + expect(result.stdout).to include("first: foo") + # nothing else should run + expect(result.stdout).not_to include("second: bar") + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound") + expect(result.error?).to be true + end + end + + when_the_repository "has a cookbook with a normal resource with an delayed notification with global resource unified mode on" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + resource_name :unified_mode + provides :unified_mode + + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :delayed + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + resource_unified_mode_default true + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the "first block" resource runs before the assignment to baz in compile time + expect(result.stdout).to include("first: bar") + # we should not run the "first block" at compile time + expect(result.stdout).not_to include("first: baz") + # (and certainly should run it this early) + expect(result.stdout).not_to include("first: foo") + # the delayed notification should still fire and run after everything else + expect(result.stdout).to include("second: baz") + # the action :nothing should suppress any other running of the second block + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: bar") + result.error! + end + end + + when_the_repository "has a cookbook with a normal resource with an immediate notification with global resource unified mode on" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + action :nothing + end + var = "bar" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + notifies :run, "ruby_block[second block]", :immediate + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + resource_unified_mode_default true + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the "first block" resource runs before the assignment to baz in compile time + expect(result.stdout).to include("first: bar") + # we should not run the "first block" at compile time + expect(result.stdout).not_to include("first: baz") + # (and certainly should run it this early) + expect(result.stdout).not_to include("first: foo") + # the immediate notifiation fires immediately + expect(result.stdout).to include("second: bar") + # the action :nothing should suppress any other running of the second block + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the second resource to the first" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + subscribes :run, "ruby_block[first block]", :immediate + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the first resource fires + expect(result.stdout).to include("first: foo") + # the second resource fires when it is parsed + expect(result.stdout).to include("second: bar") + # no other actions should run + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an immediate subscribes from the first resource to the second" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + subscribes :run, "ruby_block[second block]", :immediate + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the second resource fires first after bar is set + expect(result.stdout).to include("second: bar") + # the first resource then has its immediate subscribes fire at that location + expect(result.stdout).to include("first: bar") + # no other actions should run + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the second resource to the first" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + subscribes :run, "ruby_block[first block]", :delayed + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the first resource fires as it is parsed + expect(result.stdout).to include("first: foo") + # the second resource then fires in the delayed notifications phase + expect(result.stdout).to include("second: baz") + # no other actions should run + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("first: baz") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: bar") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with an delayed subscribes from the first resource to the second" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + subscribes :run, "ruby_block[second block]", :delayed + action :nothing + end + var = "bar" + ruby_block "second block" do + block do + puts "\nsecond: \#\{var\}" + end + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # the second resource fires first after bar is set + expect(result.stdout).to include("second: bar") + # the first resource then fires in the delayed notifications phase + expect(result.stdout).to include("first: baz") + # no other actions should run + expect(result.stdout).not_to include("first: foo") + expect(result.stdout).not_to include("first: bar") + expect(result.stdout).not_to include("second: foo") + expect(result.stdout).not_to include("second: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with a correct before notification" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + action :nothing + end + var = "bar" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + notifies :run, "ruby_block[notified block]", :before + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + expect(result.stdout.scan(/notifying: bar/).length).to eql(2) + expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block") + expect(result.stdout).to include("notified: bar") + # no other actions should run + expect(result.stdout).not_to include("notified: foo") + expect(result.stdout).not_to include("notified: baz") + expect(result.stdout).not_to include("notifying: foo") + expect(result.stdout).not_to include("notifying: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with a correct before subscribes" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before + action :nothing + end + var = "bar" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should complete with success" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + expect(result.stdout.scan(/notifying: bar/).length).to eql(2) + expect(result.stdout).to include("Would execute the whyrun_safe_ruby_block notifying block") + expect(result.stdout).to include("notified: bar") + # no other actions should run + expect(result.stdout).not_to include("notified: foo") + expect(result.stdout).not_to include("notified: baz") + expect(result.stdout).not_to include("notifying: foo") + expect(result.stdout).not_to include("notifying: baz") + result.error! + end + end + + when_the_repository "has a cookbook with a unified resource with a broken/reversed before notification" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + notifies :run, "ruby_block[notified block]", :before + end + var = "bar" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should fail the run" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default' -l debug", cwd: chef_dir) + # this doesn't work and we can't tell the difference between it and if we were trying to do a correct :before notification but typo'd the name + # so Chef::Exceptions::ResourceNotFound is the best we can do + expect(result.stdout).to include("Chef::Exceptions::ResourceNotFound") + expect(result.error?).to be true + end + end + + when_the_repository "has a cookbook with a unified resource with a broken/reversed before subscribes" do + before do + directory "cookbooks/x" do + + file "resources/unified_mode.rb", <<-EOM + unified_mode true + resource_name :unified_mode + provides :unified_mode + action :doit do + klass = new_resource.class + var = "foo" + whyrun_safe_ruby_block "notifying block" do + block do + puts "\nnotifying: \#\{var\}" + end + end + var = "bar" + ruby_block "notified block" do + block do + puts "\nnotified: \#\{var\}" + end + subscribes :run, "whyrun_safe_ruby_block[notifying block]", :before + action :nothing + end + var = "baz" + end + EOM + + file "recipes/default.rb", <<-EOM + unified_mode "whatever" + EOM + + end # directory 'cookbooks/x' + end + + it "should fail the run" do + file "config/client.rb", <<~EOM + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # this fires first normally before the error + expect(result.stdout).to include("notifying: foo") + # everything else does not run + expect(result.stdout).not_to include("notified: foo") + expect(result.stdout).not_to include("notified: bar") + expect(result.stdout).not_to include("notified: baz") + expect(result.stdout).not_to include("notifying: bar") + expect(result.stdout).not_to include("notifying: baz") + expect(result.stdout).to include("Chef::Exceptions::UnifiedModeBeforeSubscriptionEarlierResource") + expect(result.error?).to be true + end + end + + when_the_repository "has global resource unified mode on" do + before do + directory "cookbooks/x" do + + file "recipes/default.rb", <<-EOM + var = "foo" + ruby_block "first block" do + block do + puts "\nfirst: \#\{var\}" + end + end + var = "bar" + EOM + + end # directory 'cookbooks/x' + end + + it "recipes should still have a compile/converge mode" do + file "config/client.rb", <<~EOM + resource_unified_mode_default true + local_mode true + cookbook_path "#{path_to("cookbooks")}" + log_level :warn + EOM + + result = shell_out("#{chef_client} -c \"#{path_to("config/client.rb")}\" --no-color -F doc -o 'x::default'", cwd: chef_dir) + # in recipe mode we should still run normally with a compile/converge mode + expect(result.stdout).to include("first: bar") + expect(result.stdout).not_to include("first: foo") + result.error! + end + end +end diff --git a/spec/support/shared/integration/app_server_support.rb b/spec/support/shared/integration/app_server_support.rb deleted file mode 100644 index 317a5a2679..0000000000 --- a/spec/support/shared/integration/app_server_support.rb +++ /dev/null @@ -1,39 +0,0 @@ -# -# Author:: John Keiser (<jkeiser@chef.io>) -# Author:: Ho-Sheng Hsiao (<hosh@chef.io>) -# Copyright:: Copyright 2012-2018, 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 "rack" -require "stringio" - -module AppServerSupport - def start_app_server(app, port) - server = nil - thread = Thread.new do - Rack::Handler::WEBrick.run(app, - Port: 9018, - AccessLog: [], - Logger: WEBrick::Log.new(StringIO.new, 7)) do |found_server| - server = found_server - end - end - Timeout.timeout(30) do - sleep(0.01) until server && server.status == :Running - end - thread - end -end diff --git a/spec/support/shared/integration/integration_helper.rb b/spec/support/shared/integration/integration_helper.rb index 6c0eca98be..f0dfd1dc43 100644 --- a/spec/support/shared/integration/integration_helper.rb +++ b/spec/support/shared/integration/integration_helper.rb @@ -1,7 +1,7 @@ # # Author:: John Keiser (<jkeiser@chef.io>) # Author:: Ho-Sheng Hsiao (<hosh@chef.io>) -# Copyright:: Copyright 2012-2016, Chef Software Inc. +# Copyright:: Copyright 2012-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,7 +23,6 @@ require "chef/config" require "chef/json_compat" require "chef/server_api" require "support/shared/integration/knife_support" -require "support/shared/integration/app_server_support" require "cheffish/rspec/chef_run_support" require "spec_helper" diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb index 0773fc70fd..d4ed403197 100644 --- a/spec/unit/application/client_spec.rb +++ b/spec/unit/application/client_spec.rb @@ -1,6 +1,6 @@ # Author:: AJ Christensen (<aj@junglist.gen.nz>) -# Copyright:: Copyright 2008-2018, Chef Software Inc. +# Copyright:: Copyright 2008-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -85,7 +85,6 @@ describe Chef::Application::Client, "reconfigure" do allow(app).to receive(:trap) allow(app).to receive(:configure_logging).and_return(true) - Chef::Config[:interval] = 10 Chef::Config[:once] = false @@ -162,7 +161,7 @@ describe Chef::Application::Client, "reconfigure" do it_behaves_like "sets the configuration", "--no-fork", client_fork: false end - context "with an interval" do + context "with an interval", :unix_only do it_behaves_like "sets the configuration", "--interval 1800", client_fork: true end @@ -322,7 +321,7 @@ describe Chef::Application::Client, "reconfigure" do Chef::Config[:splay] = nil end - it "should terminal with message when interval is given" do + it "should terminate with message when interval is given" do Chef::Config[:interval] = 600 allow(ChefConfig).to receive(:windows?).and_return(false) expect(Chef::Application).to receive(:fatal!).with( @@ -340,8 +339,8 @@ Enable .* interval runs by setting `:client_fork = true` in your config file or allow(ChefConfig).to receive(:windows?).and_return(true) end - it "should not terminate" do - expect(Chef::Application).not_to receive(:fatal!) + it "should terminate" do + expect(Chef::Application).to receive(:fatal!) app.reconfigure end end diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb index 3f540d24e2..b70f959ab5 100644 --- a/spec/unit/application/solo_spec.rb +++ b/spec/unit/application/solo_spec.rb @@ -1,6 +1,6 @@ # # Author:: AJ Christensen (<aj@junglist.gen.nz>) -# Copyright:: Copyright 2008-2018, Chef Software Inc. +# Copyright:: Copyright 2008-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -64,18 +64,13 @@ describe Chef::Application::Solo do end it "should terminate with message" do - expect(Chef::Application).to receive(:fatal!).with( - /Unforked .* interval runs are disabled by default\. -Configuration settings: - interval = 600 seconds -Enable .* interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options\./ - ) + expect(Chef::Application).to receive(:fatal!).with(/interval runs are (disabled|not supported)/) app.reconfigure end end end - describe "when in daemonized mode and no interval has been set" do + describe "when in daemonized mode and no interval has been set", :unix_only do before do Chef::Config[:daemonize] = true end diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index e94f9b74ae..7a787d1f3f 100644 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -1,7 +1,7 @@ # # Author:: AJ Christensen (<aj@junglist.gen.nz>) # Author:: Mark Mzyk (mmzyk@chef.io) -# Copyright:: Copyright 2008-2018, Chef Software Inc. +# Copyright:: Copyright 2008-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/spec/unit/provider/ifconfig_spec.rb b/spec/unit/provider/ifconfig_spec.rb index 5b1256300e..38561d6e49 100644 --- a/spec/unit/provider/ifconfig_spec.rb +++ b/spec/unit/provider/ifconfig_spec.rb @@ -94,6 +94,17 @@ describe Chef::Provider::Ifconfig do expect(@new_resource).not_to be_updated end + it "should add a bridge interface" do + allow(@provider).to receive(:load_current_resource) + @new_resource.device "br-1234" + command = "ifconfig br-1234 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500" + expect(@provider).to receive(:shell_out_compacted!).with(*command.split(" ")) + expect(@provider).to receive(:generate_config) + + @provider.run_action(:add) + expect(@new_resource).to be_updated + end + # We are not testing this case with the assumption that anyone writing the cookbook would not make a typo == lo # it "should add a blank command if the #{@new_resource.device} == lo" do # end |