summaryrefslogtreecommitdiff
path: root/spec/integration/client/exit_code_spec.rb
blob: 4397426723bc4677180b6c7fe6eb1c977f9fe2ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

require "support/shared/integration/integration_helper"
require "chef/mixin/shell_out"
require "tiny_server"
require "tmpdir"
require "chef/platform"

describe "chef-client" do

  include IntegrationSupport
  include Chef::Mixin::ShellOut

  let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }

  # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
  # following constraints are satisfied:
  # * Windows: windows can only run batch scripts as bare executables. Rubygems
  # creates batch wrappers for installed gems, but we don't have batch wrappers
  # in the source tree.
  # * Other `chef-client` in PATH: A common case is running the tests on a
  # machine that has omnibus chef installed. In that case we need to ensure
  # we're running `chef-client` from the source tree and not the external one.
  # cf. CHEF-4914
  let(:chef_client) { "ruby '#{chef_dir}/chef-client' --no-fork --minimal-ohai" }

  let(:critical_env_vars) { %w{PATH RUBYOPT BUNDLE_GEMFILE GEM_PATH}.map { |o| "#{o}=#{ENV[o]}" } .join(" ") }

  when_the_repository "uses RFC 062 defined exit codes" do

    def setup_client_rb
      file "config/client.rb", <<EOM
local_mode true
cookbook_path "#{path_to('cookbooks')}"
EOM
    end

    def setup_client_rb_with_audit_mode
      file "config/client.rb", <<EOM
local_mode true
cookbook_path "#{path_to('cookbooks')}"
audit_mode :audit_only
EOM
    end

    def run_chef_client_and_expect_exit_code(exit_code)
      shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'",
        :cwd => chef_dir,
        :returns => [exit_code])
    end

    context "has a cookbook" do
      context "with a library" do
        context "which cannot be loaded" do
          before do
            file "cookbooks/x/recipes/default.rb", ""
            file "cookbooks/x/libraries/error.rb", "require 'does/not/exist'"
          end

          it "exits with GENERIC_FAILURE, 1" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 1
          end
        end
      end

      context "with an audit recipe" do
        context "which fails" do
          before do
            file "cookbooks/x/recipes/default.rb", <<-RECIPE
control_group "control group without top level control" do
  it "should fail" do
    expect(4 - 4).to eq(1)
  end
end
RECIPE
          end

          it "exits with AUDIT_MODE_FAILURE, 42" do
            setup_client_rb_with_audit_mode
            run_chef_client_and_expect_exit_code 42
          end
        end
      end

      context "with a recipe" do
        context "which throws an error" do
          before { file "cookbooks/x/recipes/default.rb", "raise 'BOOM'" }

          it "exits with GENERIC_FAILURE, 1" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 1
          end
        end

        context "with a recipe which calls Chef::Application.fatal with a non-RFC exit code" do
          before { file "cookbooks/x/recipes/default.rb", "Chef::Application.fatal!('BOOM', 123)" }

          it "exits with the GENERIC_FAILURE exit code, 1" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 1
          end
        end

        context "with a recipe which calls Chef::Application.exit with a non-RFC exit code" do
          before { file "cookbooks/x/recipes/default.rb", "Chef::Application.exit!('BOOM', 231)" }

          it "exits with the GENERIC_FAILURE exit code, 1" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 1
          end
        end

        context "when a reboot exception is raised (like from the reboot resource)" do
          before do
            file "cookbooks/x/recipes/default.rb", <<EOM
raise Chef::Exceptions::Reboot.new
EOM
          end

          it "exits with REBOOT_SCHEDULED, 35" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 35
          end
        end

        context "when an attempt to reboot fails (like from the reboot resource)" do
          before do
            file "cookbooks/x/recipes/default.rb", <<EOM
raise Chef::Exceptions::RebootFailed.new
EOM
          end

          it "exits with REBOOT_FAILED, 41" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 41
          end
        end
      end
    end
  end
end