Chef Blogs

Upgrading Chef using Chef

Tom Duffield | Posted on | community | cookbooks

One of our goals here at Chef (née Opscode) is to make installing Chef on a new node as easy as possible so that you can start enjoying the benefits of automation with minimal delay. But what happens to nodes that stick around for extended periods of time?

If you operate in an ecosystem where you cannot treat your infrastructure like garbage then you should have a plan for upgrading the copies of Chef Client that are already running in your environment. With Chef 11, automatic in-place upgrades can be handled using Heavy Water Operations’ omnibus\_updater cookbook. Chef can update Chef!

Basic Usage

To get started, add the omnibus\_updater cookbook to your run_list. For our example, we will add the omnibus\_updater to the base role’s run_list.

{
   "name": "base",
   "description": "All good Chefs have base roles!",
   "chef\_type": "role",
   "json\_class": "Chef::Role",
   "run\_list": [
      "recipe[chef-client::delete\_validation]",
      "recipe[chef-client]",
      "recipe[omnibus\_updater]"
   ]
}

When chef-client executes on your node, omnibus\_updater will collect the required metadata (platform, platform\_version and machine['kernel']) and use this information to download and install the correct package from Chef’s omnitruck API.

Specifying The Version

The default behavior of omnibus\_updater is to download and install the latest version of the Chef Client. You can specify which version of the Chef Client you would like to install by setting the node['omnibus\_updater']['version'] attribute appropriately.

Because the version of Chef Client you wish to download is stored as an attribute, you can leverage merge-order precedence to control what version is installed in different scenarios. As an example, let’s walk through an example where your team would like to promote versions of Chef Client through your two Chef environments: staging and production.

In staging, you hard-code the latest release of Chef Client so that your team can test its stability in your infrastructure.

{
   "name": "staging", 
   "description": "Test latest chef-client before moving to Prod.", 
   "chef\_type": "environment",
   "json\_class": "Chef::Environment",
   "default\_attributes": {
      "omnibus\_updater": {
         "version": "11.8.2"
      }
   }
}

In production, you hard-code the version of Chef Client that has been approved for use in production by your team.

{
   "name": "production", 
   "description": "Use approved, well-tested version of chef in Prod.", 
   "chef\_type": "environment",
   "json\_class": "Chef::Environment",
   "default\_attributes": {
      "omnibus\_updater": {
         "version": "11.6.2"
      }
   }
}

Once the decision has been made that 11.8.2 is stable enough for use in production, all that would be required is to update the attribute in the environment. That version of Chef Client would be installed the next time chef-client is run.

{
   "name": "production", 
   "description": "Use approved, well-tested version of chef in Prod.", 
   "chef\_type": "environment",
   "json\_class": "Chef::Environment",
   "default\_attributes": {
      "omnibus\_updater": {
         "version": "11.8.2"
      }
   }
}

Downloading From Custom Location

If you are unable to access the public Internet to download the omnibus packages from Chef or wish to download your packages from a mirror (e.g. a mirror behind your company’s firewall), you can set the node['omnibus\_updater']['direct\_url'] attribute to point to a specific omnibus package.

One solution could be to set the node['omnibus\_updater']['direct\_url'] attribute in your Chef environment.

{
   "name": "production", 
   "description": "Use approved, well-tested version of chef in Prod.", 
   "chef\_type": "environment",
   "json\_class": "Chef::Environment",
   "default\_attributes": {
      "omnibus\_updater": {
         "direct\_url": "http://example.com/chef-11.8.2-1.el6.x86_64.rpm"
      }
   }
}

The downside of this implementation is that it will not work in a cross-platform ecosystem. To create a more robust solution, you could create a wrapper cookbook and build the node['omnibus\_updater']['direct\_url'] value based on your specific requirements.

To do this, you would specify the version of Chef Client you want in your production environment like we did before.

{
   "name": "production", 
   "description": "Use approved, well-tested version of chef in Prod.", 
   "chef\_type": "environment",
   "json\_class": "Chef::Environment",
   "default\_attributes": {
      "omnibus\_updater": {
         "version": "11.8.2"
      }
   }
}

Then you would write a recipe, like the one in the example below, that uses cross-platform logic to set the node['omnibus\_updater']['direct\_url'] attribute to point to the appropriate Chef Client package before calling the omnibus\_updater cookbook via include\_recipe.

# If no version is specified, use a default value (latest)
version = node['omnibus\_updater']['version'] || '11.8.2'

if platform\_family?('rhel')
   node.default['omnibus\_updater']['direct\_url'] = "http://example.com/chef\_#{version}-1.el#{node['platform\_version'].to_i}.#{node['kernel']['machine']}.rpm"

elsif platform\_family('debian')
   node.default['omnibus\_updater']['direct\_url'] = "http://example.com/chef\_#{version}-1.#{node['platform']}.#{node['platform\_version']}_#{node['kernel']['machine']}.deb"

else
  Chef::Log.warn "The platform #{node['platform']} has not been accounted for in the download logic. Please update this recipe accordingly."
end

include\_recipe "omnibus\_updater"

Summary

Using Heavy Water Operations’ omnibus\_updater cookbook you can avoid the laborious and time consuming practice of manually upgrading Chef Client. The omnibus\_updater cookbook has other useful features that might interest you so I encourage you to read its documentation.