3
votes

First off, can (and is it good practice) chef run a recipe at a specified interval on a specific role?

I've got a ruby script which manages user accounts and ssh identities, it currently runs on a cron every hour and I'd like to turn it into a Chef recipe for obvious reasons (I want it to be there on all machines).

I can see two ways of doing this:

Either turn the script into a template, the recipe would simply render the template to a given path and then register a cronjob

OR

Break the script into resources, providers, etc., and have Chef run it every hour.

Ideas?

2

2 Answers

8
votes

You can run chef-client as a daemon (-d option, as used in init scripts), or under a service management tool like upstart, runit/daemontools or bluepill. You can certainly also launch it from cron - just make sure to not run daemon mode there :).

Chef's resource providers take idempotent actions to configure the resources to be in the desired state. This means that if Chef has already run on the system, it only modifies resources if they do not match what the recipe says. For example, if you have a recipe that says:

package "haproxy"

service "haproxy" do
  action [:enable, :start]
end

template "/etc/haproxy/haproxy.cfg" do
  source "haproxy.cfg.erb"
end

The package will be installed the first time chef runs and won't be modified again unless the package were removed from the system, or you modify the resource. Likewise, the haproxy service will be enabled (through your platform's service management tools, usually symlinks in /etc/rc*.d) and then started (e.g., via /etc/init.d/haproxy start). Finally only if the content of the template changes will Chef render a new version of the template. For templates it determines this based on a SHA256 checksum.

There are a few exceptions - execute, script and ruby_block resources are not idempotent without you providing some kind of qualifier conditional.

Also, Chef doesn't have "one time" or "one off" recipes run lists when using the server. There was a thread on the Chef mailing list recently about the topic.

2
votes

Both are feasible.
Your options you mentioned were:

1)

turn the script into a template, the recipe would simply render the template to a given path and then register a cronjob

This is easy to get started (no real change to your script, it just ensures it's there)

Remember that chef runs every recipe, every time.... As jtimberman said: "it only modifies resources if they do not match the recipe". So your recipe should just overwrite new template when it changes.

OR 2)

Break the script into resources, providers, etc., and have Chef run it every hour.

This option is more chef-like, and probably more reliable and scalable -- especially as you put more infrastructure under chef management.

It'll work great if your chef client is daemonized, or chef-solo is run on cron.

In this case you could setup a recipe using resources like the 'user', 'group' and 'file' (to copy ssh keys). See here for details: http://wiki.opscode.com/display/chef/Resources#Resources-File

Then, you're best bet is to use a 'data-bag' (json data) to store user details, and install your users based on that. It's exactly what opscode have done in this recipe (look in ./recipe/sysadmins.rb for inspiration): https://github.com/opscode/cookbooks/tree/master/users

Just be aware they are using chef-server (or opscode platform). If you're using chef-solo you'll need to replace 'search(:users, 'groups:sysadmin')' with your own data-bag file found somewhere chef-solo can get at it (downloadable, or within your chef-repo).