Recipes
Recipes are where all the magic happens with Chef; they are the secret sauce, the man behind the mask. They are the workhorses of configuring hosts with Chef. Recipes are scripts written in Ruby using Chef's DSL that contain the instructions to be executed on end hosts when the Chef client is run. Every time the client is executed on the end host, a few things happen:
- The end host makes a request to the Chef server saying, "I need to do some work".
- The Chef server looks at the requesting host's identity and determines:
- Which recipes need to be run and in what order (the run list)
- The computed configuration data for that host
- This information is passed back to the end host along with the necessary artifacts it needs (recipes, templates, and so on).
- The client then combines the configuration data with the recipes and begins to execute its run list.
Developing recipes
As a developer, you will be placing your recipes inside the recipes
directory of your cookbook. Each recipe is designed to perform a specific action or set of actions to achieve a goal such as provisioning accounts, installing and configuring a database server, custom software deployments, or just about any other action that you could perform on a server.
A key concept when developing recipes is that they should be idempotent. For those unfamiliar with the term, an idempotent operation is an operation that can be applied multiple times and have the same outcome. Consider the following recipe:
user "smith" do action :create system true comment "Agent Smith" uid 1000 gid "agents" home "/home/matrix" shell "/bin/zsh" end
One would expect, from looking at this recipe, that Chef should be able to execute it once, five times, or one thousand times, and it would have the same effect as the initial application of the recipe. There would not be five or one thousand users on the host with the login name smith
; there would be only one single user with the login name smith
. Also, in all the runs, it would be constructed with the same UID 1000, the same group name, and so on.
Similarly, given a particular state of the system and assuming nothing has changed in between runs, subsequent client executions should produce the same, consistent ending state. In short, the Chef client should be able to run two times in a row, and if the configuration has not been updated, the system should look exactly the same after the second run as after the first run.
Recipes use provided configuration data along with the current state of the host to determine the flow and actions taken by the script. The execution of a recipe will take the system from its initial state, Sintial, to its new state, Sfinal. Well-written recipes should be idempotent such that if they're executed immediately afterwards any number of times with no configuration or stat changes, then the system should go from Sfinal to the same Sfinal with no new changes to the system. This allows you to keep your systems in a consistent state at all times, assuming that nothing goes wrong during the execution of those operations; if something does go wrong, you should be able to revert to a previously known good state.