Amsterdam InSpec Handler!
InSpec Handler is a HWRP to automatically run your inspec tests at the end of the chef client run. This directly runs in the target machine eliminating the need for sharing password or keys of privileged accounts. It also detects your runlist and environment and runs those tests that are necessary.
Wacht! What is InSpec?
InSpec is an open-source testing framework with a human-readable language for infrastructure testing as well as compliance testing which is optimized for DevOps. If you want to learn more about inspec, visit their official website or (My Favourite) blog by Annie Hedgpeth. This series of blogs is dedicated to ways of managing your test infrastructure. If you want to learn writing inspec tests, you might want to start with the blog by Annie Hedgpeth.
1..2…3… Go! [ Quick Start ]
The basic idea of this framework is to keep your inspec tests at a specific location say /etc/inspec-tests/ on your chef-fifed machine and run these tests when a chef-client run happens. This is achieved by placing the inspec_handler cookbook at the end of your runlist. Ummm Well, its sounds simple, why do I need to dedicate an entire cookbook to do this? Because… there is more to it. You might want to run your tests in only test environments, and run the tests only when things change in the production environment. You might also consider to fail the chef run if the tests are not passed so that it triggers some handlers or to stop it moving forward in the CI/CD pipeline of CHEF Automate. (Integration tests! Rings a bell?) You might also think of logging these failures. There are many possibilities and inspec_handler tries to simplify these for you!
- The first step is to place your tests in a specific location. It should follow the format <your location>/<cookbook name>/<recipe name>.rb. You might place them in any way you want as long as it follows the convention. I personally place these tests by including them in my cookbook as templates or files and copying them to the desired location in the recipe. This makes updating these tests a lot easier. You might also want to look at a generator cookbook that automates this for you.
- Now create a wrapper cookbook and include the supermarket cookbook inspec-handler. You might want to install inspec before including it if you are below chef 13.
- Now lets look at the inspec handler resource.
inspec_handler 'name' do run_path String log_path String log_shift_age String enforced TrueClass, FalseClass abort_on_fail TrueClass, FalseClass whitelist Array blacklist Array test_environment Array production_environment String action Symbol, :hard_run if not specified end
run_path -> This is the location where inspec tests are located. In our example, we used /etc/inspec-tests. This means that if a node has the following recipes :
your /etc/inspec-tests might have the following arrangement
You might skip some of the files if a test is not required. If the node’s runlist has a recipe, this is the location where inspec_handler will be expecting the test file to exist.
log_path -> This is the place where log files will be placed. inspec_handler creates two log files.
The first one stores(postfix _error) your appended logs for all failed cases, the second one (postfix _last_run) provides a quick view of the last failed run.
log_shift_age -> inspec_handler supports log rotation. This is a number that defines an age to the log files. By default, it is set to 10, which means failures that occurred in the last 10 days will be stored. You might change this as per your organizational need.
enforced -> There are times when you want to enforce testing. If a recipe is found in the run list, it should have it’s corresponding test at the run path, if it is not then generate a warning or fail the client run. And there are times when you are more liberal and want to skip tests if a test does not exist. Set this to ‘true’ or ‘false’ to enforce this rule. Default is ‘false’.
abort_on_fail -> We believe that depending on a node’s role, it would be associated with multiple cookbooks and recipes. And there would be tests for each of them. What do you want to do if a test fails? Do you want to continue further tests or stop immediately? Set this to ‘true; if you want to skip further tests if one fails. The default is true.
whitelist -> In general inspec_handler looks at your runlist and performs the tests sequentially. That are certain times mainly while testing, for productivity, you want to run test only for a specific recipe(s). Provide the recipes names in this attribute. This will override the runlist and will run inspec tests only for this list. Example %w(myapp::default cookbook2::install).
blacklist -> Just the opposite of whitelist. Recipes provided in this array will be skipped for testing. (Even though they exist in the runlist). Note that only the ‘inspec tests’ are skipped and not the actuall recipe!
test_environment -> inspec_handler runs tests in all environment. What if you want to lock your tests to a specific environment(s)? say testing or quality_assurance. Provide a list of such environments here.
production_environment -> ‘Arrr. I don’t want to run all the tests each time when a chef-client runs ‘. This property is intended to set the production environment. When set, in your production environment, inspec_handler will run tests only when a cookbook/recipe is added or removed from runlist, a cookbook has updated its version, or when a test has failed in the last chef-client run. In all other scenarios, inspec_handler sits silently.
- ACTIONS: Deserves attention! Important !inspec_handler gives you two options. :hard_run and :soft_run
:hard_run This will fail your chef-client run when a test fails or the policy set by attributes are not met. We use this in test (in the runner node) to notify the CI/CD pipeline that something has gone wrong and NOT to proceed. This is automatically achieved, as chef-automate will fail the uploaded cookbook as the chef-client has errored out in the runner node. This also serves as a good integration test in production as client-run fails immediately indicating some problem.
:soft_run This will NEVER fail your client-run when a test fails. It will rather generate a warning and push it to the log. I find this useful when I am experimenting with things and don’t want my entire team to be notified when I fail. Cool!
Oke! How do I set all these?
inspec_handler sets these properties via attributes. If you are using a wrapper cookbook you can customize it by setting the attributes. In the CHEF world wrapper cookbook’s attributes will have higher precedence. You can also set them as environment or role attributes if you ar not using a wrapper cookbook. Refer Chef’s Attribute page. I use a wrapper cookbook ( because I need to install inspec at first, this is where I do it) and my attributes/default.rb looks like this :
default['inspec_handler']['run_path'] = "/opt/coe/inspec-tests" default['inspec_handler']['log_path'] = "/var/log/inspec_handler" default['inspec_handler']['log_shift_age'] = "30" default['inspec_handler']['enforced'] = false default['inspec_handler']['abort_on_fail'] = true default['inspec_handler']['whitelist']= nil default['inspec_handler']['blacklist']= %w(coe-inspec-handler::install coe::base) default['inspec_handler']['test_environment']= %w(staging-sid _rehersh) default['inspec_handler']['production_environment']= "_delivered" default['inspec_handler']['action']= :hard_run
And you are all set!
Hoi! Waar is de source ?
Concept: inspec_handler is based on the ideas of Blake Dworaczyk who leads the College of Engineering Linux IT team at Texas A&M U.
Please feel free to contribute!
– Tot ziens , Siddhant Rath