0

Some Background

  1. I am using Vagrant and chef-zero; chef server is not part of my equation
  2. I have two roles: base and infrastructure, and two cookbooks: apt and php. The purpose of the base role is to do some basic provisioning and package management and to clone a repository from github, etc. The apt cookbook has a recipe, update_upgrade.rb which runs an execute resource to effectively call apt update, apt upgrade, apt-get autoremove and so forth
  3. The infrastructure role sets up PHP via the php cookbook. It'll include more later, but it serves as a good example of the need I have.
  4. The php cookbook has two recipes: add_repository.rb and install.rb. The add_repository recipe runs an execute resource to add the ondrej/php repository to apt. The install 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).

Steve K
  • 387
  • 1
  • 8
  • 22

1 Answers1

0

the best practice is to use the appropriate chef resource in conjunction with notification.

since you are using apt, use the appropriate apt resources, such as apt_update (to make sure apt repositories are up to date), apt_package (to manipulate packages using apt, apt_repository (to add apt respository) and try to avoid using execute resource to do all of that - this will take advantage of chef idompotance (you might like reading Thinking Like a Chef)

each chef resource, can notify and\or subscribe to notifications. but you might not need to use it if you write the resources in order.

if you these advises won't lead you the right way, please post a relevant snippet where you have an issue and i will try to help you farther.

UPDATE

since apt suffers from many failures during update, i would give it few retries and also a periodic update.

apt_update 'update_packages' do
  retries 3
  action [:update, :periodic]
end

depending of the chef-client version that you are using, apt resouce has been bundles into chef standard resource collection, rather than using the apt cookbook.

so you might not need to specify the dependency on apt cookbook in the metadata.rb file. also, make sure you have no collision between the builtin apt and the apt cookbook functionality.

if you like all your nodes to have the apt_update step at boot time, then place the recipe which utilizes apt_update at your base chef role and make sure that each node runs this recipe (by running the base role) first in the node run-list.

when using apt_repository you did not specify any options for the resource, like arch and others. make sure that this is what you want.

when php packages are being installed using apt_package, there is no need to update apt repositories again. thus, there is no need to notify apt_update each time apt_package is invoked.

Mr.
  • 9,429
  • 13
  • 58
  • 82
  • Thanks for your helpful answer! I've updated the question with additional findings based on my experimentation and would love any additional feedback you might have based on my additional findings and question at the end. – Steve K Jan 02 '20 at 20:43
  • Hugely helpful, thanks! Will mark as the accepted answer. One final point: The apt cookbook is my own wrapper cookbook. I thought I needed my own wrapper cookbook to be able to reference apt_update in other cookbooks which install packages. Your explanation clarifies the fact that I don't need to run apt_update when I run apt_package, so what I plan to do instead is to create a base cookbook to use for installing system packages I need and then to get rid of the apt_update recipe I had altogether as it's probably unnecessary. – Steve K Jan 03 '20 at 21:05
  • @SteveK: you will need to update apt repositories if you add a new repository or when you need the repositories to be up to date for smooth install (but not for each package installation) – Mr. Jan 04 '20 at 06:29