Some Background
- I am using Vagrant and chef-zero; chef server is not part of my equation
- I have two roles:
base
andinfrastructure
, and two cookbooks:apt
andphp
. The purpose of thebase
role is to do some basic provisioning and package management and to clone a repository from github, etc. Theapt
cookbook has a recipe,update_upgrade.rb
which runs anexecute
resource to effectively callapt update
,apt upgrade
,apt-get autoremove
and so forth - The
infrastructure
role sets up PHP via thephp
cookbook. It'll include more later, but it serves as a good example of the need I have. - The
php
cookbook has two recipes:add_repository.rb
andinstall.rb
. Theadd_repository
recipe runs anexecute
resource to add theondrej/php
repository toapt
. Theinstall
recipe iterates over a list of packages in order to install php from the added repository. This is where the need for the utility comes into play.
The crux is, I need to run apt update
after I add the PHP repo and before I install its packages. What's the best way to achieve this?
What I've Tried
Initially, I had my add_repository
and install
resources combined into a single install
recipe and had planned to use include_recipe apt::update_upgrade
to insert my apt
cookbook's execute
resource into that recipe. However, after scouring the chef docs, and this handy article: Should I use include_recipe or add the recipe to run_list?, it seems that include_recipe
doesn't run immediately where it's placed. The suggestion in said article was to use a run list to dictate precise execution of recipes, and the chef docs do suggest that the recipes execute in the order they are listed.
This led me to break up the php resources into add_repository
and install
and to use the following role run list for infrastructure
:
"run_list": [
"recipe[php::add_repository]",
"recipe[apt::update_upgrade]",
"recipe[php::install]"
]
My php
cookbook's metadata.rb
has depends apt
, and Chef doesn't error out. However, it also doesn't trigger the update_upgrade
recipe after the add_repository
one, either. Instead, the install
recipe runs after add_repository
which causes an error.
This all leads me to assume that role run lists are also executed in stages similar to include_recipe
. Is this accurate?
My Conclusions
If these assumptions are accurate, then it seems the only sure way to update apt with the new repository is to duplicate my execute
resource from my apt
cookbook into my `php cookbook. The DRY-ist in me screams at this; hence the question of a utility recipe/resource.
EDIT
Using the apt
Resource
As the original answerer indicates, for this specifically-described use case is to use a combination of apt_package
, apt_update
, and apt_repository
. Here's how I've set it up to get what I want:
In the afore-mentioned apt
cookbook, I now have two recipes, install.rb
and update.rb
. The key recipe is update
:
apt_update 'update_packages' do
action :update
end
According to the documentation (https://docs.chef.io/resource_apt_update.html), the :update
action will update the Apt repository at the start of a Chef Infra Client run. This is useful for me, as I want to update my OS packages when my VM is first spun up.
Here's my base
role's run list which makes use of the apt::update
recipe:
"run_list": [
"recipe[apt::install]",
"recipe[apt::update]",
...
]
Next, PHP. First, I want to add the ondrej/php
repository, then loop through a list of packages and install them. Lastly, I want to run apt update
after installing the new packages.
The first thing I do is to ensure, again, that my php
cookbook's metadata.rb
has:
depends 'apt'
which will allow me to reuse the apt_update['update_packages'] resource I defined in my apt
cookbook.
My install.rb
recipe in my php
cookbook now contains the following:
# Add PHP repository
apt_repository 'php7' do
uri "ppa:#{node[:infrastructure][:php][:repository][:name]}"
action :add # I know this is unnecessary, but I like to be explicit
end
# Install PHP
node[:infrastructure][:php][:extensions][:list].each do |package|
apt_package package do
action :install
notifies :update, 'apt_update[update_packages]', :delayed
end
end
Since I loop through a list of packages stored in attributes, I notify the apt_update[update_packages]
resource after all of the php packages are installed with the :delayed
timer.
Finally, here's a look at the infrastructure
role which handles the PHP cookbook execution:
"run_list": [
"recipe[php::install]"
]
This explanation handles the above use case beautifully and now hopefully makes use of the appropriate Chef resources. There are, however, some additional Apt actions which aren't covered with resources and which may still require some specific execute
resources. Consider the recipes in this apt_cleanup cookbook which cover things like:
- Cleaning up apt-cache
- Purging leftovers from removed packages
- Removing old unused kernels (for a cleaner /boot)
- Removes dependencies dragged in by already deleted packages
Looking at those recipes, I see a collection of execute
resources, which implies that for these actions, the execute
approach may still be necessary. (Albeit within a cookbook).