Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you abort/end a Chef run?

Under certain conditions, I need to abort/end a Chef run with a non-zero status code, that will then propagate back through our deployment chain and eventually to Jenkins resulting in a big, fat red ball.

What is the best way to do this?

like image 601
Jordan Dea-Mattson Avatar asked Jan 12 '13 04:01

Jordan Dea-Mattson


People also ask

How do I stop chef client?

Stops and disables chef-client systemd unit: sudo systemctl stop chef-client and sudo systemctl disable chef-client. Overrides /usr/bin/chef-client with a shell script emitting the log message, its timestamp, and the user who logged it.

What is the command for chef agent?

On UNIX and UNIX-like operating systems this can be done by running the command as root. On Microsoft Windows this can be done by running the command prompt as an administrator.

What is chef infra client?

The chef client can be used in local mode to test cookbooks or in a single server setup, or it can be connected with a Chef Infra Server, a tool that can be used to centrally manage many clients on different VMs. It uses cookbooks to act as infrastructure as code and dictate the state of the system.


2 Answers

For the readers coming to this question and answer in the future that may not be familiar with Chef, a Chef run "converges" the node, or brings it in line with the policy declared in the recipe(s) it is running. This is also called "convergence." This has two phases, "compile" and "execute." The compile phase is when Chef evaluates ("compiles") the recipes' Ruby code, looking for resources to add to the Resource Collection. Once that is complete, it "executes" the actions for each resource to put it into the state desired. System commands are run, etc.

Erik Hollensbe wrote an excellent walk through of how this works in 2013.

Now, for the answer:

There are several ways to end a Chef run, or exit a Chef recipe, depending on how you want to go about it, since Chef recipes are Ruby code.

If your goal is to stop processing a recipe based on a condition, but continue with the rest of the run, then use the return Ruby keyword. For example:

file '/tmp/ponies' do   action :create end  return if platform?('windows')  package 'bunnies-and-flowers' do   action :install end 

We presume that if the system is Windows, it doesn't have a package manager that can install the bunnies-and-flowers package, so we return from whence we came.

If you wish to abort the Chef run entirely

Tl;dr: Use raise. It's the best practice to abort a Chef run in case of an error condition.

That said, chef-client exits if it encounters an unhandled exception anywhere in the run. For example, if a template resource can't find its source file, or if the user running chef-client doesn't have permission to do something like make a directory. This is why using raise also works to end a run.

Where you put raise matters. If you use it in a ruby_block resource, it will only raise during the execution phase in convergence. If you use it outside of a resource like the return example above, it will happen during the compile phase.

file '/tmp/ponies' do   action :create end  raise if platform?('windows')  package 'bunnies-and-flowers' do   action :install end 

Perhaps we do have a package manager on Windows, and we want this package installed. The raise will result in Chef fatally exiting and giving a stack trace.

In years past, another approach was to use Chef::Application.fatal! - as written by me in this answer. Times have changed and this is NOT RECOMMENDED. Do not do this anymore. If you're doing it, switch to raise and as mentioned, write your own exception handler if your needs are more complicated (see below).

More Graceful error handling

Since recipes are Ruby, you can also gracefully handle error conditions with a begin..rescue block.

begin   dater = data_bag_item(:basket, "flowers") rescue Net::HTTPServerException   # maybe some retry code here?   raise "Couldn't find flowers in the basket, need those to continue!" end 

data_bag_item makes an HTTP request for a data bag on the Chef Server, and will return a Net::HTTPServerException if there's a problem from the server (404 not found, 403 unauthorized, etc). We could possibly attempt to retry or do some other handling, and then fall back to raise.

Reporting Errors

Simply exiting and tossing a stack trace is fine if you're running Chef from the command-line. However, if you're running it in cron or as a daemon across a few, or even dozens or hundreds of machines, this isn't a great way to keep sanity when there's problems.

Enter Chef's report/exception handler feature. You can use a handler for your Chef runs. All report handlers are run at the end of a Chef run. Exception handlers are run at the end of an aborted Chef run. The status of the run is tracked, and can be checked in the handler, so you can write one that handles both kinds of run (successful/completed or unsuccessful/aborted).

The documentation tells you how to write one. It also includes a list of available open source handlers that you can use for a variety of services, including:

  • Email over SMTP
  • IRC
  • Graphite
  • HipChat

And many more.

like image 133
jtimberman Avatar answered Sep 24 '22 00:09

jtimberman


The recommended way to abort or edit a Chef run is to raise an exception. Here's an example:

ruby_block "some tricky operation" do   block do     OperationFoo     raise "Operation Foo Failed" if some_condition   end end 
like image 35
Jordan Dea-Mattson Avatar answered Sep 21 '22 00:09

Jordan Dea-Mattson