This should not happen often and maybe never, but there have been a few occasions where I needed the attribute values stored in the "node under test" to verify my kitchen test verifications. For example, maybe your recipe uses the node's IP or machine names as part of writing a config file and your kitchen driver uses dhcp to obtain an IP and auto generates unique machine names. In this case, you cant know the IP or machine name at the time you are authoring your test to check that the correct text was used in the config file.
This post demonstrates a very small and simple technique to retrieve this data inside of the test in order to dynamically verify the correct values were used in your recipe that you are testing.
At CenturyLink Cloud we use this in a few serverspec tests. One place we use this is in testing our haproxy configuration. Some values we need to inject into the configuration include the subnet of the current node and its chef environment which we include in the server names. Depending on where the kitchen test is being run, a different environment may be used so we cant be sure what the names of the subnet or chef environment will be while writing the test.
We solve this by adding a recipe to the end of our kitchen run list called "export-node":
suites:
- name: my-suite
run_list:
- recipe[cookbook-under-test]
- recipe[export-node]This is a very simple recipe that simply converts the current node's attributes to json and writes it to a known location in /tmp/kitchen on the node.
ruby_block "Save node attributes" do
block do
if Dir::exist?('/tmp/kitchen')
IO.write("/tmp/kitchen/chef_node.json", node.to_json)
end
end
endThen our test can read that file and parse its json to a hash to be used throughout the test.
require 'json'
require 'serverspec'
set :backend, :exec
describe file('/etc/haproxy/haproxy.cfg') do
let(:node) { JSON.parse(IO.read('/tmp/kitchen/chef_node.json')) }
let(:subnet) {
ip = node["automatic"]["ipaddress"]
ip[0,ip.rindex(".")]
}
let(:env) { node["chef_environment"].upcase}
it { should be_a_file }
its(:content) {
should match <<-EOS
backend elasticsearch_backend
mode http
balance roundrobin
server #{env}SRCH01 #{subnet}.121:92 weight 1 check port 92
server #{env}SRCH02 #{subnet}.122:92 weight 1 check port 92
server #{env}SRCH03 #{subnet}.123:92 weight 1 check port 92
backend web_backend
mode http
balance roundrobin
timeout server 5m
server #{env}WEB01 #{subnet}.131:80 weight 1 check port 80
server #{env}WEB02 #{subnet}.132:80 weight 1 check port 80
backend rabbit_backend
mode http
balance roundrobin
server #{env}RABBIT01 #{subnet}.141:1567 weight 1 check port 1567
server #{env}RABBIT02 #{subnet}.142:1567 weight 1 check port 1567
server #{env}RABBIT03 #{subnet}.143:1567 weight 1 check port 1567
EOS
}
endAbove is a serverspec test that parses the node json to a hash. That hash is then accessible to our it blocks.
I have extracted the handful of lines in the export-node recipe to its own cookbook so you can grab it here.