Sorry this page looks weird. It was automatically migrated from my old blog, which had a different layout and different CSS.

Three Chef Gotchas

I just used Chef Solo for the first time to configure a production server.

I wrote all my cookbooks from scratch, mostly as a learning exercise but also so I know exactly what they all do. I’d say the first 90% took 10% of the time, and the last 10% took 90% of the time.

Here are three parts of Chef which weren’t immediately obvious to me.

Executing a Bash script as a user

The bash docs say you can set user to run your code as that user. Ideally this would execute the script in a full login shell for the user. However it only sets the uid; it doesn’t set the PATH and environment variables as you’d expect. There’s more information in the bug report.

I found the way round this is to pass all the variables your script will need to environment. For example:

bash 'clone_rbenv' do
  user 'someuser'
  environment ({'HOME' => '/home/someuser'})
  code <<-END
    git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
  END
  not_if 'test -d /home/someuser/.rbenv', user: 'someuser'
end

Installing gems as a user

I’m using rbenv to install Ruby for my so-called ‘deploy’ user. However Chef Solo runs as root.

To install gems for a particular user, I was hoping I could do this:

gem_package 'bundler' do
  user 'deploy'
end

But it doesn’t work.

This was the best (only!) solution I found:

gem_package 'bundler' do
  gem_binary '/home/deploy/.rbenv/versions/1.9.3-p327/bin/gem'
end

Cron tasks running @reboot

Chef doesn’t support @reboot in its cron resource. However you can make it happen like this:

cron 'my cron task' do
  minute  '@reboot'
  hour    ''
  day     ''
  month   ''
  weekday ''
  command 'somecmd'
end

Update 31-07-2014: it turns out this workaround works once but subsequent runs add a duplicate entry each time. I now have my entire crontab in a template and update it with a bash resource:

template '/tmp/rendered_crontab' do
  source 'crontab.erb'
  owner 'deploy'
  mode 0755
end

bash 'replace crontab' do
  user 'deploy'
  environment ({'HOME' => '/home/deploy'})
  code <<-END
    crontab -u deploy /tmp/rendered_crontab
  END
  not_if 'crontab -u deploy -l | diff /tmp/rendered_crontab -'
end

Supposedly Chef supports @reboot properly in 11.12.0. See opscode/chef#971 and opscode/chef#1708.

Vagrant

I tested my cookbooks on Vagrant, specifically its official Ubuntu Precise 64 Bit box.

However when I ran my cookbooks for the first time against my fresh production Ubuntu 12.04 LTS server, they failed. The reason was obvious in hindsight: the Vagrant boxes come with a Ruby pre-installed but my production server didn’t. Although I was installing a fresh Ruby via my cookbooks, its openssl dependency was missing on the server.

So I added the libssl-dev package to my pre-Chef bootstrap script and everything was fine.

Update 31-07-2014: Ansible doesn’t have this problem ;)

More information

The two Peepcodes on Chef (Part 1, Part 2) explain Chef with admirable clarity. I highly recommend them!

Andrew Stewart • 27 November 2012 • Chef
You can reach me by email or on Twitter.