summaryrefslogtreecommitdiff
path: root/spec/integration/client/exit_code_spec.rb
blob: 30020f6a3f209bbba15c77e25a63f3c40bf1f917 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

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 "does not have exit_status configured" 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(2 - 2).to eq(1)
  end
end
RECIPE
          end

          it "exits with GENERIC_FAILURE, 1" do
            setup_client_rb_with_audit_mode
            run_chef_client_and_expect_exit_code 1
          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 specified exit code" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 123
          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 specified exit code" do
            setup_client_rb
            run_chef_client_and_expect_exit_code 231
          end
        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 GENERIC_FAILURE, 1" do
          setup_client_rb
          run_chef_client_and_expect_exit_code 1
        end
      end
    end
  end

  when_the_repository "does has exit_status enabled" do

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

    def setup_client_rb_with_audit_mode
      file "config/client.rb", <<EOM
local_mode true
cookbook_path "#{path_to('cookbooks')}"
exit_status :enabled
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