Use Chef Zero Kitchen Test with Docker

  07 Nov 2016


First, setting up new cookbook to test. Let’s say we want to create git cookbook. For that we can generate git cookbook with the following command:

$ chef generate cookbook git_cookbook

Generating cookbook git_cookbook
- Ensuring correct cookbook file content
- Committing cookbook files to git
- Ensuring delivery configuration
- Ensuring correct delivery build cookbook content
- Adding delivery configuration to feature branch
- Adding build cookbook to feature branch
- Merging delivery content feature branch to master

Your cookbook is ready. Type `cd git_cookbook` to enter it.

There are several commands you can run to get started locally developing and testing your cookbook.
Type `delivery local --help` to see a full list.

Why not start by writing a test? Tests for the default recipe are stored at:

test/recipes/default_test.rb

If you'd prefer to dive right in, the default recipe can be found at:

recipes/default.rb

It will get project directory structure, sample recipe and test for cookbook development

We can see that we have recipes/default.rb generated by chef in the directory. Let’s modify this a bit. Just try to add:

log "This is default git recipe"

And also, at the root of project in Gemfile, don’t forget adding some of required ruby gems:

$ cat Gemfile
source "https://rubygems.org"

gem "chef"
gem 'berkshelf'
gem "test-kitchen"
gem "kitchen-docker"
gem "kitchen-inspec"

We got .kitchen.yml generated by chef as well. We use docker driver, set use_sudo false and add centos 7 as a platform from docer image

$ cat .kitchen.yml
---
driver:
  name: docker
  use_sudo: false

provisioner:
  name: chef_zero
  always_update_cookbooks: true

verifier:
  name: inspec

platforms:
  - name: 'centos'
    driver_plugin: docker
    driver:
      use_sudo: false
      image: centos:7
suites:
  - name: default
    run_list:
      - recipe[git_cookbook::default]
    verifier:
      inspec_tests:
        - test/recipes
    attributes:

At this point, we can launch container by kitchen create.

    $ kitchen create
    -----> Starting Kitchen (v1.10.2)
    -----> Creating <default-centos>...
           Sending build context to Docker daemon 233.5 kB
           Step 1 : FROM centos:7
            ---> 980e0e4c79ec
           Step 2 : ENV container docker
            ---> Using cache
            ---> 8d0bd86bcf63
           Step 3 : RUN yum clean all
            ---> Using cache
            ---> c701e05433eb
           Step 4 : RUN yum install -y sudo openssh-server openssh-clients which curl
            ---> Using cache
            ---> 68bb1d38b6de
           Step 5 : RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ''
            ---> Using cache
            ---> c793e932260d
           Step 6 : RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N ''
            ---> Using cache
            ---> 26db97a999c2
           Step 7 : RUN if ! getent passwd kitchen; then                 useradd -d /home/kitchen -m -s /bin/bash -p '*' kitchen;               fi
            ---> Using cache
            ---> 57209faaf99d
           Step 8 : RUN echo "kitchen ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
            ---> Using cache
            ---> 98f65d290f5e
           Step 9 : RUN echo "Defaults !requiretty" >> /etc/sudoers
            ---> Using cache
            ---> 1e8b528eeff8
           Step 10 : RUN mkdir -p /home/kitchen/.ssh
            ---> Using cache
            ---> 83e6c1d52ba7
           Step 11 : RUN chown -R kitchen /home/kitchen/.ssh
            ---> Using cache
            ---> d05e480c4b2e
           Step 12 : RUN chmod 0700 /home/kitchen/.ssh
            ---> Using cache
            ---> 6ce88518efd7
           Step 13 : RUN touch /home/kitchen/.ssh/authorized_keys
            ---> Using cache
            ---> fe341d5ed189
           Step 14 : RUN chown kitchen /home/kitchen/.ssh/authorized_keys
            ---> Using cache
            ---> 8e100b398ec6
           Step 15 : RUN chmod 0600 /home/kitchen/.ssh/authorized_keys
            ---> Using cache
            ---> 2407020fa3e8
           Step 16 : RUN echo ssh-rsa\ AAAAB3NzaC1yc2EAAAADAQABAAABAQCmHanpb30UoLEB03CvQesfSKk98PaMLRsRKrLP9JZACqwfNIXOO6Hg13AM4I5rN\+iGYbETrpk5C7M30h43dExU5B56A4LV7S\+Xbr6DQWkRfCNeoiHKxAWcTfxeZg6pODcK1qdbTKG52pZ0PVzVo3zmISj6KHa0f5mGW6QPT1xRL7\+zEWOpGPPoN/xiFMckUC5UbHbji3JMNqem4zDOK0H5KM87qJO7JVpFBeW5trh1WsHTIwnLafnt/2LTEvhk4ljUGgQ9AUAekWpw8WGkYYpGHnWb/YatfYGsQs10neNXCIUVYEKCEf/hVbJ1cuIbw0JY7mqghA1Wtcxc2pU3y7CF\ kitchen_docker_key >> /home/kitchen/.ssh/authorized_keys
            ---> Using cache
            ---> 0981a7ac059a
           Successfully built 0981a7ac059a
           168e79b16b35541339c0ef8d3972d62cb953ae7f107dce45337e574421993071
           0.0.0.0:32772
           [SSH] Established
           Finished creating <default-centos> (0m2.92s).
    -----> Kitchen is finished. (0m6.74s)

If we try kitchen list

    $ kitchen list
    Instance        Driver  Provisioner  Verifier  Transport  Last Action
    default-centos  Docker  ChefZero     Inspec    Ssh        Created

Then we can see that default-centos instance (in this case is docker container) is created.

After that we should try to run our recipe that is recipes/default.rb with kitchen converge With this command it will run recipes from our run_list items from .kitchen.yml which contains

    run_list:
      - recipe[git_cookbook::default]

Ok, let’s run it.

    $ kitchen converge
    -----> Starting Kitchen (v1.10.2)
    -----> Converging <default-centos>...
           Preparing files for transfer
           Preparing dna.json
           Resolving cookbook dependencies with Berkshelf 5.1.0...
           Removing non-cookbook files before transfer
           Preparing validation.pem
           Preparing client.rb
    -----> Chef Omnibus installation detected (install only if missing)
           Transferring files to <default-centos>
           Starting Chef Client, version 12.15.19
           [2016-11-09T09:45:32+00:00] WARN: unable to detect ipaddress
           resolving cookbooks for run list: ["git_cookbook::default"]
           Synchronizing Cookbooks:
             - git_cookbook (0.1.0)
           Installing Cookbook Gems:
           Compiling Cookbooks...
           Converging 1 resources
           Recipe: git_cookbook::default
             * log[This is default git recipe] action write


           Running handlers:
           Running handlers complete
           Chef Client finished, 1/1 resources updated in 02 seconds
           Finished converging <default-centos> (0m5.63s).
    -----> Kitchen is finished. (0m9.60s)

As we can see it prints out This is default git recipe from what we have written in the recipe as expected.

Since we use kitchen, it is excellent tool for testing. Now we should try to run some tests. From what we got from chef generated

    verifier:
      name: inspec
    suites:
      - name: default
        verifier:
          inspec_tests:
            - test/recipes

That means we can write a test using inspec ( It is testing and compliance auditing framwork http://inspec.io/ ) and from .kitchen.yml information above says that it will run any test in test/recipes path.

So, after we run kitchen verify, it should then run all tests under test/recipes path. Before continue, let’s try running it first

    $ kitchen verify
    -----> Starting Kitchen (v1.10.2)
    -----> Setting up <default-centos>...
           Finished setting up <default-centos> (0m0.00s).
    -----> Verifying <default-centos>...
           Using `/Users/zdk/git_cookbook/test/recipes/default` for testing

    Target:  ssh://kitchen@localhost:32772


      User root
         ✔  should exist
         ○  This is an example test, replace with your own test.
      Port 80
         ✔  should not be listening
         ○  This is an example test, replace with your own test.

    Test Summary: 2 successful, 0 failures, 2 skipped
           Finished verifying <default-centos> (0m0.60s).
    -----> Kitchen is finished. (0m4.08s)

And it runs two default tests that test root user should exist and there should not be anything listening on port 80. We can add new file in test/recipes directory, but for now just add the following content at the end of test/recipes/default_test.rb

     describe command('git --version') do
         its('stdout') { should match /1\.8\.3/ }
     end

That said the converged system should have git installed with version 1.8.3 After we added, then run verify again. And we should get error because we haven’t installed git on container yet.

    $ kitchen verify
    -----> Starting Kitchen (v1.10.2)
    WARN: Unresolved specs during Gem::Specification.reset:
          ffi (>= 1.0.1)
          multi_json (~> 1.10)
          excon (>= 0.38.0)
          unf_ext (>= 0)
    WARN: Clearing out unresolved specs.
    Please report a bug if this causes problems.
    -----> Verifying <default-centos>...
           Using `/Users/zdk/git_cookbook/test/recipes/default` for testing

    Target:  ssh://kitchen@localhost:32772


      User root
         ✔  should exist
      Port 80
         ✔  should not be listening
      Command git
         ✖  --version stdout should match /1\.8\.3/
         expected "" to match /1\.8\.3/
         Diff:
         @@ -1,2 +1,2 @@
         -/1\.8\.3/
         +""


    Test Summary: 2 successful, 1 failures, 0 skipped
    >>>>>> ------Exception-------
    >>>>>> Class: Kitchen::ActionFailed
    >>>>>> Message: 1 actions failed.
    >>>>>>     Verify failed on instance <default-centos>.  Please see .kitchen/logs/default-centos.log for more details
    >>>>>> ----------------------
    >>>>>> Please see .kitchen/logs/kitchen.log for more details
    >>>>>> Also try running `kitchen diagnose --all` for configuration

Yes, it failed as expected. Now let’s fix this. Open recipes/default.rb to edit. Add the following content to the default.rb file

    execute 'yum update -y'
    package 'git'

Save and then run kitchen converge && kitchen verify

    $ kitchen converge && kitchen verify
    -----> Starting Kitchen (v1.10.2)
    WARN: Unresolved specs during Gem::Specification.reset:
          ffi (>= 1.0.1)
          multi_json (~> 1.10)
          excon (>= 0.38.0)
          unf_ext (>= 0)
    WARN: Clearing out unresolved specs.
    Please report a bug if this causes problems.
    -----> Converging <default-centos>...
           Preparing files for transfer
           Preparing dna.json
           Resolving cookbook dependencies with Berkshelf 5.1.0...
           Removing non-cookbook files before transfer
           Preparing validation.pem
           Preparing client.rb
    -----> Chef Omnibus installation detected (install only if missing)
           Transferring files to <default-centos>
           Starting Chef Client, version 12.15.19
           [2016-11-09T10:03:47+00:00] WARN: unable to detect ipaddress
           resolving cookbooks for run list: ["git_cookbook::default"]
           Synchronizing Cookbooks:
             - git_cookbook (0.1.0)
           Installing Cookbook Gems:
           Compiling Cookbooks...
           Converging 3 resources
           Recipe: git_cookbook::default
             * log[This is default git recipe] action write

             * execute[yum update -y] action run
               - execute yum update -y
             * yum_package[git] action install
               - install version 1.8.3.1-6.el7_2.1 of package git

           Running handlers:
           Running handlers complete
           Chef Client finished, 3/3 resources updated in 02 minutes 01 seconds
           Finished converging <default-centos> (2m5.74s).
    -----> Kitchen is finished. (2m10.30s)
    -----> Starting Kitchen (v1.10.2)
    WARN: Unresolved specs during Gem::Specification.reset:
          ffi (>= 1.0.1)
          multi_json (~> 1.10)
          excon (>= 0.38.0)
          unf_ext (>= 0)
    WARN: Clearing out unresolved specs.
    Please report a bug if this causes problems.
    -----> Setting up <default-centos>...
           Finished setting up <default-centos> (0m0.00s).
    -----> Verifying <default-centos>...
           Using `/Users/zdk/git_cookbook/test/recipes/default` for testing

    Target:  ssh://kitchen@localhost:32772


      User root
         ✔  should exist
      Port 80
         ✔  should not be listening
      Command git
         ✔  --version stdout should match /1\.8\.3/

    Test Summary: 3 successful, 0 failures, 0 skipped
           Finished verifying <default-centos> (0m0.70s).
    -----> Kitchen is finished. (0m4.72s)

What we have done is Test-Driven Infrastructure with Test Kitchen. I hope it is fun and also help you developing better quality Chef recipe!

comments powered by Disqus