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 end
Then 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 } end
Above 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.