Refactoring Your Recipe

Developed with
Start course
2h 12m

In this lesson, we will cover how to make your recipe ready for production. We will make the cookbook capable of running on either a Redhat or Debian based Linux distribution. We will also add code to ensure that the Apache service starts when the recipe runs.

We will start by verifying that our recipe is still working correctly. We will go back to the text editor and start refactoring. We will create a variable that will pass on a list of platform families, and then check if the node is running on one of these families.

We will review how the code changes affect the Chef outcome. We will make sure the service will be in a started state.

You will run through multiple code changes to check service resources, node states, and make the recipe more cross-distribution friendly.

We will execute Kitchen to specify a different OS. You will run “kitchen converge” to execute the runlist against Ubuntu and CentOS, thus meeting the objectives for this lesson.

Finally, we will provide a quick recap of the lesson to see what you have retained.


Welcome back! In the previous lesson I create a chef-repo and a cookbook. After that I added some code to the default recipe. However that code needs some work before it’s ready for production. So that’s what I’ll cover in this lesson. I’ll make the cookbook capable of running on either a Redhat or Debian based Linux distribution. I’ll also add some code to ensure that the apache service is started whenever the recipe runs.

So there’s a lot of work to do. Let’s get started by running the recipe and making sure everything is still working correctly. It ran pretty fast this time because Chef didn’t need to make any changes to make the current state match the desired state. Everything is listed as being “up-to-date”

I’m going to go back to my text editor and start refactoring. I’ll create a variable named apache and it’s going to be set to the results of the “value_for_platform_family” method. This method allows me to pass in a list of platform families, and then the value that I want to use if the node is running on one of these families.
So, I’ll set Redhat, Fedora and Suse to all return “httpd”. If the platform is Debian based such as the Ubuntu VM I’m using, I’ll return “apache2”. So since I only have the one platform here, I don’t need to pass in a list, I can just use a string.

Now, when this code runs on a node, it checks the results from Ohai, and depending on the platform family it will either return “httpd” or “apache2.” And if the node’s platform doesn’t match any of those then the value will be nil.

Okay, now I can change the resource from apt_package to use the package resource. The package resource is smart enough to figure out which package manager to use for the platform the code is being run on. And in place of the name I’ll pass in the “apache” variable. Also, since the default action for the package resource is install, I can remove the block.

So I’ve added a bit of code, and removed some. And I can test this code by running it. What do you think will happen when I run this?
Well, if you said something along the lines of, Chef will report that everything is up-to-date, then you’re right.

Even though the code changed, the desired state didn’t change. I’m still telling Chef that I want it to make sure the apache2 package is installed.

Awesome, with that working I can move on to make sure that the service will be in a started state, if it’s not already. You might be wondering why I’m adding this, since the service is already running. The idea with a configuration management tool such as Chef is to specify the desired state of the node; by having the desired state specified in code if for any reason the node drifts away from that state, by running your recipe on the node, it will be back to your desired state.
As an example, let’s say your co-worker connects into a node and stops the apache2 service manually. If you run the recipe as it stands currently, it’s not going to start the service back up.

I’m going to add the service resource and then I’ll set the name to the value in the apache variable; then I’ll open the block with a “do” and “end” and I’ll set the action to be a list. The values in the list are going to be the :enable and :start symbols.
Enable is going to make sure the service starts on boot up, and start makes sure that if the service isn’t running when Chef runs the recipe that Chef will start it up.
So by specifying the desired state of the node, you can ensure that by running Chef at anytime the node will converge to the desired state.

Now, my goal was to make this recipe more cross distribution friendly. By running this exclusively on an Ubuntu VM I can’t know if I was successful. This is where Kitchen proves itself invaluable, because I can add another platform to the platforms section of the .kitchen.yml file and this time I can specify a different OS. I’m going to paste in this code here, and it should look familiar since it is basically a copy of the Ubuntu platform. The difference is that this CentOS VM will bind to port 8082 on my Mac. With this in place, and the file saved, I’m ready to test out the changes.

By running “kitchen converge” it’ll execute the runlist against both platforms. Since the CentOS is a new platform, it’ll take a little while to download and prepare. However, once it’s done I can see if everything works on both platforms. So I’ll fast forward to when it’s complete.

There it is, in the browser on port 8081, which is the Ubuntu server. And if I switch tabs, and you can see it running on the CentOS VM. So, I’d call this a success!

Okay, with that done I’ve met all of the objectives for this lesson. Let’s see how well you remember what was covered with a couple questions.

The first question is: How did the package resource improve this recipe?
The package resources replaced the apt_package resources, and the package resources is able to run on multiple platforms without issue. And that makes the recipe more cross-platform friendly.

The next question is: Are you starting to see any patterns regarding creating recipes?
There are a lot of potential good answers here. So your answer may be different than mine, however one of the patterns I noticed when I started with Chef is that using resources to create recipes is a lot like using Lego blocks. If you’ve ever used Lego blocks, you know that by connecting the blocks together in different patterns you can build all kinds of cool things. And that’s what I saw with Chef. By combining resources together to form a recipe you can create something awesome.

Alright, in the next lesson I’m going to start writing the recipes for installing the rest of the LAMP stack and get a Python based application deployed.

So if you’re ready to keep learning, then let’s get started with the next lesson!

About the Author
Learning Paths

Ben Lambert is a software engineer and was previously the lead author for DevOps and Microsoft Azure training content at Cloud Academy. His courses and learning paths covered Cloud Ecosystem technologies such as DC/OS, configuration management tools, and containers. As a software engineer, Ben’s experience includes building highly available web and mobile apps. When he’s not building software, he’s hiking, camping, or creating video games.

Covered Topics