\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7 \8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh \00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€ \00 $\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95 59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9 C/// File Manager

File Manager

Path: /opt/chef/embedded/lib/ruby/gems/2.7.0/gems/chef-16.3.45/spec/unit/provider/

Viewing File: execute_spec.rb

#
# Author:: Prajakta Purohit (<prajakta@chef.io>)
# Copyright:: Copyright (c) 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 "spec_helper"

describe Chef::Provider::Execute do

  let(:node) { Chef::Node.new }
  let(:events) { Chef::EventDispatch::Dispatcher.new }
  let(:run_context) { Chef::RunContext.new(node, {}, events) }
  let(:provider) { Chef::Provider::Execute.new(new_resource, run_context) }
  let(:current_resource) { Chef::Resource::Ifconfig.new("foo_resource", run_context) }
  # You will be the same object, I promise.
  @live_stream = Chef::EventDispatch::EventsOutputStream.new(Chef::EventDispatch::Dispatcher.new, name: :execute)

  let(:opts) do
    {
      timeout: 3600,
      returns: 0,
      log_level: :info,
      default_env: false,
      log_tag: new_resource.to_s,
    }
  end

  let(:new_resource) { Chef::Resource::Execute.new("foo_resource", run_context) }

  before do
    allow(Chef::EventDispatch::EventsOutputStream).to receive(:new) { @live_stream }
    allow(ChefUtils).to receive(:windows?) { false }
    Chef::Log.level = :info
    allow(STDOUT).to receive(:tty?).and_return(false)
  end

  describe "#initialize" do
    it "should return a Chef::Provider::Execute provider" do
      expect(provider.class).to eql(Chef::Provider::Execute)
    end
  end

  describe "#load_current_resource" do
    before do
      expect(Chef::Resource::Execute).to receive(:new).with(new_resource.name).and_return(current_resource)
    end

    it "should return the current resource" do
      expect(provider.load_current_resource).to eql(current_resource)
    end

    it "our timeout should default to 3600" do
      provider.load_current_resource
      expect(provider.timeout).to eql(3600)
    end
  end

  describe "#action_run" do
    it "runs shell_out with the default options" do
      expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
      expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
      expect(Chef::Log).not_to receive(:warn)
      provider.run_action(:run)
      expect(new_resource).to be_updated
    end

    # this next test is tightly coupled to the implementation of the underlying shell_out mixin that we're using
    # but the point is to ensure that we are not picking up the PATH mangling and locale-variable mangling that the internal
    # shell_out API uses.  we are asserting that we emulate `ls -la` when the user does `execute "ls -la"`, and to
    # do that we get dirty and start mocking the implementation of the shell_out mixin itself.  while arguments like
    # "timeout", "returns", "log_level" and "log_tag" appear here, we MUST NOT have an "environment" or "env" argument
    # that we are passing to Mixlib::ShellOut by default -- ever.  you might have to add some other argument here from
    # time to time, but you MUST NOT change the environment.
    it "does not use shell_out in such a way as to insert extra environment variables" do
      mock = instance_double(Mixlib::ShellOut)
      expect(Mixlib::ShellOut).to receive(:new).with("foo_resource", { timeout: 3600, returns: 0, log_level: :info, log_tag: "execute[foo_resource]" }).and_return(mock)
      expect(mock).to receive(:live_stream=).with(nil)
      allow(mock).to receive(:live_stream)
      expect(mock).to receive(:run_command)
      expect(mock).to receive(:error!)
      provider.run_action(:run)
      expect(new_resource).to be_updated
    end

    it "if you pass a command property, it runs the command" do
      new_resource.command "/usr/argelbargle/bin/oogachacka 12345"
      expect(provider).to receive(:shell_out!).with(new_resource.command, opts)
      expect(provider).to receive(:converge_by).with("execute #{new_resource.command}").and_call_original
      expect(Chef::Log).not_to receive(:warn)
      provider.run_action(:run)
      expect(new_resource).to be_updated
    end

    it "should honor sensitive property" do
      new_resource.sensitive true
      # Since the resource is sensitive, it should not have :live_stream set
      opts.delete(:live_stream)
      expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
      expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
      expect(Chef::Log).not_to receive(:warn)
      provider.run_action(:run)
      expect(new_resource).to be_updated
    end

    it "should do nothing if the sentinel file exists" do
      new_resource.creates "/foo_resource"
      expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
      expect(provider).not_to receive(:shell_out!)
      expect(Chef::Log).not_to receive(:warn)
      provider.run_action(:run)
      expect(new_resource).not_to be_updated
    end

    describe "when the user specifies a relative path without cwd" do
      before do
        expect(new_resource.cwd).to be_falsey
        new_resource.creates "foo_resource"
      end

      it "should raise if user specified relative path without cwd for Chef-13" do
        expect(provider).not_to receive(:shell_out!)
        expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::Execute)
      end
    end

    it "should respect cwd options for 'creates'" do
      new_resource.cwd "/tmp"
      new_resource.creates "foo_resource"
      expect(FileTest).not_to receive(:exist?).with(new_resource.creates)
      expect(FileTest).to receive(:exist?).with(File.join("/tmp", new_resource.creates)).and_return(true)
      expect(Chef::Log).not_to receive(:warn)
      expect(provider).not_to receive(:shell_out!)

      provider.run_action(:run)
      expect(new_resource).not_to be_updated
    end

    it "should not include stdout/stderr in failure exception for sensitive resource" do
      opts.delete(:live_stream)
      new_resource.sensitive true
      expect(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
      expect do
        provider.run_action(:run)
      end.to raise_error(Mixlib::ShellOut::ShellCommandFailed, /suppressed for sensitive resource/)
    end

    describe "streaming output" do
      it "should not set the live_stream if sensitive is on" do
        new_resource.sensitive true
        expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
        expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
        expect(Chef::Log).not_to receive(:warn)
        provider.run_action(:run)
        expect(new_resource).to be_updated
      end

      describe "with an output formatter listening" do
        let(:events) { d = Chef::EventDispatch::Dispatcher.new; d.register(Chef::Formatters::Doc.new(StringIO.new, StringIO.new)); d }

        before do
          Chef::Config[:stream_execute_output] = true
        end

        it "should set the live_stream if the log level is info or above" do
          nopts = opts
          nopts[:live_stream] = @live_stream
          expect(provider).to receive(:shell_out!).with(new_resource.name, nopts)
          expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
          expect(Chef::Log).not_to receive(:warn)
          provider.run_action(:run)
          expect(new_resource).to be_updated
        end

        it "should set the live_stream if the resource requests live streaming" do
          Chef::Log.level = :warn
          new_resource.live_stream true
          nopts = opts
          nopts[:live_stream] = @live_stream
          expect(provider).to receive(:shell_out!).with(new_resource.name, nopts)
          expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
          expect(Chef::Log).not_to receive(:warn)
          provider.run_action(:run)
          expect(new_resource).to be_updated
        end

        it "should not set the live_stream if the resource is sensitive" do
          new_resource.sensitive true
          expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
          expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
          expect(Chef::Log).not_to receive(:warn)
          provider.run_action(:run)
          expect(new_resource).to be_updated
        end
      end

      describe "with only logging enabled" do
        it "should set the live_stream to STDOUT if we are a TTY, not daemonized, not sensitive, and info is enabled" do
          nopts = opts
          nopts[:live_stream] = STDOUT
          allow(STDOUT).to receive(:tty?).and_return(true)
          expect(provider).to receive(:shell_out!).with(new_resource.name, nopts)
          expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
          expect(Chef::Log).not_to receive(:warn)
          provider.run_action(:run)
          expect(new_resource).to be_updated
        end

        it "should not set the live_stream to STDOUT if we are a TTY, not daemonized, but sensitive" do
          new_resource.sensitive true
          allow(STDOUT).to receive(:tty?).and_return(true)
          expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
          expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
          expect(Chef::Log).not_to receive(:warn)
          provider.run_action(:run)
          expect(new_resource).to be_updated
        end

        it "should not set the live_stream to STDOUT if we are a TTY, but daemonized" do
          Chef::Config[:daemon] = true
          allow(STDOUT).to receive(:tty?).and_return(true)
          expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
          expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
          expect(Chef::Log).not_to receive(:warn)
          provider.run_action(:run)
          expect(new_resource).to be_updated
        end

      end
    end
  end
end