Introduction

Grocery Delivery (GD) is a ruby gem that helps keep cookbooks, roles, and data bags in sync between a source control repo and your chef server. This allows administrators to keep their entire chef code in source control with continuous integration, peer reviews, and any other checks they can think of. Once the code has been checked and merged into the master branch the grocery-delivery binary takes the place of knife upload. This can be ran on a cron job, or event based using web-hooks to verify that the latest code is on your chef server. This also means your server is safer from someone accidentally pushing breaking code to production.

So lets get started on setting up GD using a ubuntu 16.04 host that already has the chef server configured. You could technically run this on a separate box but keeping it on the chef server is easier to troubleshoot/maintain in my opinion.

Requirements

In case it wasn’t clear your chef server needs to already be setup, if not go to chef docs on installing the server. Plus:

  • access to a user key
  • access to a validator key
  • ssh access to the chef server
  • your chef repo in source control

Setup

If you want a sample chef repo to fork I recommend forking my example repo:

https://github.com/clburlison/example-chef-repo

Run the following commands as root:

sudo su

Install the following packages so we can build dependencies for GD:

apt-get update
apt-get upgrade
apt-get install cmake pkg-config

Install the GD gem into the chef server ruby path. The path is not a strict requirement but it is nice to have locked version of ruby:

/opt/opscode/embedded/bin/gem install grocery_delivery

Create the config file for GD using the default path.

Info

NOTE: you will need to change the git repo_url and potentially the cookbook_paths, role_path, and databag_path.

vi /etc/gd-config.rb

Paste the following:

stdout          true
repo_url        '[email protected]:clburlison/chef-repo.git'
reponame        'cpe'
cookbook_paths  ['cookbooks']
role_path       'roles'
databag_path    'data_bags'
rev_checkpoint  'gd_revision'
knife_config    '/root/.chef/knife.rb'
knife_bin       '/opt/opscode/bin/knife'
vcs_type        'git'
berks           false
berks_bin       '/opt/opscode/embedded/bin/berks'

Create a hidden chef folder for the root user:

mkdir /root/.chef

Create the knife config:

vi /root/.chef/knife.rb

Paste the following:

Info

NOTE: make sure and update the node_name, client_key, validation_client_name, and chef_server_url keys

log_level               :info
log_location            STDOUT
node_name               'gd'
client_key              'gd.pem'
validation_client_name  'clburlison-validator'
validation_key          'clburlison-validator.pem'
chef_server_url         'https://chef.example.com/organizations/clburlison'
cookbook_path [
  '/var/chef/grocery_delivery_work/cpe/cookbooks/',
]

Copy the copy the two pem files (gd.pem and clburlison-validator.pem from my example) from your admin workstation to the chef server under /root/.chef.

SSH deploy key

The following can be ran on your workstation or chef server.

Create a ssh deploy key so the Chef server can access your chef git repo:

ssh-keygen -t rsa -b 4096 -C "[email protected]"

Change the save location if you wish. Full output below:

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/clburlison/.ssh/id_rsa): /Users/clburlison/.ssh/gd_deploy
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/clburlison/.ssh/gd_deploy.
Your public key has been saved in /Users/clburlison/.ssh/gd_deploy.pub.
The key fingerprint is:
SHA256:4OIJ40Lncc7cwfAGY1UoBtutBDpKLoYwlOQDL8o9GkE [email protected]
The key's randomart image is:
+---[RSA 4096]----+
|oE.o.  .o.       |
|=o. +oo.         |
|+B...Bo.         |
|Oo= o.B.         |
|=*o=.o.=S        |
|+.=+Boo .        |
|....o+ .         |
| .               |
|                 |
+----[SHA256]-----+

Create the .ssh directory for the root user if it doesn’t exist:

mkdir -p /root/.ssh/

Copy the gd_deploy private key to your chef server under /root/.ssh/id_rsa. Make sure the file permissions are correct:

chmod 0600 /root/.ssh/id_rsa

Then add the key to your ssh agent:

eval "$(ssh-agent -s)"
ssh-add /root/.ssh/id_rsa

Now copy the gd_deploy.pub public key to your chef repo deploy key section. For Github the url will be:

https://github.com/USERNAME/example-chef-repo/settings/keys

Info

NOTE: that this key should not have write access to the git repo.

Make a test connection to github to add the RSA fingerprint.

ssh -T [email protected]

You should get the following as output:

root@chef:~$ ssh -T [email protected]
The authenticity of host 'github.com (192.30.253.113)' can't be established.
RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,192.30.253.113' (RSA) to the list of known hosts.
Hi clburlison/example-chef-repo! You've successfully authenticated, but GitHub does not provide shell access.

Run GD

Great now if we did everything correctly the following command should add our code to the chef server:

/opt/opscode/embedded/bin/grocery-delivery -vv

If all works you’ll likely want to run this script on a cron job without the verbose flag:

vi /etc/cron.d/gd

Paste the following to run every 5 minutes. Obviously modify to taste:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * root /opt/opscode/embedded/bin/grocery-delivery

Cron hates me so lets be safe and force a reload.

/etc/init.d/cron reload

If you are having cron issues add &>> /tmp/gd.log to the end of your command and reload to write debug info to a log file.

Bonus

If you wish to delete all cookbooks run:

knife cookbook bulk delete "/*"