Developing a Cookbook
In this chapter, we walk you through the process of creating a cookbook. The chapter covers all important aspects of developing cookbooks.
Developing Your First Cookbook
Chef uses cookbooks to configure systems the way the administrator wants them to be configured.
We will assume that you have a chef server, a workstation, and a node already configured.
Cookbooks are created on the workstation using knife and are then uploaded to the chef server using knife.
We demonstrate the creation of a basic nginx cookbook that installs the Nginx web server on our node.
Figure 9-1 demonstrates how we will proceed in this chapter.
Figure 9-1. Flow of the chapter
The general syntax for creating a cookbook is
knife cookbook create cookbook_name
Since we are creating an nginx cookbook, we will run the following command, as shown in Figure 9-2:
knife cookbook create nginx
Figure 9-2. Creating a cookbook
Any knife command has to be executed from a workstation only.
The command would create a list of directories that are necessary for a cookbook, as shown in Figure 9-3.
Figure 9-3. Cookbook Directory Structure
The cookbook will be created in the directory specified in the knife configuration file. By default the directory in Windows is c:/chef/cookbooks and in Linux is /var/chef/cookbooks.
We will start our cookbook with the biggest chunk of configuration (i.e., the recipe).
Go to the recipe subdirectory in the nginx cookbook directory. You will find a file named default.rb. This is the file that references the nginx recipe, and this is where we will add our code, as shown in Figure 9-4.
Figure 9-4. Listing the default recipe
Whenever you create a cookbook with knife it creates a default recipe. This recipe is executed whenever you add the cookbook to the run list of any node. Open the default.rb file in the text editor of your choice. There may be a commented header in the file as shown in Figure 9-5.
Figure 9-5. Content of a recipe
The first thing we need to make sure of is that we install the nginx package on the node. We can achieve this using the package resource as shown in Figure 9-6.
Figure 9-6. Adding a resource to our recipe
The code in Figure 9-6 will use the native functionality based upon the operating system and will install the nginx package on it. If nginx is already installed on the node, then chef agent will do nothing and it will not be reinstalled.
After installing the package, we would like to start the service and enable the service on startup. This can be achieved using the service resource. The piece of code in Figure 9-7 will help us in achieving that goal.
Figure 9-7. Adding another resource
We have included two items in the action. The enable action will enable the nginx service on startup, and the start action will start the nginx service after installing the package.
After making sure that the package is installed and the service is running properly, we need to create a file that will be hosted on our nginx server. We will create a file and will use a cookbook_file resource to distribute the file.
The code shown in Figure 9-8 displays this.
Figure 9-8. Adding the cookbook_file resource
We are done with the recipe writing. You can save and close your recipe. The final recipe should look like the one shown in Figure 9-9.
Figure 9-9. The Recipe
We have used the cookbook_file resource, which will look for the index.html file in the files directory of the cookbook. If the file is not present, then the cookbook will not be compiled properly, so we need to create the index file. Go to the nginx/files/default subdirectory and create the index file as shown in Figure 9-10.
Figure 9-10. Writing a recipe
This is an optional but recommended step in order to create a cookbook. The metadata file is present in the cookbook subdirectory. By default, it looks like the example shown in Figure 9-11. We can add or change this file as per the requirement. We can change the cookbook version and other things. Also, metadata can be used to provide any dependencies.
Figure 9-11. Changing the metadata
The next step would be to upload the cookbook to the chef server so that it can be deployed on any client. This can be easily done with the help of knife as shown in Figure 9-12.
knife cookbook upload nginx
Figure 9-12. Uploading the cookbook
Running the Cookbook
After the cookbook has been uploaded to the chef server, we need to add it to the run list of the node on which we want to deploy it. This can be done with the help of knife, as shown in Figure 9-13. These commands are executed from a workstation.
knife node run_list add node_name cookbook_name
Figure 9-13. Adding to run list
The next step would be to run the chef client on the node to see the cookbook execution (see Figure 9-14). Whenever we install chef client it automatically gets added to the path, and thus it can run from anywhere.
Figure 9-14. Running chef client
Figure 9-15. Chef client finished
To check whether your cookbook has been deployed successfully (see Figure 9-16), open a web browser and enter http://ipaddressofyourserver. You should be able to view the contents of the index page.
Figure 9-16. Checking whether cookbook is successfully deployed
Add an Attribute
Now we will add an attribute to our recipe. An attribute can be used to change the default settings on the node. We will change the listen port of nginx. By default, nginx services run on port 80. We will change the port to 82. To change the listen port, add an attribute in the default.rb file present in the attributes subdirectory as shown in Figure 9-17.
Figure 9-17. Adding an attribute
Add a Resource to the Default Recipe
The next step would be to add a template resource to our recipe which will help us in rendering the configuration file. A cookbook template is an Embedded Ruby (ERB) template that is used to generate files based on the variables and logic contained within the template. Templates may contain Ruby expressions and statements and are a great way to manage configuration files across an organization.
Add the template resource as shown in Figure 9-18.
Figure 9-18. Adding a template resource
This would render the default.conf.erb file present in templates/default subdirectory and copy it to /etc/nginx/conf.d/default.conf. We can manage the permissions of the file using the attributes available in the template resource. Refer to Chapter 7 for more on this topic.
We also need to add the template file that would be rendered. Create a file in the templates/default subdirectory as shown in Figure 9-19.
Figure 9-19. Adding a template file
At the time of rendering, the listen port will take from the attribute file by default. The attribute defined earlier is passed on to the template as shown in the code.
The attributes can also be provided in an environment or in roles, and they would be taken according to the precedence level.
Uploading and Running the Cookbook
Upload the cookbook again to the chef server so that the changes are updated. Run chef client again on the node to apply the changes (see Figures 9-20 and 9-21).
Figure 9-20. Testing cookbooks
Figure 9-21. Changing the configuration
Knife commands are executed from a workstation and chef client can run on any node that is under management of chef.
To test whether your changes have been applied, open a web browser and try opening http://ipaddressofyourserver. An error will come up.
Now try opening http://ipaddressofyourserver:82 and it will open the index page as shown in Figure 9-22.
Figure 9-22. Testing cookbook
Using Environments
An environment is a way to group our nodes; we can have environment-specific attributes. We can use environments to map the organization’s policies.
Chef creates a default environment and puts every node in it by default. We can modify the environment later on by editing the node object. An environment can be created using knife or the management console. This time, we will create the environment using the management console.
After a login to the management console, click the environments tab as shown in Figure 9-23.
Figure 9-23. Creating the environment
Click create to create a new environment. Provide a name for your environment and add some description to it (see Figure 9-24).
Figure 9-24. Creating an environment
We can add cookbook version constraints if we want to. We will keep it empty for now. This is used if we want certain configuration on nodes in a particular environment.
The next step is to add some attributes that will be overridden during the next chef run (see Figure 9-25).
Figure 9-25. Creating an environment
Click create environment to create the environment. After the environment has been created, we will edit it using knife and add an override attribute to override the listen port for nginx. This would open up the environment object located on the chef server in the EDITOR environment variable (see Figure 9-26).
Figure 9-26. Editing an environment
Save the file and it will be updated on the server. The file gets updated on the chef server database directly. The next step is to change the environment of our node so that the attributes are overridden. Go to the nodes tab in the management console and select the node you want to edit. We can also change a node’s environment using knife (see Figure 9-27).
Figure 9-27. Changing a node’s environment(1)
Click edit and select the environment from the dropdown as shown in Figure 9-28.
Figure 9-28. Changing a node’s environment(2)
Figure 9-29. Changing a node’s environment
Click the save node button to save the changes you made.
Now run chef client on the node to check whether the attribute is getting overridden. Run chef client on the node where we will deploy the nginx server (Figure 9-30).
Figure 9-30. Changing the configuration file
To test whether your changes have been applied, open a web browser and try opening http://ipaddressofyourserver:85. The index page should come up as shown in figure 9-31.
Figure 9-31. Testing the changes
We have demonstrated how environments can be used to manage our nodes. The implementation of environments is specific to an organization’s policies and processes. With chef, it becomes simple and easy to manage different environments. As an example, an organization may have Development, Test, QA, and production environments, each with different guidelines and policies. A single chef cookbook can be overridden to provide different attributes for different environments without changing the code in the cookbook. Thus, environments provide an easy mechanism to change or override the configuration based on the environment to which a node belongs.