test-kitchenでsymbolやnilを含むChefのattributeをverifierに連携

test-kitchenで、chefのattributeをjsonに書き出してinspecなどに渡すと、symbolやnilの情報は失われてしまう。

そこでnodeオブジェクトをhashとしてテキストで書き出し、読み込ませることで、連携させる例を示す。


#環境
Mac

#前提
ChefDK (導入、セットアップ済み)
Vagrant (proxy provisionerを使い既存のサーバを管理するならば不要)
virtualbox (proxy provisionerを使い既存のサーバを管理するならば不要)


#クックブック作成から

```bash
$ chef generate cookbook shell_inspec
...
$ cd shell_inspec
$ vi .kitchen.yml
...
$ vi recipes/default.rb
...
$ vi test/integration/default/default_test.rb
...
```

# test-kitchen設定

```yaml:.kitchen.yml
---
<% 
  TEST  = 'test/integration/default/default_test.rb'
  ATTR0 = '/tmp/node.hash' 
  ATTR1 = './node_${KITCHEN_INSTANCE}.hash'	# $KITCHEN_INSTANCE の展開はここでは行わない
%>

driver:
  name: vagrant

provisioner:
  name: chef_zero
  always_update_cookbooks: true
  attributes:
    ATTR0: <%= ATTR0 %>         # 書き出し先ファイル名はnode['ATTR0']として連携

verifier:
  #name: inspec
  name: shell
  command: |
    #set -x
    export ATTR1=<%= ATTR1 %>	# ここで$KITCHEN_INSTANCEを展開。InSpecにて環境変数から使用
    ssh -o StrictHostKeyChecking=no -i $KITCHEN_SSH_KEY -p $KITCHEN_PORT $KITCHEN_USERNAME@$KITCHEN_HOSTNAME "sudo cat <%= ATTR0 %>" > $ATTR1
    inspec exec <%= TEST %> -t ssh://${KITCHEN_USERNAME}@${KITCHEN_HOSTNAME}:${KITCHEN_PORT} -i ${KITCHEN_SSH_KEY}

platforms:
  - name: centos-7

suites:
  - name: shell_inspec
    run_list:
      - recipe[shell_inspec::default]
```
変数(Chefのnodeオブジェクトをhashに変換したもの)は、virtualboxのATTR0ファイルに保存し、ローカルのATTR1ファイルにcopyし、InSpecのテストにて読み込んで使用。
shell verifierを使用して、ファイルのcopyとInSpecを実行させる。

# Chefレシピ

```ruby:recipes/default.rb

node.default['foo'] = 'vagrant'       # ここで設定される node['foo'] をInSpecに連携させる

# 後処理
hash = node.to_hash                   # nodeオブジェクトをhashに変換
hash['run_list'] = node['run_list']   # ここは Chef::RunList::RunListItem の Array であり、そのままだと読み込み時にエラーとなることから、回避
IO.write(node['ATTR0'], hash.inspect) # hashをATTR0に文字列として書き出し。

```

# InSpecテスト


```ruby:test/integration/default/default_test.rb
# # encoding: utf-8

# Inspec test for recipe shell_inspec::default

# The Inspec reference, with examples and extensive documentation, can be
# found at http://inspec.io/docs/reference/resources/

node = {}                               # hash型とする
eval('node = ' + `cat #{ENV['ATTR1']}`) # evalを用いてファイルからhashとして読み込み

describe user(node['foo']) do
  it { should exist }
end
```
# vm作成、chef反映、inspecテスト

```bash:
$ kitchen test -d never 
-----> Starting Kitchen (v1.21.2)
-----> Cleaning up any prior instances of <shell-inspec-centos-7>
-----> Destroying <shell-inspec-centos-7>...
       Finished destroying <shell-inspec-centos-7> (0m0.00s).
-----> Testing <shell-inspec-centos-7>
-----> Creating <shell-inspec-centos-7>...
       Bringing machine 'default' up with 'virtualbox' provider...
       ==> default: Importing base box 'bento/centos-7'...
==> default: Matching MAC address for NAT networking...
       ==> default: Checking if box 'bento/centos-7' is up to date...
       ==> default: A newer version of the box 'bento/centos-7' for provider 'virtualbox' is
       ==> default: available! You currently have version '201803.24.0'. The latest is version
       ==> default: '201807.12.0'. Run `vagrant box update` to update.
       ==> default: Setting the name of the VM: shell-inspec-centos-7_default_1533023768058_56231
       ==> default: Clearing any previously set network interfaces...
       ==> default: Preparing network interfaces based on configuration...
           default: Adapter 1: nat
       ==> default: Forwarding ports...
           default: 22 (guest) => 2222 (host) (adapter 1)
       ==> default: Booting VM...
       ==> default: Waiting for machine to boot. This may take a few minutes...
           default: SSH address: 127.0.0.1:2222
           default: SSH username: vagrant
           default: SSH auth method: private key
           default: 
           default: Vagrant insecure key detected. Vagrant will automatically replace
           default: this with a newly generated keypair for better security.
           default: 
           default: Inserting generated public key within guest...
           default: Removing insecure key from the guest if it's present...
           default: Key inserted! Disconnecting and reconnecting using new SSH key...
       ==> default: Machine booted and ready!
       ==> default: Checking for guest additions in VM...
       ==> default: Setting hostname...
       ==> default: Mounting shared folders...
           default: /tmp/omnibus/cache => /Users/aa220269/.kitchen/cache
       ==> default: Machine not provisioned because `--no-provision` is specified.
       [SSH] Established
       Vagrant instance <shell-inspec-centos-7> created.
       Finished creating <shell-inspec-centos-7> (0m47.04s).
-----> Converging <shell-inspec-centos-7>...
       Preparing files for transfer
       Preparing dna.json
       Resolving cookbook dependencies with Berkshelf 7.0.2...
       Removing non-cookbook files before transfer
       Preparing validation.pem
       Preparing client.rb
-----> Installing Chef Omnibus (install only if missing)
       Downloading https://omnitruck.chef.io/install.sh to file /tmp/install.sh
       Trying wget...
       Download complete.
       el 7 x86_64
       Getting information for chef stable  for el...
       downloading https://omnitruck.chef.io/stable/chef/metadata?v=&p=el&pv=7&m=x86_64
         to file /tmp/install.sh.2962/metadata.txt
       trying wget...
       sha1	3229e44c1136741be7df65d1441732bae55a2a02
       sha256	1807c453c5a16fd2cb0941f98847a0031c3812ccb9e3a628b2f8fba8335f485c
       url	https://packages.chef.io/files/stable/chef/14.3.37/el/7/chef-14.3.37-1.el7.x86_64.rpm
       version	14.3.37
       downloaded metadata file looks valid...
       /tmp/omnibus/cache/chef-14.3.37-1.el7.x86_64.rpm exists
       Comparing checksum with sha256sum...
       
       WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
       
       You are installing an omnibus package without a version pin.  If you are installing
       on production servers via an automated process this is DANGEROUS and you will
       be upgraded without warning on new releases, even to new major releases.
       Letting the version float is only appropriate in desktop, test, development or
       CI/CD environments.
       
       WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
       
       Installing chef 
       installing with rpm...
       警告: /tmp/omnibus/cache/chef-14.3.37-1.el7.x86_64.rpm: ヘッダー V4 DSA/SHA1 Signature、鍵 ID 83ef826a: NOKEY
       準備しています...              ################################# [100%]
       更新中 / インストール中...
          1:chef-14.3.37-1.el7               ################################# [100%]
       Thank you for installing Chef!
       Transferring files to <shell-inspec-centos-7>
       Starting Chef Client, version 14.3.37
       Creating a new client identity for shell-inspec-centos-7 using the validator key.
       resolving cookbooks for run list: ["shell_inspec::default"]
       Synchronizing Cookbooks:
         - shell_inspec (0.1.0)
       Installing Cookbook Gems:
       Compiling Cookbooks...
       Converging 0 resources
       
       Running handlers:
       Running handlers complete
       Chef Client finished, 0/0 resources updated in 01 seconds
       Downloading files from <shell-inspec-centos-7>
       Finished converging <shell-inspec-centos-7> (0m19.31s).
-----> Setting up <shell-inspec-centos-7>...
       Finished setting up <shell-inspec-centos-7> (0m0.00s).
-----> Verifying <shell-inspec-centos-7>...
       [Shell] Verify on instance shell-inspec-centos-7 with state={:hostname=>"127.0.0.1", :port=>"2222", :username=>"vagrant", :ssh_key=>"/Users/aa220269/repo/repo-test/cookbooks/shell_inspec/.kitchen/kitchen-vagrant/shell-inspec-centos-7/.vagrant/machines/default/virtualbox/private_key", :last_action=>"setup", :last_error=>nil}

Profile: tests from test/integration/default/default_test.rb (tests from test.integration.default.default_test.rb)
Version: (not specified)
Target:  ssh://vagrant@127.0.0.1:2222

  User vagrant
     ✔  should exist

Test Summary: 1 successful, 0 failures, 0 skipped
       Finished verifying <shell-inspec-centos-7> (0m4.71s).
       Finished testing <shell-inspec-centos-7> (1m11.08s).
-----> Kitchen is finished. (1m11.99s)

```
意図通り、node['foo']が連携された。

# 参考

[ChefからInSpecにattributeをJSON経由で渡したら、シンボルが文字列になっていた](https://qiita.com/hiroyuki_onodera/items/526c33b2c09a145c1a76)