2

I want to have puppet run a specific command before performing any type of change. I am aware of the prerun_command option in the main puppet.conf, but this is not what I'm looking for. I want the command to only run if something is about to change, not on every puppet run.

Here's the scenario. Let's say I have a bunch of web servers behind a load balancer. I then want puppet to update the web site files. But in order to prevent issues where some files have been updated, but other files haven't, and the mixed versions causing problems, I want to take the server out of the load balancer pool.
I could write a script which when run will tell the load balancer to remove the box from the pool. Then puppet can do the change, and use postrun_command to put the box back in the pool once complete. But I need a way to run that script to remove the server from the pool.


The only solution I can think of is to keep 2 copies of the files on the box. One a staging copy, and when puppet updates that, use a notify action to trigger the removal script, and then copy from staging into the live location. But I was hoping for something a little more generic that would work on any change being performed (upgrading a package, restarting a service, creating a user, anything).

2
  • How about SVN/git pre-commit hook?
    – quanta
    Aug 31, 2012 at 3:26
  • @quanta I don't see how a pre-commit hook would help (even if the commit is happening on a puppet run machine, it's only one box, all the others in the pool wont run the hook. Plus if I'm using a puppet master, the repo wont even be on all the puppet clients), but it did give me an idea. Instead of running the removal script only when puppet is going to make a change, run the removal script if the puppet manifests have been updated since the last puppet run. It's not as guaranteed as running it before puppet makes a change, but it could be acceptable.
    – phemmer
    Aug 31, 2012 at 10:55

3 Answers 3

5

Puppet's main purpose is configuration management. That is to say, it will enforce the configuration(s) you declare. What you need (based on the scenario you outlined in your question) is command and control. As @Mike points out, fabric is one option. I personally like Marionette Collective, as it is a Puppet Labs product and plays nice with the Puppet internals (facts, etc).

1
  • +1 for MCollective. It is a definitive answer to OP's question.
    – daff
    Sep 13, 2012 at 19:17
4

Here's my 2 cents on it.. You can do this in puppet but I've always found deploying webapps is very messy with puppet. Sure it'll work but what happens when you need a fast revert. not going to happen with puppet.

I'd look into a deployment tool like fabric

http://docs.fabfile.org/en/1.4.3/index.html

It will allow you to run commands on all the webnodes to handle deployments. So right before you deploy you can stop the webserver for example.. then start it back up when the deployment is over.

EDIT

Here is a stripped down example of what you can do in puppet

So you have a file but need to exec a command before the file is changed?

file { "/path/to/file":
  ensure  => file,
  require  => Exec['command to run'],
}

So that will run a command before a file needs to be changed due to the require.

5
  • The web server was just one scenario. I want this to be applied to any change to any box in a pooled environment.
    – phemmer
    Aug 31, 2012 at 10:36
  • ok see my edit for a simple example.. just use a require in a file type to run a command before a change should be made.
    – Mike
    Aug 31, 2012 at 12:29
  • I thought about this, but would need the exec {} to only run when required. This means putting refreshonly on it. Will the refreshonly still trigger the exec through the require, or does refreshonly only work with notify and subscribe? I'll give it a shot in the next couple days when I have some time to go play around.
    – phemmer
    Sep 1, 2012 at 0:02
  • you can always do the notify in the file
    – Mike
    Sep 1, 2012 at 2:19
  • +1 for better method and working function in Puppet. Sep 13, 2012 at 18:58
2

After playing with several different methods of doing this, a wrapper script was my final solution.

The script is called instead of puppet agent whenever a run occurs.

  1. It sets a FACTER_ env variable which init.pp looks for. If init.pp doesn't find the variable, it fail()s with an error message.
  2. The script first runs puppet agent --noop --onetime --no-daemonize --verbose --color=false | grep -q ' (noop)$' (it's a little more intelligent than this as it also checks the puppet agent run for error codes. It also can't use --detailed-exitcodes due to bug 6322.)
  3. If the above matched the (noop)$, it removes the server from the load balancer and runs puppet again without the --noop, and with --detailed-exitcodes.
  4. If the run exits with a 0 or 2, the script runs a few additional checks to make sure all services are running properly and then puts the box back in the load balancer.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .