Deploying PHP applications: PEAR and composer resources for chef
This is something experimental I have been working on for our chef deployments. So the objective was/is to find a sane way to install PEAR packages and install dependencies with composer.
execute in chef recipes
In chef recipes, almost everything is a resource. In case you're just getting started with Chef, a list of current resources is available on the Opscode Wiki. It's a link I put in my browser bar since I frequently work on chef recipes.
Some examples for resources are:
- package (to install software)
- cron (setup a crontab)
- directory (create directories)
- template (install customized configuration files)
- user and group (to create users and groups)
- mdadm (to setup a RAID)
The above list are examples - so there is more. But if there isn't a designated resource, you can always use an execute block.
An example for an execute block is the following:execute "discover a pear channel" do command "pear channel-discover easybib.github.com/pear" end
This works pretty well, but it is also not very robust.
By default whenever a command fails, chef fails hard.
To illustrate what I'm talking about, let's test and execute the command from our execute block multiple times on the shell to see its exit status ($?):till:~/ $ pear channel-discover easybib.github.com/pear Adding Channel "easybib.github.com/pear" succeeded Discovery of channel "easybib.github.com/pear" succeeded till:~/ $ echo $? 0 till:~/ $ pear channel-discover easybib.github.com/pear Channel "easybib.github.com/pear" is already initialized till:~/ $ echo $? 1
So whenever a command returns not 0, chef will bail.
One solution is to brute-force your way through these things with ignore_failure true in your execute block. But that's usually not a great idea either because it hides other issues from you (and me) when we need to debug this later on.
For example, if this PEAR channel is unavailable during your next chef-run, it would be much, much harder to find the root cause as of why the install commands failed.
Another solution is using the not_if or only_if options with execute:execute "discover a pear channel" do command "pear channel-discover easybib.github.com/pear" not_if do `pear channel-info easybib.github.com/pear` end end
If the command wrapped in not_if succeeds (success is exit status), we would skip the execute block.
Since I discovered not_if and only_if, it allows me write recipes which work in most cases. More robust code, which allows me to re-execute recipes on already running instances. So for example when I update a recipe or configuration file which is distributed through a recipe I can re-run the entire recipe and it will not fail but instead complete successfully.
One problem remains with this approach I end up doing the same checks again and again.... more after the jump.