You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
5.5 KiB
149 lines
5.5 KiB
---
|
|
layout: post
|
|
title: Using Chef Infra Node Attributes in Chef InSpec
|
|
category: chef_infra
|
|
tags: [chef, cookbooks, attributes, inspec]
|
|
summary: Don't use Chef Infra attributes in Chef InSpec
|
|
---
|
|
|
|
## Chef Infra, Chef InSpec, Node Attributes, what are they!?!
|
|
|
|
Chef Infra and Chef InSpec are open source products made by Chef Software and
|
|
each fulfill separate needs in their respective problem spaces. That doesn't
|
|
mean they shouldn't be used together though. Pairing configuration management
|
|
(Chef Infra) and infrastructure/application testing (Chef InSpec) is a
|
|
wonderful thing. It is made even more delightful when the same company (and in
|
|
most cases the same humans) work on the tools to pair them.
|
|
|
|
That being said, convenience and in some cases developer intuition can lead to
|
|
unintended and sometimes dangerous consequences. This blog post was created to
|
|
highlight those consequences.
|
|
|
|
## Chef Infra Node Attributes
|
|
|
|
In order to understand the potential consequences, we must first understand
|
|
Chef Infra node attributes and their purpose.
|
|
|
|
Chef Infra utilizes the concept of a node object. This node object is a data
|
|
store for both information about a system (provided by Ohai) and user defined
|
|
information (variables, metadata, etc). It is very common to use this node
|
|
object to drive the behavior of a Chef Infra cookbook (the collection of code
|
|
used to define the desired state of a system).
|
|
|
|
Here is an example that creates users based on a list defined as a node
|
|
attribute:
|
|
|
|
```ruby
|
|
# attributes/default.rb
|
|
default['my_cookbook']['users'] = %w(jerryaldrichiii bobsmith janedoe)
|
|
```
|
|
|
|
```ruby
|
|
# recipies/default.rb
|
|
node['my_cookbook']['users'].each do |my_user|
|
|
user my_user
|
|
end
|
|
```
|
|
|
|
## Testing the Results of Chef Infra
|
|
|
|
Trusting Chef Infra to do what Chef Infra does is great and all, but how would
|
|
you verify it actually did what you expected?
|
|
|
|
Using ChefSpec you could test that the run_list compiled correctly (and the
|
|
correct attributes were set), but how do you verify it actually created the
|
|
users you specified?
|
|
|
|
Chef InSpec!
|
|
|
|
Chef InSpec is built for testing just that. Below is the Chef InSpec to test
|
|
the example above:
|
|
|
|
```ruby
|
|
users = %w(jerryaldrichiii bobsmith janedoe)
|
|
users.each do |my_user|
|
|
describe user(my_user) do
|
|
it { should exist }
|
|
end
|
|
end
|
|
```
|
|
|
|
### DRY Code
|
|
|
|
Now, some of you reading the above might notice that the list of users is
|
|
repeated between Chef Infra and Chef InSpec. In order to follow the DRY (Don't
|
|
Repeat Yourself) methodology you might look to remove this repetition.
|
|
|
|
Couldn't we just use the node object from Chef Infra in Chef InSpec? You might
|
|
ask.
|
|
|
|
In short, you absolutely can! There are even patterns defined in the community
|
|
to do just that! See:
|
|
|
|
http://www.hurryupandwait.io/blog/accessing-chef-node-attributes-from-kitchen-tests
|
|
https://github.com/chef-cookbooks/audit#using-chef-node-data
|
|
|
|
In practice, while seemingly counter intuitive, you might want to avoid doing
|
|
using the Chef Infra node attribute data source in Chef InSpec.
|
|
|
|
## Perils of Persistence
|
|
|
|
Persisting (or saving) the data sources from Chef Infra and using those same
|
|
data sources in Chef InSpec can have perilous consequences.
|
|
|
|
Before we dive in, I would like to take a moment to say that using the Chef
|
|
Infra node object's data in Chef InSpec isn't categorically wrong. In fact,
|
|
there may be very valid use cases to do just that! Just keep the potential
|
|
problems below in mind if you choose to go down that path.
|
|
|
|
### Asking Yourself, "What am I actually testing?"
|
|
|
|
When sharing the same data source between Chef Infra and Chef InSpec you have
|
|
to ask yourself, "What am I actually testing?"
|
|
|
|
To use the example from above, are you testing that Chef Infra can create users
|
|
or that a certain list of users are created? While these two questions may seem
|
|
the same on the surface, they are different.
|
|
|
|
Using our example above we can demonstrate the difference. Let's say that
|
|
another developer comes along and tries to modifies the list of users.
|
|
|
|
Here is an example of these changes:
|
|
|
|
```ruby
|
|
# attributes/default.rb
|
|
default['my_cookbook']['users'] = %w(
|
|
jerryaldrichiii
|
|
bobsmith
|
|
janedoe
|
|
awesomee_developer
|
|
)
|
|
```
|
|
|
|
As you can see, `awesome` is spelled incorrectly. If we had used the same data
|
|
source (the Chef Infra node object) between Chef InSpec and Chef Infra, Chef
|
|
InSpec would not have caught this, Chef Infra would have created the user
|
|
`awesomee_developer` and Chef InSpec would have verified that the
|
|
`awesomee_developer` user existed.
|
|
|
|
In this case Chef InSpec would be testing that Chef Infra can create a set of
|
|
users, not testing if Chef Infra actually created the users you want. Chef
|
|
Infra is perfectly capable of creating users. You most likely were intending
|
|
that the test would test that a defined set of a users were created. Thus, you
|
|
should hard code those values both in the Chef Infra cookbook and the Chef
|
|
Infra tests.
|
|
|
|
## Summary
|
|
|
|
Persisting the node object from Chef Infra to Chef InSpec may seem like the
|
|
most efficient way to test the results of Chef Infra on the surface. In some
|
|
cases, it may be, but if you choose to do that you must keep in mind the
|
|
dangers that come with it. Mainly, it allows for potentially dangerous code
|
|
changes that won't be caught by your automated testing.
|
|
|
|
Instead of persisting the node object, consider other methods of getting the
|
|
data. For example: Using `inspec.command('command').stdout`, hardcoding the
|
|
input attributes in your test framework (e.g. Test Kitchen), and/or moving
|
|
attribute related tests to ChefSpec and using Chef InSpec for testing higher
|
|
level performance (e.g. "Does this web service return a HTTP 200?" vs "Is NGINX
|
|
installed?").
|
|
|