Puppet dependencies and run stages

I’m using Puppet to manage some apt repositories on Ubuntu and have had a dependency problem. I want to write the source configs before running apt-get update and I want to run that before installing any packages.  Otherwise, a manifests that tries to install a package from a custom repository will fail, either because the repository is not configured or the apt metadata hasn’t been retrieved yet.

Due to Puppet changes being idempotent, this is usually solvable by running puppet a few times (ew). Or you can do this properly by diligently setting all the dependencies for all of your packages on your apt-get update command, and having that depend on your source configs, but that’s pretty fiddly.

New versions of Puppet now have a feature called run stages that can be used to solve this problem. You put your source configs and apt update command in a run stage and tell puppet to run everything in that run stage before the other stages (there is an implicit run stage called main where everything goes by default).

This seems quite neat at first, but only the new parameterized classes can be set to be in certain run stages – so you end up putting things into a class just to put it into a run stage. It’s really not much better than being diligent with your dependencies for this problem – worse in many ways. (There is a long discussion about the implementation of run stages in the Puppet issue tracker that might help you understand the use case for them).

But there is a new relationship syntax to make setting dependencies much easier which I’m using to mass-depend all packages on my apt-get update command:

class apt {
  exec { "apt-update":
    command => "/usr/bin/apt-get update"
  }

  # Ensure apt is setup before running apt-get update
  Apt::Key <| |> -> Exec["apt-update"]
  Apt::Source <| |> -> Exec["apt-update"]

  # Ensure apt-get update has been run before installing any packages
  Exec["apt-update"] -> Package <| |>
}

Remember, Puppet is declarative – the dependencies set there get applied to all Apt::Key, Apt::Source and Package resources even if they’ve not yet been defined. (This example also assumes the only packages you’re defining are apt ones btw!)

In summary, run stages are hard and fiddly. You probably don’t need them. Learn how to use the new relationship syntax.

Comments

Thanks for this very helpful post, John. I’ve been typical in using a product without properly reading the documentation. This new relationship syntax, and use of collections, is one of many great additions to Puppet in the 2.x series. The problem with apt that you describe has been bugging me and others I know for quite some time!

Jurgen Lamsens says:

But is it correct, that with this setup, ‘apt-get update’ will be executed at every puppet run, and thus not only when a package is installed ?

Dom says:

Jurgen, I think you can fix that by making the exec “refreshonly” and making the key/source notify the exec (with ~>, rather than ordering).

lotyrin says:

You can do this and maintain compatibility with Puppet Exec[“apt-update”]
}

In configuration class:

before => Exec[“apt-update”],

lotyrin says:

Previous comment got eaten by a zealous markup filter:

You can do this and maintain compatibility with Puppet below 2.6 by extending Package to “require” your exec, and having your apt configuration classses (keys, sources, etc.) “before” your exec.


Package {
require => Exec["apt-update"]
}

#In configuration class:
...
before => Exec["apt-update"],
...

aubricus says:

Apt::Key and Apt::Source, what are these? What does Key and Source refer to?

Daniel Werner says:

aubricus, these are types defined in the puppetlabs/apt module:

http://forge.puppetlabs.com/puppetlabs/apt

Gary Wilson says:

One gotcha about this statement:

Exec[“apt-update”] -> Package

…seems to be that all virtual packages you’ve defined (from any manifest that gets loaded) will also be realized. So, if you have multilpe machines that realize some, but not all, of your defined virtual packages this would not be desirable.

Leave a Reply to Matthew Pontefract Cancel reply