Separating the content from the desired state for clarity
In the last section we updated our two cookbooks to display information about our node. We added this content to the file resource in their respective recipes.
What if new changes are given to us for the website splash page. Each new addition we would need to return to this recipe and carefully paste the contents of the new html into the string value of the content attribute.
There are some things that we need to be careful of when working with double-quoted strings with Ruby. It is that double-quoted strings are terminated by double-quotes so if any of the text that we paste into this content field has double quotes it is going to have to be escaped.
With Ruby strings we can use the backslash character as an escape character. In this case if we would want to have a double-quote in a double-quoted string we would need to place a backslash before the double-quote.
So that also brings up an issue with continually pasting in text. We will also need to keep an eye out for backslash characters. Because backslash characters are now the escape character. If you want to literally represent a backslash you need to use two-backslashes.
So every time text is pasted into the string value of the content attribute we will need to find and replace all backslashes with double-backslashes and then replace all double-quotes with backslash double-quotes.
Also it is important to note that the file content may have some important formatting that can be easily overlooked when working with the content in a recipe file.
Besides that, if the size of the string value of the content field grows it will consume the recipe. Making it difficult to understand what is desired state and what is data.
To me this sounds like a bug waiting to happen.
Any process that requires me to manually copy and paste values and then remember to escape out characters in a particular order is likely going to lead to sadness later when I deploy this recipe to production.
More desireable is being able to store this data in another file. The file would be native to whatever format is required so it we wouldn't need to escape any common characters.
But we still need a way to insert node attributes. So really we need a native file format that allows us to escape out to ruby.
To solve this problem I think we need to read up on the file resource more or see if Chef provides alternatives.
Lets start from what we know. The file resource. Open the documentation and see what it says and see if it gives us an clue to finding alternatives.
The file resource documentation suggests a couple of alternatives to using the file resource: cookbook_file resource; template resource; and remote_file resource.
Lets start with the remote_file resource:
Reading the documentation for remote_file, it seems that remote_file is similar to file. Except remote_file is used to specify a file at a remote location that is copied to a specified file path on the system.
So we could define our index file or message-of-the-day file on a remote system. But that does not allow us to insert attributes about the node we are currently on.
Reading the documentation for cookbook_file, after the boiler-plate resource definition, it sounds as though a cookbook file is capable of ...
... allowing us to store a file within our cookbook and then have that file transfered to a specified file path on the system.
While it sounds like it allows us to write a file in its native format. It does not sound as though the ability exists to escape out to access the node object and dynamically populate data.
Lets explore templates. Reviewing the documentation it seems as though it shares some similarities to cookbook_files. A template can be placed in a particular directory within the cookbook and it will be delivered to a specified file path on the system.
The biggest difference is that it says templates can contain ruby expressions and statements. This sounds like what we wanted: A native file format with the ability to insert information about our node.
And if we look at the bottom section about "Using Templates" we see more information about what is required and how we can use them to escape out to execute ruby code.
What resource could be used in this situation?
What resource will allow us to insert our node data into the file that it copies to the target system?
Why is using the template resource the best choice in this situation?
So our objective is clear. We need to use a template resource and create a template and then somehow link them together.
Lets start with creating the actual template file and then we will update the recipe.
Remember that application Chef. The one that generated our cookbooks. Well it is able to generate cookbook components as well. Templates and files (for cookbook_files) are a few of the other things it can generate for us.
Lets use help to review the command again.
And lets ask for help about the generate subcommand.
Finally lets ask for help for generating templates.
The command requires two parameters. The path to where the cookbook is located and the name of the template to generate. There are some other additional options but these two seem like the most important.
So we want to use chef generate template to create a template in the apache cookbook found in the cookbooks-slash-apache directory and the file we want to create is named index-dot-h-t-m-l.
Well that is the first step. Now that the template exists. We are ready for us to define the content within the template file.
Now we need to understand what does ERB mean.
ERB template files are special files because they are the native file format we want to deploy but we are allowed to include special tags to execute ruby code to insert values or logically build the contents.
Here is an example of a text file that has several ERB tags defined in it.
Each ERB tag has a beginning tag and an ending tag. The beginning tag is a less-than sign followed by a percent sign. The closing tag is a percent sign followed by a greater-than sign.
These tags are used to execute ruby but the results are not displayed.
ERB supports additional tags, one of those is one that allows you to output some variable or some ruby code. Here the example is going to display that 50 plus 50 equals the result of ruby calculating 50 plus 50 and then displaying the result.
The starting tag is different. It has an equals sign. This means show the value stored in a variable or the result of some calculation.
I often refer to this opening tag that outputs the content as the Angry Squid. The less-than is its head, the percent sign as its eyes, and the equals sign its tenticles shooting away after blasting some ink.
With that in mind lets update the template with the current value of the file resource's content field. Copying this literally into the file does not work because we no longer have the ability to use string interpolation within this html file.
We are going to need to change string interpolation sequence with the ERB template syntax. And it seems for this content we want to display the output so we want to make sure that we are using ERB's angry squid opening tag.
The template is created and the contents are correctly defined. It is time to update the recipe.
Lets open the apache cookbook's recipe named 'server'.
We will want to remove the content attribute from the file resource. Because that content is now in the template. But only if we use a template resource.
So its time to change the file resource to a template resource so that it can use the template file that we have defined.
Last we need to specify a source attribute which contains that path to the template we generated. This path is relative starting from within the cookbook's template directory.
To visualize that with tree we can run it with a path that places us right at the templates directory. So the results will be relative paths from the point specified.
The default folder denotes that we want to use this file for all platforms.
And we see the filepath index-dot-h-t-m-l-dot-e-r-b.
Now we have the path to our template so we can update the the template resource's source attribute value.
We hopefully haven't changed the original goal of our recipe but we have made some changes.
So its time to use kitchen to verify the cookbook and use chef-client to apply the cookbook. If everything is working then update the patch number and commit the changes to version control.
Kitchen is a cookbook testing tool so we need to move into the cookbook's directory.
Then we run the kitchen test command. Addressing any issues if they show up.
When all the test pass, return to the home directory, so we can execute chef-client.
And we apply the apache cookbook's default recipe to the local system.
If everything converges correctly, then it is time to update the version number. I mentioned earlier its a patch fix.
We return to the cookbook directory. Add all the changed files and commit them with a message.
Its time to do that again. This time for the workstation cookbook.
Generate a template named motd, copy in the source attribute from the file resource, and then update it to use ERB tags.
Then come back to the recipe. Change it a template resource and then add a source attribute whose value is that partial path to the new template you created.
What questions can we help you answer?
Generally or specifically about resources, templates, ERB, and Angry Squids.