Creating Custom Resources for Your Cookbooks
Introduction
Like we saw in the previous post, Chef Cookbooks can also provide custom resources that we can use in a dependant cookbooks' recipes. These custom resources can also use other resources from arbitrary cookbooks, or resources builtin to Chef.
A custom resource file is a Ruby file, ending in *.rb, and its syntax is, for example: [1]
|
|
We will generally only have to worry about properties and actions. The load_current_value block can be used in more complex resources, to gather the current state of our resource, if it already exists, thus overriding any properties that we may want.
Development
Before starting to develop our custom resource we should create a regular cookbook, with the difference that our default recipe may not exist, and the recipes directory may not exist at all. This means that our resources cookbook has to have a proper metadata.rb file with proper versioning.
We should create a directory named resources, where our resource files reside. And very importantly remember that the resources made available by our cookbook are named after the cookbook's name and the resource's name. So, if our cookbook is named myapps and we have a resource file named superapp, to use the superapp resource we would call the myapps_superapp resource in the external cookbook.
Let us take as an example the nano text editor, an external Linux application that can be compiled manually and installed to arbitrary directories.
For this example we will call our resource file from_tar.rb and our cookbook pkg, so our directory structure should look something like:
pkg
├── Berksfile
├── metadata.rb
└── resources
└── from_tar.rb
Our Berksfile is as simple as it gets:
|
|
And so is our metadata.rb file:
|
|
For our resource we plan it first:
- install tar
- create extract directory
- fetch package
- extract package
- compile and install
Then we implement it:
|
|
Note that when we want to use a property value, instead of using the variable name directly we use the namespace new_resource to access them.
We can use this sequence not just for nano, but for any application that:
- comes packaged in a '.tar.gz' format
- requires execution of configure, and only that, to generate a Makefile
- its Makefile supports the install argument
This specific process is actually already implemented by the tar cookbook available at the supermarket.
Testing
Of course that now that we have written our custom resource we want to be able to test it.
Let us start by creating a test cookbook, inside our pkg resource cookbook, which will have a nano_install recipe that uses our resource.
First create the metadata.rb file:
|
|
Then, the recipes/nano_install.rb file:
|
|
Next we update our original Berksfile to contemplate our test cookbook:
|
|
And finally we create a .kitchen.yml file for us to use with kitchen and test our resource in a local VM, in this case using Vagrant.
(Note that you will need vagrant and virtualbox installed on your workstation)
|
|
So now our directory structure will now look like this:
pkg
├── Berksfile
├── .kitchen.yml
├── metadata.rb
├── resources
| └── from_tar.rb
└── test
└── cookbooks
└── test
├── metadata.rb
└── recipes
└── nano_install.rb
And we can now test it with kitchen:
~/pkg$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action Last Error
nano-ubuntu-1404 Vagrant ChefZero Busser Ssh <Not Created> <None>
nano-ubuntu-1604 Vagrant ChefZero Busser Ssh <Not Created> <None>
nano-ubuntu-1804 Vagrant ChefZero Busser Ssh <Not Created> <None>
nano-debian-8 Vagrant ChefZero Busser Ssh <Not Created> <None>
nano-debian-9 Vagrant ChefZero Busser Ssh <Not Created> <None>
nano-centos-6 Vagrant ChefZero Busser Ssh <Not Created> <None>
nano-centos-7 Vagrant ChefZero Busser Ssh <Not Created> <None>
~/pkg$ kitchen test debian-9
-----> Starting Kitchen (v1.24.0)
-----> Cleaning up any prior instances of <nano-debian-9>
-----> Destroying <nano-debian-9>...
Finished destroying <nano-debian-9> (0m0.00s).
-----> Testing <nano-debian-9>
-----> Creating <nano-debian-9>...
(...)
Thank you for installing Chef!
Transferring files to <nano-debian-9>
Starting Chef Client, version 14.11.21
Creating a new client identity for nano-debian-9 using the validator key.
resolving cookbooks for run list: ["test::nano_install"]
Synchronizing Cookbooks:
- test (0.1.0)
- pkg (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 1 resources
Recipe: test::nano_install
* pkg_from_tar[nano] action install
* apt_package[tar] action install (up to date)
* apt_package[gcc] action install (up to date)
* apt_package[libncursesw5-dev] action install
- install version 6.0+20161126-1+deb9u2 of package libncursesw5-dev
* directory[/tmp/nano-3.2] action create
- create new directory /tmp/nano-3.2
- change mode from '' to '0755'
- change owner from '' to 'root'
- change group from '' to 'root'
* remote_file[nano-3.2.tar.gz] action create
- create new file /tmp/nano-3.2/nano-3.2.tar.gz
- update content in file /tmp/nano-3.2/nano-3.2.tar.gz from none to ca6945
(new content is binary, diff output suppressed)
- change mode from '' to '0755'
* execute[unpack nano] action run
- execute tar xfz nano-3.2.tar.gz
* execute[compile and install] action run
- execute ./configure --quiet --prefix=/usr/local && make -s && make -s install
Running handlers:
Running handlers complete
Chef Client finished, 6/8 resources updated in 39 seconds
Downloading files from <nano-debian-9>
Finished converging <nano-debian-9> (1m3.59s).
-----> Setting up <nano-debian-9>...
Finished setting up <nano-debian-9> (0m0.00s).
-----> Verifying <nano-debian-9>...
Preparing files for transfer
Transferring files to <nano-debian-9>
Finished verifying <nano-debian-9> (0m0.00s).
-----> Destroying <nano-debian-9>...
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...
Vagrant instance <nano-debian-9> destroyed.
Finished destroying <nano-debian-9> (0m4.10s).
Finished testing <nano-debian-9> (2m8.26s).
-----> Kitchen is finished. (2m9.01s)
You can test all of the flavors by not passing any arguments after kitchen test.
You can also use kitchen converge instead of test, so that the VM is not destroyed after successful convergence, and you can see the state of the VM after applying the run list.
Conclusion
In this post we saw how to design and implement custom resources for Chef, from scratch. We also saw how to test said resources using kitchen and Vagrant.
A working cookbook representing what was reviewed in this post is available here
Feel free to post any questions, or point out any mistakes, in the comment box below.
Tags: linux devops chef configuration management ruby
Related Content
- Hitchhiker's Guide to Chef
- An Advanced Guide to Salt
- A Comprehensive Introduction to Salt
- Slurm in Ubuntu Clusters - Part 2
- Slurm in Ubuntu Clusters - Part 1