Skip steps in an InSpec integration test based on Chef attribute values

To speed up the test of some complex recipes, I use an attribute to skip certain long-running installations when I only need to test the rest of the recipe.

We will pass Chef Attribute values into an InSpec test using environment variables.

Declare an attribute in the atrributes.rb file with a safe default value, to allow the execution of all steps if the value is not overridden

# Set this to true in the .kitchen.yml file to skip long-running tasks during local test
default['quick_launch_for_test'] = false

Set the value in the suites: section of the kitchen.yml file to true to skip the long-running installations

  attributes:
    quick_launch_for_test: true

Save the attribute value in an environment variable in the recipe to pass it to InSpec

# Store the value in an environment variable for the integration test
env 'quick_launch_for_test' do
  value "#{node['quick_launch_for_test']}"
end

Make a decision in the recipe based on the value of the attribute

if ( node['quick_launch_for_test'] != true )
  #########################################
  #Microsoft Visual Studio Community 2017 #
  #########################################
  ...
end

Read the value of the environment variable into a local variable in the InSpec test file. If the environment variable does not exist, or the value is not “true”, the value of the local_execution variable will be “false”.

local_execution = ( os_env('quick_launch_for_test').content() == 'true')

Use the if statement to make the decision in your InSpec test file

if !local_execution
  describe package('Microsoft Visual Studio Community 2017') do
    it { should be_installed }
  end
end

 

Waiting for SSH service on …:22, retrying in 3 seconds

When you try to launch a new EC2 instance in AWS using Chef Test Kitchen and the process times out with the message:

  Waiting for SSH service on …:22, retrying in 3 seconds

$$$$$$ [SSH] connection failed, terminating (#<Net::SSH::AuthenticationFailed: Authentication failed for user …@…>)
>>>>>> ——Exception——-
>>>>>> Class: Kitchen::ActionFailed
>>>>>> Message: 1 actions failed.
>>>>>> Failed to complete #create action: [SSH session could not be established] on default-rhel7
>>>>>> ———————-
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose –all` for configuration

Check the access to the “ssh_key” which is specified in the platforms: driver: transport: section of the .kitchen.yml file.

The .pem file should be accessible from the cookbook directory with the absolute or relative path.

Failed to complete #create action: [undefined method `version’ for nil:NilClass] on default…

When you execute kitchen converge to launch an EC2 instance in AWS with Chef Test Kitchen, you get the error message:

>>>>> ——Exception——-
>>>>>> Class: Kitchen::ActionFailed
>>>>>> Message: 1 actions failed.
>>>>>> Failed to complete #create action: [undefined method `version’ for nil:NilClass] on default-windows-2012r2
>>>>>> ———————-
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose –all` for configuration

The kitchen-ec2 driver tries to read the operating system version from the name of the image, but it cannot find it.

When you create your own AMI (Amazon Machine Image), make sure the version of the operating system is in the name:

Windows-2012
Windows-2012r2
Windows-2012r2sp1
RHEL-7.2

An acceptable name is my_windows-2012r2_base-1

Could not load the ‘ec2’ driver from the load path

When you execute kitchen list and the driver in your .kitchen file is “ec2“, the following error message appears:

>>>> ——Exception——-
>>>>>> Class: Kitchen::ClientError
>>>>>> Message: Could not load the ‘ec2’ driver from the load path. Please ensure that your driver is installed as a gem or included in your Gemfile if using Bundler.
>>>>>> ———————-
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose –all` for configuration

If you have recently installed the Chef Development Kit, you need to install the Kitchen EC2 driver to be able to launch servers in AWS.

To install the Kitchen EC2 driver, execute

chef gem install kitchen-ec2

 

No instances for regex `’, try running `kitchen list’ in Chef Test Kitchen

When I tried to execute the “kitchen list” command in Chef Test Kitchen the following error came up:

No instances for regex `’, try running `kitchen list’

I could not find the reason for the error, but when I opened the .kitchen.yml file in the Atom editor, made an insignificant change and saved the file, the error went away.

Berks update fails with ‘Missing artifacts’ error message

When you add cookbooks as dependencies with the “depends” statement to the metadata.rb file of your Chef cookbook, to be able to test your cookbooks in Chef Test Kitchen, you also have to specify the location of those cookbooks in the Berksfile file.

For all the cookbooks that are available on the Chef Supermarket, one line

source "https://supermarket.chef.io"

is sufficient to specify their location. If a cookbook is only available at GitHub, specify the location with

cookbook 'COOKBOOK_NAME', git: 'git@github.com:PATH_TO_COOKBOOK.git'

If the cookbook is available on the local drive of the workstation, specify the path with

cookbook 'COOKBOOK_NAME', path: '../COOKBOOK_FOLDER_NAME'

Use the above relative path if all of your cookbooks are under the same cookbooks directory.

If a reference to a Chef cookbook is missing from the Berksfile file, the following message appears when you execute berks update.

Unable to satisfy constraints on package …, which does not exist, due to solution constraint (… = …). Solution constraints that may result in a constraint on …: [(… = …) -> (… >= …)]
Missing artifacts: ...
Demand that cannot be met: (… = …)
Unable to find a solution for demands: … (…)

Chef file locations

The Chef file and folder locations are different on Linux and Windows machines. This article explains the purpose of each file and the location.

Summary

Linux Windows
Cookbook location /var/chef/cache/cookbooks  C:\chef\cache\cookbooks
Chef Client run log /var/log/chef.log First run only
C:\chef\chef-client.log
Subsequent Chef client runs
C:\chef\log\client.log
Error log /var/chef/cache/chef-stacktrace.out C:\chef\cache\chef-stacktrace.out
Ohai output /var/chef/cache/failed-run-data.json C:\chef\cache\failed-run-data.json
Recommended location for custom log files /tmp/cheflog.log C:\Logs\Chef\cheflog.log
Chef Client configuration /etc/chef/client.rb C:\chef\client.rb

When you test your cookbook in Test Kitchen

The .kitchen.yml file contains the username to execute the Chef cookbook. It is specified under platforms:, transport:, username:

Use that value in place of USER-NAME-FROM-KITCHEN-YML below.

Linux Windows
Cookbook location /tmp/kitchen/cookbooks
/tmp/kitchen/cache/cookbooks
 C:\Users\USER-NAME-FROM-KITCHEN-YML\AppData\Local\Temp\kitchen\cookbooks
Error log /tmp/kitchen/cache/chef-stacktrace.out C:\Users\USER-NAME-FROM-KITCHEN-YML\AppData\Local\Temp\kitchen\cache\chef-stacktrace.out
Ohai output /tmp/kitchen/cache/failed-run-data.json C:\Users\USER-NAME-FROM-KITCHEN-YML\AppData\Local\Temp\kitchen\cache\failed-run-data.json
Data bags /tmp/kitchen/data_bags C:\Users\USER-NAME-FROM-KITCHEN-YML\AppData\Local\Temp\kitchen\data_bags

Cookbook location

When the Chef recipes are executed, all cookbooks are stored on the node. You can examine the code to make sure your latest changes are reflected on the machine.

The log of the Chef client run

The output of the Chef cookbook execution is in the chef.log or chef-client.log file

On Windows

The log of the first Chef Client run and subsequent runs are stored in different log files. After the initial Chef Client run, the rest of the log entries are collected in the second file.

Stacktrace

Chef saves information on the hard drive when scripts are executed. If there is a failure, the stack trace of the last error is saved in the chef-stacktrace.out file.

Ohai output

All the information that Ohai collects on the instance, is saved in the failed-run-data.json file, even if there is no error. It is a great resource to get the server specific values.

Cloud info

  • cloud-specific information under “cloud
  • cloud instance information under “ec2

Computer info

  • CPU and memory configuration under “cpu“, “cs_info“, “memory
  • drive sizes and network settings under “filesystem” and “network

Operating system info

  • operating system information under “os_info
  • list of enabled Windows features under “dism_features
  • the list of installed applications under “packages

Chef info

  • Chef client configuration values under “chef_client
  • the Chef node information under “chef_type”: “node
  • the Chef run list under “run_list
  • list of Chef cookbooks and their versions under “cookbooks
  • list of the executed recipes under “recipes
  • the value of the passed in Chef attributes under “normal
  • the value of the Chef cookbook attributes under “chef_client” and the cookbook name
  • all the information on the Chef resources under “json_class
  • the stack trace of the last error under “exception

Launch Windows instances locally with Chef Test Kitchen

Most Linux distributions are free, and do not require product keys to launch them.

The steps below are based on the great article at http://kitchen.ci/blog/test-kitchen-windows-test-flight-with-vagrant/

I have summarized the steps below to create a free Virtual Box Windows Server 2012R2 image on your workstation, so Test Kitchen can use Vagrant and Virtual Box to launch Windows instances and test cookbooks locally fast and free.


Install the Vagrant WinRm plugin

vagrant plugin install vagrant-winrm

Get BoxCutter

Create the Windows image with BoxCutter. This process will take 30 minutes or more to fully configure the Windows image. The download step may time out on slower VPN connections. In that case disconnect the VPN connection and try it again without it. In case of any error, just execute the last command again and if it can, the process will continue from the point of error.

cd ~/
mkdir boxcutter
cd boxcutter
git clone https://github.com/boxcutter/windows.git
cd windows

Modify the BoxCutter JSON file

Change the ~/boxcutter/windows/eval-win2012r2-standard.json file to avoid the error message:

virtualbox-iso: Removing floppy drive…
==> virtualbox-iso: Error removing floppy controller: VBoxManage error: VBoxManage: error: The machine ‘eval-win2012r2-standard’ is already locked for a session (or being unlocked)
==> virtualbox-iso: VBoxManage: error: Details: code VBOX_E_INVALID_OBJECT_STATE (0x80bb0007), component MachineWrap, interface IMachine, callee nsISupports
==> virtualbox-iso: VBoxManage: error: Context: “LockMachine(a->session, LockType_Write)” at line 1038 of file VBoxManageStorageController.cpp

  • Change “headless”: “false”, to “headless”: “true”,
  • Add under “headless”: “true”,
"shutdown_timeout": "60m",
"post_shutdown_delay": "120s",

 

  • Add under every occurrence of “headless”: “{{ user `headless` }}”,
"shutdown_timeout": "{{ user `shutdown_timeout` }}",
"post_shutdown_delay": "{{ user `post_shutdown_delay` }}",

Create the virtual machine

make virtualbox/eval-win2012r2-standard

Add the image to Vagrant

vagrant box add windows-2012r2 ./box/virtualbox/eval-win2012r2-standard-nocm-1.0.4.box

Test the virtual machine

Your .kitchen.yml file should look like this

---
driver:
  name: vagrant

provisioner:
  name: chef_zero

platforms:
  - name: windows-2012r2

suites:
  - name: MY_SUITE_NAME
  run_list:
  - recipe[MY_COOKBOOK::MY_RECIPE]

 

You may need to execute the kitchen converge commands as sudo to be able to launch the Windows instance.

`block in filter_instances’: undefined method `empty?’ for nil:NilClass (NoMethodError)

When you try to execute any of the “kitchen” commands in Chef Test Kitchen you may get the following error message.

lib/kitchen/config.rb:182:in `block in filter_instances': undefined method `empty?' for nil:NilClass (NoMethodError)

This happened in my cookbook, when I have removed the value from the “excludes:” option in the .kitchen.yml file, but left the “excludes:” option there alone.

To make your cookbook work in Chef Test Kitchen again, remove the empty “excludes:” option form the .kitchen.yml file

DevOps Engineering part 5. – Create an enterprise cookbook

In this exercise we will create a Chef cookbook for a corporation. It will be robust and include all the necessary elements to be used in a large enterprise. If your company already has established standards, as you progress with this tutorial, copy the appropriate file from an existing cookbook. If this is the first cookbook in your organization, or you don’t want to follow the current standards, copy the sample files from this page.

Create the cookbook

  • Navigate to the C:\Chef\…\cookbooks folder and open a Bash window
  • Create an empty cookbook
    chef generate cookbook COOKBOOK_NAME

    Chef creates a new sub-folder with the name of the cookbook.

  • Rename the new folder to cookbook-COOKBOOK_NAME to distinguish it in version control from other repository types.

Update the .gitignore file

  • Chef DK has generated a .gitignore file that contains most of the important entries, but we need to add a few important lines:
    # Certificates
    *.pem
    
    # Unencrypted Data Bags
    data_bags_unencrypted/
    
    # Macintosh folder custom attribute file
    .DS_Store
    
    # If working in a team, personalized Test Kitchen config file 
    .kitchen.yml

Update the metadata.rb file

Add your name, email address, and the version of the cookbook to the top of the metadata.rb file

name 'THE_NAME_OF_THE_COOKBOOK'
maintainer 'YOUR_NAME'
maintainer_email 'YOUR_EMAIL_ADDRESS'
license 'All rights reserved'
description 'Installs/Configures THE_NAME_OF_THE_COOKBOOK'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '1.0.0'

The expression in the long_description line refers to the README.md file that GitHub can create when you set up a new repository. Place the cookbook related information into that file, so others who want to use your cookbook can easily find it.

Update the Berksfile

Add the list of cookbooks this cookbook depends on to the Berksfile file. To download all necessary cookbooks execute

berks install

Update the .kitchen.yml file to be able to test your cookbook.

If you want to test your cookbook in the Cloud, copy the following sections from an existing .kitchen.yml file of your organization. To learn Chef and launch instances with Vagrant on your workstation, the automatically created .kitchen.yml file is perfect.

  • driver
  • provisioner
  • platforms
  • a suite as an example

If your organization use tags to track instances in the Cloud,  update the tags section to reflect the values of the cookbook.

To add multiple recipes to the Chef run list use this syntax:

run_list:
  - recipe[COOKBOOK_NAME1::RECIPE_NAME]
  - recipe[COOKBOOK_NAME2::RECIPE_NAME]

Encrypted Data Bags

If you want to use encrypted data bags, ask your Chef administrator to send you the key file to encrypt and decrypt data bags. Create the data_bags_unencrypted folder for the data bag secret key on the same level where the cookbook and environment folders are.

This is a chicken and egg paradox. We don’t want to commit secrets into version control, so we need to encrypt them. But how can we place the secret encryption key on the server to decrypt the secrets? We will use Packer to create our own server images that will contain the secret key, so when Chef starts to run on the server, the key is going to be there.

Default recipe

We will place code in the default.rb file that is common to all recipes in the cookbook. All custom recipes will call the default recipe as the first step.

Update the header comments of the default.rb recipe with your name and company information

#
# Cookbook Name:: COOKBOOK_NAME
# Recipe:: default
#
# Author:: YOUR_NAME (<YOUR_EMAIL_ADDRESS>)
# Copyright (c) 2015-2016 COMPANY_NAME, All Rights Reserved.

Attribute file

Create the default attribute file with

chef generate attribute default

Add the necessary attributes to the attribute file to store the AWS tag and other cookbook specific values.

Custom recipe

Use the Chef generate command to create the new recipes. It creates all test files in the corect location.

  • Create a new recipe and the test files with
    chef generate recipe MY_RECIPE_NAME
  • Add the following line under the header to call the default recipe, even if it is currently empty
    include_recipe 'COOKBOOK_NAME::default'

Add the cookbook to GitHub

Create the local repository

  • In the Bash window execute the following commands
git init
git add .
git commit -m "Initial commit"

Add the repository to GitHub

In your web browser log into your GitHub account and create a new repository

  • Click the New Repository button
  • Name the repository the same as the name of the folder of the cookbook (cookbook-…)
  • Execute the lines in the section …or push an existing repository….  If you work on a Windows workstation make sure HTTPS is selected
    git remote add origin https://github.com/....git
    git push -u origin master
  • In the Collaborators & teams section of Settings select the group who will have access to the new repository

Test the cookbook

In the Bash window launch the instance with Test Kitchen

List the available instances

kitchen list

Launch the instance

kitchen converge STRING_UNIQUE_TO_THE_INSTANCE

If there are multiple suites or platforms in the .kitchen.yml file you need to type the unique part of the name of the instance to identify it.

Start a Remote Desktop connection to a Windows instance

kitchen login STRING_UNIQUE_TO_THE_INSTANCE

If you work on a Macintosh workstation and testing a Windows server, the best way to remote into the server is

  1. Install Microsoft Remote Desktop for free from the Apple App Store (See the Remote Desktop Client section in Install the DevOps development tools on Macintosh,
  2. Execute the kitchen login STRING_UNIQUE_TO_THE_INSTANCE command in the terminal window,
  3. The Microsoft Remote Desktop window will pop up with the User account name and the IP address. If you click “Connect”, the remote connection opens with the default settings, that are may not optimal for your display. The “login” command already created a new entry in the Microsoft Remote Desktop app with the IP address of the instance. To use custom settings, click Cancel,
  4. Open the Microsoft Remote Desktop app, right click the last entry that contains the IP address, select Edit, and copy the IP address to the clipboard,
  5. Create a new connection with custom display settings and paste the IP address there. You can keep this generic connection to access the Test Kitchen instances, just update the IP address.
  6. Delete the automatically created connection

SSH into a Linux instance

ssh USER_NAME@IP_ADDRESS -i PATH_TO_THE SSH_KEY_FILE

Where

  • USER_NAME is the value of username: in the .kitchen.yml file.
  • IP_ADDRESS is the IP of the instance. Get it from the “Waiting for SSH service on…” line of the Test Kitchen bash or command window, or from the .yml file in the .kitchen/logs folder of the cookbook.
  • PATH_TO_THE_SSH_KEY_FILE is the value of ssh_key: in the .kitchen.yml file.

Terminate the instance

kitchen destroy STRING_UNIQUE_TO_THE_INSTANCE

Upload the cookbook to the Chef server

See Connect to the Chef server in Beginner’s Guide to DevOps Engineering part 4.


Next:

Infrastructure as code in Beginner’s Guide to DevOps Engineering part 6.

Back:

to the Tutorials page