OxDEAD Unicornz

Have you ever seen so many?

Chef-zero Is Broken in Ubuntu 14.04

I was playing a bit with applying chef cookbooks locally, without a chef server and found that version of Chef from Ubuntu repo is way too low for normal operation in local mode.

1
2
3
4
5
6
7
# apt-cache policy chef
chef:
  Installed: 11.8.2-2
  Candidate: 11.8.2-2
  Version table:
 *** 11.8.2-2 0
        500 http://us.archive.ubuntu.com/ubuntu/ trusty/universe amd64 Packages

There is a bug resolved in recent versions of Chef related to the way in which chef-zero parses json files. If there is metadata.json file instead of metadata.rb in a cookbook chef-zero fails to parse this file expecting just a filename instead of full file content.

For example I’ve got the following error trying to apply one of my cookbooks that depends on yum cookbook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
================================================================================
Error Resolving Cookbooks for Run List:
================================================================================


Unknown Server Error:
---------------------
The server had a fatal error attempting to load the node data.



Server Response:
----------------
Exception raised!  #<Errno::ENAMETOOLONG: File name too long - {
  "name": "yum",
  "version": "3.5.3",
  "description": "Configures various yum components on Red Hat-like systems",
  "long_description": "",
  "maintainer": "Chef",
  "maintainer_email": "cookbooks@getchef.com",
  "license": "Apache 2.0",
  "platforms": {
    "redhat": ">= 0.0.0",
    "centos": ">= 0.0.0",
    "scientific": ">= 0.0.0",
    "amazon": ">= 0.0.0",
    "fedora": ">= 0.0.0"

My first idea was to simply install needed version of Chef using install script during initial node configuration.

1
# curl -L https://www.chef.io/chef/install.sh | sudo bash -s -- -v 11.18.6-1

My second idea was to simply add omnibus_updater cookbook to the base role:

1
2
3
4
5
6
7
8
9
10
11
12
# cat roles/base.json 
{
  "name": "base",
  "override_attributes": {
    "omnibus_updater": {
      "version": "11.18.6-1"
    }
  },
  "run_list": [
    "recipe[omnibus_updater]"
  ]
}

Unfortunately this approach does not work very well – first you need to get omnibus_updater cookbook to the server. It’s not a big deal but adds an additional step to whole process. Other problem is that Chef client is unable to configure node within one run: during the initial run you have to execute chef-client on the node with only omnibus_updater cookbook in the run list. If you have other cookbooks in run list chef-client will fail due to json parsing bug described above. Even if everything works well omnibus_updater kills chef-client immediately after performing upgrade which makes lot of sense but once again prevents chef-client from configuring a node within one run:

1
2
3
4
5
6
7
8
9
  * ruby_block[omnibus chef killer] action create
================================================================================
Error executing action `create` on resource 'ruby_block[omnibus chef killer]'
================================================================================


RuntimeError
------------
New omnibus chef version installed. Killing Chef run!

so I decided to fallback to my first decision with installing correct version of chef-client using installer script. Having omnibus_updater in a base role run list is still a good idea – we’re locking chef-client to the particular version this way.

Another local-cookbook-installation gotcha.

If you’re trying to install cookbooks into the directory one level above your cookbook using —path option you’ll get an ugly stack trace:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
root@ubuntu1404:~/chef-zero/cookbooks/openvpn# berks install --path /root/chef-zero/cookbooks
Using openvpn (2.1.1)
Using apt (2.7.0)
Using yum (3.5.3)
Using yum-epel (0.6.0)
/var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/locations/path_location.rb:70:in `expand_path': No such file or directory - getcwd (Errno::ENOENT)
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/locations/path_location.rb:70:in `expand_path'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/locations/path_location.rb:70:in `relative_path'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/cookbook_source.rb:206:in `block in to_hash'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/cookbook_source.rb:196:in `tap'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/cookbook_source.rb:196:in `to_hash'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/cookbook_source.rb:218:in `to_json'
        from /var/lib/gems/1.9.1/gems/json-1.7.7/lib/json/common.rb:285:in `generate'
        from /var/lib/gems/1.9.1/gems/json-1.7.7/lib/json/common.rb:285:in `pretty_generate'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/lockfile.rb:150:in `to_json'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/lockfile.rb:177:in `block in save'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/lockfile.rb:176:in `open'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/lockfile.rb:176:in `save'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/lockfile.rb:90:in `update'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/berksfile.rb:426:in `install'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/cli.rb:153:in `install'
        from /usr/lib/ruby/vendor_ruby/thor/command.rb:27:in `run'
        from /usr/lib/ruby/vendor_ruby/thor/invocation.rb:121:in `invoke_command'
        from /usr/lib/ruby/vendor_ruby/thor.rb:363:in `dispatch'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/lib/berkshelf/cli.rb:21:in `dispatch'
        from /usr/lib/ruby/vendor_ruby/thor/base.rb:440:in `start'
        from /var/lib/gems/1.9.1/gems/berkshelf-2.0.18/bin/berks:6:in `<top (required)>'
        from /usr/local/bin/berks:23:in `load'
        from /usr/local/bin/berks:23:in `<main>'

The point is that berkshelf in this case actually recreates directory you’ve launched it from. The correct way is to install cookbooks to some temporary directory:

1
2
3
4
5
root@ubuntu1404:~/chef-zero/cookbooks/openvpn# berks install --path ./tmp
Using openvpn (2.1.1) from metadata
Using apt (2.7.0)
Using yum (3.5.3)
Using yum-epel (0.6.0)

and then move them to your cookbook_path directory.