diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2017-04-06 10:03:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-06 10:03:51 -0700 |
commit | b2b87c35ceb2d6e05c2be2f381b3e3dbe86a3dd1 (patch) | |
tree | b47c5f209041af5af5f704303005a8bb1386a65c | |
parent | c36bf3013e7c1f54efc6635145a5fb28daf17c7a (diff) | |
parent | 292a117c5796f1d7338b7632c6af77734ccf11a9 (diff) | |
download | chef-b2b87c35ceb2d6e05c2be2f381b3e3dbe86a3dd1.tar.gz |
Merge pull request #6014 from chef/lcg/shell-out-path
Chef-13: shell_out PATH fixes and path_sanity changes
-rw-r--r-- | RELEASE_NOTES.md | 35 | ||||
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 2 | ||||
-rw-r--r-- | kitchen-tests/Berksfile.lock | 8 | ||||
-rw-r--r-- | lib/chef/guard_interpreter/default_guard_interpreter.rb | 6 | ||||
-rw-r--r-- | lib/chef/mixin/path_sanity.rb | 36 | ||||
-rw-r--r-- | lib/chef/mixin/shell_out.rb | 40 | ||||
-rw-r--r-- | spec/unit/mixin/path_sanity_spec.rb | 14 | ||||
-rw-r--r-- | spec/unit/mixin/shell_out_spec.rb | 102 | ||||
-rw-r--r-- | spec/unit/resource/conditional_spec.rb | 4 |
9 files changed, 99 insertions, 148 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1f5b069d0c..ac49fd7edc 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -297,3 +297,38 @@ to the command line that invokes your daemon process. Redirecting output to a file with `chef-client > /tmp/chef.out` now captures the same output as invoking it directly on the command line with no redirection. + +### Path Sanity disabled by default and modified + +The chef client itself no long modifies its `ENV['PATH']` variable directly. When using the `shell_out` API now, in addition to +setting up LANG/LANGUAGE/LC_ALL variables that API will also inject certain system paths and the ruby bindir and gemdirs into +the PATH (or Path on Windows). The `shell_out_with_systems_locale` API still does not mangle any environment variables. During +the Chef-13 lifecycle changes will be made to prep Chef-14 to switch so that `shell_out` by default behaves like +`shell_out_with_systems_locale`. A new flag will get introduced to call `shell_out(..., internal: [true|false])` to either +get the forced locale and path settings ("internal") or not. When that is introduced in Chef 13.x the default will be `true` +(backwards-compat with 13.0) and that default will change in 14.0 to 'false'. + +The PATH changes have also been tweaked so that the ruby bindir and gemdir PATHS are prepended instead of appended to the PATH. +Some system directories are still appended. + +Some examples of changes: + +* `which ruby` in 12.x will return any system ruby and fall back to the embedded ruby if using omnibus +* `which ruby` in 13.x will return any system ruby and will not find the embedded ruby if using omnibus +* `shell_out_with_systems_locale("which ruby")` behaves the same as `which ruby` above +* `shell_out("which ruby")` in 12.x will return any system ruby and fall back to the embedded ruby if using omnibus +* `shell_out("which ruby")` in 13.x will always return the omnibus ruby first (but will find the system ruby if not using omnibus) + +The PATH in `shell_out` can also be overridden: + +* `shell_out("which ruby", env: { "PATH" => nil })` - behaves like shell_out_with_systems_locale() +* `shell_out("which ruby", env: { "PATH" => [...include PATH string here...] })` - set it arbitrarily however you need + +Since most providers which launch custom user commands use `shell_out_with_systems_locale` (service, execute, script, etc) the behavior +will be that those commands that used to be having embedded omnibus paths injected into them no longer will. Generally this will +fix more problems than it solves, but may causes issues for some use cases. + +### Default guard clauses (`not_if`/`only_if`) do not change the PATH or other env vars + +The implementation switched to `shell_out_with_systems_locale` to match `execute` resource, etc. + diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index c63098930b..0666dd869d 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -250,7 +250,7 @@ module ChefConfig default(:policy_path) { derive_path_from_chef_repo_path("policies") } # Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity - default :enforce_path_sanity, true + default :enforce_path_sanity, false # Formatted Chef Client output is a beta feature, disabled by default: default :formatter, "null" diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock index c9d533da60..3d6e8d15c0 100644 --- a/kitchen-tests/Berksfile.lock +++ b/kitchen-tests/Berksfile.lock @@ -26,7 +26,7 @@ GRAPH build-essential (8.0.0) mingw (>= 1.1) seven_zip (>= 0.0.0) - chef-client (7.2.1) + chef-client (8.0.0) cron (>= 1.7.0) logrotate (>= 1.9.0) windows (>= 1.42.0) @@ -36,8 +36,6 @@ GRAPH compat_resource (>= 0.0.0) iis (5.1.0) windows (>= 2.0) - inifile_chef_gem (0.1.0) - build-essential (>= 0.0.0) iptables (4.0.1) logrotate (2.1.0) compat_resource (>= 0.0.0) @@ -45,11 +43,11 @@ GRAPH seven_zip (>= 0.0.0) multipackage (4.0.0) compat_resource (>= 0.0.0) - mysql (8.3.0) + mysql (8.3.1) nscd (5.0.0) ntp (3.3.1) ohai (5.0.2) - openssh (2.1.1) + openssh (2.2.0) iptables (>= 1.0) php (1.5.0) build-essential (>= 0.0.0) diff --git a/lib/chef/guard_interpreter/default_guard_interpreter.rb b/lib/chef/guard_interpreter/default_guard_interpreter.rb index 449ca9a316..f93c0e04f0 100644 --- a/lib/chef/guard_interpreter/default_guard_interpreter.rb +++ b/lib/chef/guard_interpreter/default_guard_interpreter.rb @@ -1,6 +1,6 @@ # # Author:: Adam Edwards (<adamed@chef.io>) -# Copyright:: Copyright 2014-2016, Chef Software, Inc. +# Copyright:: Copyright 2014-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,7 +33,9 @@ class Chef public def evaluate - shell_out(@command, @command_opts).status.success? + shell_out_with_systems_locale(@command, @command_opts).status.success? + # Timeout fails command rather than chef-client run, see: + # https://tickets.opscode.com/browse/CHEF-2690 rescue Chef::Exceptions::CommandTimeout Chef::Log.warn "Command '#{@command}' timed out" false diff --git a/lib/chef/mixin/path_sanity.rb b/lib/chef/mixin/path_sanity.rb index 6a8e017bcd..c441d0770a 100644 --- a/lib/chef/mixin/path_sanity.rb +++ b/lib/chef/mixin/path_sanity.rb @@ -1,6 +1,6 @@ # # Author:: Seth Chisamore (<schisamo@chef.io>) -# Copyright:: Copyright 2011-2016, Chef Software Inc. +# Copyright:: Copyright 2011-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,25 +22,23 @@ class Chef def enforce_path_sanity(env = ENV) if Chef::Config[:enforce_path_sanity] - env["PATH"] = "" if env["PATH"].nil? - path_separator = Chef::Platform.windows? ? ";" : ":" - existing_paths = env["PATH"].split(path_separator) - # ensure the Ruby and Gem bindirs are included - # mainly for 'full-stack' Chef installs - paths_to_add = [] - paths_to_add << ruby_bindir unless sane_paths.include?(ruby_bindir) - paths_to_add << gem_bindir unless sane_paths.include?(gem_bindir) - paths_to_add << sane_paths if sane_paths - paths_to_add.flatten!.compact! - paths_to_add.each do |sane_path| - unless existing_paths.include?(sane_path) - env_path = env["PATH"].dup - env_path << path_separator unless env["PATH"].empty? - env_path << sane_path - env["PATH"] = env_path.encode("utf-8", invalid: :replace, undef: :replace) - end - end + env["PATH"] = sanitized_path(env) + end + end + + def sanitized_path(env = ENV) + env_path = env["PATH"].nil? ? "" : env["PATH"].dup + path_separator = Chef::Platform.windows? ? ";" : ":" + # ensure the Ruby and Gem bindirs are included + # mainly for 'full-stack' Chef installs + new_paths = env_path.split(path_separator) + [ ruby_bindir, gem_bindir ].compact.each do |path| + new_paths = [ path ] + new_paths unless new_paths.include?(path) + end + sane_paths.each do |path| + new_paths << path unless new_paths.include?(path) end + new_paths.join(path_separator).encode("utf-8", invalid: :replace, undef: :replace) end private diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb index 18bd067989..b6e4cffe92 100644 --- a/lib/chef/mixin/shell_out.rb +++ b/lib/chef/mixin/shell_out.rb @@ -16,11 +16,12 @@ # limitations under the License. require "mixlib/shellout" -require "chef/deprecated" +require "chef/mixin/path_sanity" class Chef module Mixin module ShellOut + include Chef::Mixin::PathSanity # PREFERRED APIS: # @@ -108,6 +109,7 @@ class Chef "LC_ALL" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], + env_path => sanitized_path, }.update(options[env_key] || {}) shell_out_command(*args, **options) end @@ -129,28 +131,6 @@ class Chef cmd end - DEPRECATED_OPTIONS = - [ [:command_log_level, :log_level], - [:command_log_prepend, :log_tag] ] - - # CHEF-3090: Deprecate command_log_level and command_log_prepend - # Patterned after https://github.com/chef/chef/commit/e1509990b559984b43e428d4d801c394e970f432 - def run_command_compatible_options(command_args) - return command_args unless command_args.last.is_a?(Hash) - - my_command_args = command_args.dup - my_options = my_command_args.last - - DEPRECATED_OPTIONS.each do |old_option, new_option| - # Edge case: someone specifies :command_log_level and 'command_log_level' in the option hash - next unless value = my_options.delete(old_option) || my_options.delete(old_option.to_s) - deprecate_option old_option, new_option - my_options[new_option] = value - end - - my_command_args - end - # Helper for sublcasses to convert an array of string args into a string. It # will compact nil or empty strings in the array and will join the array elements # with spaces, without introducing any double spaces for nil/empty elements. @@ -186,16 +166,12 @@ class Chef private def shell_out_command(*command_args) - cmd = Mixlib::ShellOut.new(*run_command_compatible_options(command_args)) + cmd = Mixlib::ShellOut.new(*command_args) cmd.live_stream ||= io_for_live_stream cmd.run_command cmd end - def deprecate_option(old_option, new_option) - Chef.deprecated :internal_api, "Chef::Mixin::ShellOut option :#{old_option} is deprecated. Use :#{new_option}" - end - def io_for_live_stream if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.debug? STDOUT @@ -203,6 +179,14 @@ class Chef nil end end + + def env_path + if Chef::Platform.windows? + "Path" + else + "PATH" + end + end end end end diff --git a/spec/unit/mixin/path_sanity_spec.rb b/spec/unit/mixin/path_sanity_spec.rb index 675b5722be..c9ed4e2595 100644 --- a/spec/unit/mixin/path_sanity_spec.rb +++ b/spec/unit/mixin/path_sanity_spec.rb @@ -1,6 +1,6 @@ # # Author:: Seth Chisamore (<schisamo@chef.io>) -# Copyright:: Copyright 2011-2016, Chef Software Inc. +# Copyright:: Copyright 2011-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,19 +41,19 @@ describe Chef::Mixin::PathSanity do it "adds all useful PATHs even if environment is an empty hash" do env = {} @sanity.enforce_path_sanity(env) - expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") + expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "adds all useful PATHs that are not yet in PATH to PATH" do env = { "PATH" => "" } @sanity.enforce_path_sanity(env) - expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") + expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "does not re-add paths that already exist in PATH" do env = { "PATH" => "/usr/bin:/sbin:/bin" } @sanity.enforce_path_sanity(env) - expect(env["PATH"]).to eq("/usr/bin:/sbin:/bin:#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin") + expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/bin:/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin") end it "creates path with utf-8 encoding" do @@ -65,7 +65,7 @@ describe Chef::Mixin::PathSanity do it "adds the current executing Ruby's bindir and Gem bindir to the PATH" do env = { "PATH" => "" } @sanity.enforce_path_sanity(env) - expect(env["PATH"]).to eq("#{@ruby_bindir}:#{@gem_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") + expect(env["PATH"]).to eq("#{@gem_bindir}:#{@ruby_bindir}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") end it "does not create entries for Ruby/Gem bindirs if they exist in SANE_PATH or PATH" do @@ -75,7 +75,7 @@ describe Chef::Mixin::PathSanity do allow(RbConfig::CONFIG).to receive(:[]).with("bindir").and_return(ruby_bindir) env = { "PATH" => gem_bindir } @sanity.enforce_path_sanity(env) - expect(env["PATH"]).to eq("/yo/gabba/gabba:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") + expect(env["PATH"]).to eq("/usr/bin:/yo/gabba/gabba:/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/bin") end it "builds a valid windows path" do @@ -86,7 +86,7 @@ describe Chef::Mixin::PathSanity do allow(ChefConfig).to receive(:windows?).and_return(true) env = { "PATH" => 'C:\Windows\system32;C:\mr\softie' } @sanity.enforce_path_sanity(env) - expect(env["PATH"]).to eq("C:\\Windows\\system32;C:\\mr\\softie;#{ruby_bindir};#{gem_bindir}") + expect(env["PATH"]).to eq("#{gem_bindir};#{ruby_bindir};C:\\Windows\\system32;C:\\mr\\softie") end end end diff --git a/spec/unit/mixin/shell_out_spec.rb b/spec/unit/mixin/shell_out_spec.rb index aa38639c1c..9b12969026 100644 --- a/spec/unit/mixin/shell_out_spec.rb +++ b/spec/unit/mixin/shell_out_spec.rb @@ -21,91 +21,19 @@ # require "spec_helper" +require "chef/mixin/path_sanity" describe Chef::Mixin::ShellOut do + include Chef::Mixin::PathSanity + let(:shell_out_class) { Class.new { include Chef::Mixin::ShellOut } } subject(:shell_out_obj) { shell_out_class.new } - describe "#run_command_compatible_options" do - subject { shell_out_obj.run_command_compatible_options(command_args) } - let(:command_args) { [ cmd, options ] } - let(:cmd) { "echo '#{rand(1000)}'" } - - let(:output) { StringIO.new } - let!(:capture_log_output) { Chef::Log.logger = Logger.new(output) } - let(:assume_deprecation_log_level) { allow(Chef::Log).to receive(:level).and_return(:warn) } - - before do - Chef::Config[:treat_deprecation_warnings_as_errors] = false - end - - context "without options" do - let(:command_args) { [ cmd ] } - - it "should not edit command args" do - is_expected.to eql(command_args) - end - end - - context "without deprecated options" do - let(:options) { { :environment => environment } } - let(:environment) { { "LC_ALL" => "C", "LANG" => "C", "LANGUAGE" => "C" } } - - it "should not edit command args" do - is_expected.to eql(command_args) - end - end - - def self.should_emit_deprecation_warning_about(old_option, new_option) - it "should emit a deprecation warning" do - assume_deprecation_log_level && capture_log_output - subject - expect(output.string).to match Regexp.escape(old_option.to_s) - expect(output.string).to match Regexp.escape(new_option.to_s) - end - end - - context "with :command_log_level option" do - let(:options) { { :command_log_level => command_log_level } } - let(:command_log_level) { :warn } - - it "should convert :command_log_level to :log_level" do - is_expected.to eql [ cmd, { :log_level => command_log_level } ] - end - - should_emit_deprecation_warning_about :command_log_level, :log_level - end - - context "with :command_log_prepend option" do - let(:options) { { :command_log_prepend => command_log_prepend } } - let(:command_log_prepend) { "PROVIDER:" } - - it "should convert :command_log_prepend to :log_tag" do - is_expected.to eql [ cmd, { :log_tag => command_log_prepend } ] - end - - should_emit_deprecation_warning_about :command_log_prepend, :log_tag - end - - context "with 'command_log_level' option" do - let(:options) { { "command_log_level" => command_log_level } } - let(:command_log_level) { :warn } - - it "should convert 'command_log_level' to :log_level" do - is_expected.to eql [ cmd, { :log_level => command_log_level } ] - end - - should_emit_deprecation_warning_about :command_log_level, :log_level - end - - context "with 'command_log_prepend' option" do - let(:options) { { "command_log_prepend" => command_log_prepend } } - let(:command_log_prepend) { "PROVIDER:" } - - it "should convert 'command_log_prepend' to :log_tag" do - is_expected.to eql [ cmd, { :log_tag => command_log_prepend } ] - end - should_emit_deprecation_warning_about :command_log_prepend, :log_tag + def env_path + if Chef::Platform.windows? + "Path" + else + "PATH" end end @@ -127,13 +55,13 @@ describe Chef::Mixin::ShellOut do describe "when the last argument is a Hash" do describe "and environment is an option" do it "should not change environment language settings when they are set to nil" do - options = { :environment => { "LC_ALL" => nil, "LANGUAGE" => nil, "LANG" => nil } } + options = { :environment => { "LC_ALL" => nil, "LANGUAGE" => nil, "LANG" => nil, env_path => nil } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should not change environment language settings when they are set to non-nil" do - options = { :environment => { "LC_ALL" => "en_US.UTF-8", "LANGUAGE" => "en_US.UTF-8", "LANG" => "en_US.UTF-8" } } + options = { :environment => { "LC_ALL" => "en_US.UTF-8", "LANGUAGE" => "en_US.UTF-8", "LANG" => "en_US.UTF-8", env_path => "foo:bar:baz" } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end @@ -146,6 +74,7 @@ describe Chef::Mixin::ShellOut do "LC_ALL" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], + env_path => sanitized_path, }, }).and_return(true) shell_out_obj.shell_out(cmd, options) @@ -159,6 +88,7 @@ describe Chef::Mixin::ShellOut do "LC_ALL" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], + env_path => sanitized_path, }, }).and_return(true) shell_out_obj.shell_out(cmd, options) @@ -168,13 +98,13 @@ describe Chef::Mixin::ShellOut do describe "and env is an option" do it "should not change env when langauge options are set to nil" do - options = { :env => { "LC_ALL" => nil, "LANG" => nil, "LANGUAGE" => nil } } + options = { :env => { "LC_ALL" => nil, "LANG" => nil, "LANGUAGE" => nil, env_path => nil } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end it "should not change env when language options are set to non-nil" do - options = { :env => { "LC_ALL" => "de_DE.UTF-8", "LANG" => "de_DE.UTF-8", "LANGUAGE" => "de_DE.UTF-8" } } + options = { :env => { "LC_ALL" => "de_DE.UTF-8", "LANG" => "de_DE.UTF-8", "LANGUAGE" => "de_DE.UTF-8", env_path => "foo:bar:baz" } } expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true) shell_out_obj.shell_out(cmd, options) end @@ -187,6 +117,7 @@ describe Chef::Mixin::ShellOut do "LC_ALL" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], + env_path => sanitized_path, }, }).and_return(true) shell_out_obj.shell_out(cmd, options) @@ -200,6 +131,7 @@ describe Chef::Mixin::ShellOut do "LC_ALL" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], + env_path => sanitized_path, }, }).and_return(true) shell_out_obj.shell_out(cmd, options) @@ -216,6 +148,7 @@ describe Chef::Mixin::ShellOut do "LC_ALL" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], + env_path => sanitized_path, }, }).and_return(true) shell_out_obj.shell_out(cmd, options) @@ -230,6 +163,7 @@ describe Chef::Mixin::ShellOut do "LC_ALL" => Chef::Config[:internal_locale], "LANG" => Chef::Config[:internal_locale], "LANGUAGE" => Chef::Config[:internal_locale], + env_path => sanitized_path, }, }).and_return(true) shell_out_obj.shell_out(cmd) diff --git a/spec/unit/resource/conditional_spec.rb b/spec/unit/resource/conditional_spec.rb index 135f798676..0219945936 100644 --- a/spec/unit/resource/conditional_spec.rb +++ b/spec/unit/resource/conditional_spec.rb @@ -92,7 +92,7 @@ describe Chef::Resource::Conditional do describe "after running a command which timed out" do before do @conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false") - allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out).and_raise(Chef::Exceptions::CommandTimeout) + allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out_with_systems_locale).and_raise(Chef::Exceptions::CommandTimeout) end it "indicates that resource convergence should not continue" do @@ -195,7 +195,7 @@ describe Chef::Resource::Conditional do describe "after running a command which timed out" do before do @conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false") - allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out).and_raise(Chef::Exceptions::CommandTimeout) + allow_any_instance_of(Chef::GuardInterpreter::DefaultGuardInterpreter).to receive(:shell_out_with_systems_locale).and_raise(Chef::Exceptions::CommandTimeout) end it "indicates that resource convergence should continue" do |