4. Exploring InSpec Resources
Resources
Now that you have learned about making and running InSpec profiles, let's dig deeper into the mechanics of InSpec by learning about resources.
Core Resources
As you saw in the Beginner class, when writing InSpec code, many core resources are available because they are included in the main InSpec code base.
- You can explore the core InSpec resources to existing resources.
- You can also examine the source code to see what's available. For example, you can see how
file
and other InSpec resources are implemented.
Local Resources
Local resources are those that exist only in the profile in which they are developed. Local resources are put in the libraries
directory:
$ tree examples/profile
examples/profile
...
βββ libraries
β βββ custom_resource.rb
Note that the libraries
directory is not created by default within a profile when we use inspec init
. We need to create the directory ourselves.
Once you create and populate a custom resource Ruby file inside the libraries
directory, it can be utilized inside your local profile just like the core resources.
6.1. Resource Overview
Resources may be added to profiles in the libraries folder:
$ tree examples/profile
examples/profile
...
βββ libraries
β βββ gordon_config.rb
6.2. Resource Structure
The smallest possible InSpec resource takes this form:
class Tiny < Inspec.resource(1)
name 'tiny'
end
This is easy to write, but not particularly useful for testing.
Resources are written as a regular Ruby class, which inherits from the base inspec.resource
class. The number (1) specifies the version this resource plugin targets. As Chef InSpec evolves, this interface may change and may require a higher version.
In addition to the resource name, the following attributes can be configured:
name
- Identifier of the resource (required)desc
- Description of the resource (optional)example
- Example usage of the resource (optional)supports
- (Chef InSpec 2.0+) Platform restrictions of the resource (optional)
The following methods are available to the resource:
inspec
- Contains a registry of all other resources to interact with the operating system or target in general.skip_resource
- A resource may call this method to indicate that requirements arenβt met. All tests that use this resource will be marked as skipped.
The following example shows a full resource using attributes and methods to provide simple access to a configuration file:
class GordonConfig < Inspec.resource(1)
name 'gordon_config'
# Restrict to only run on the below platforms (if none were given, all OS's supported)
supports platform_family: 'fedora'
supports platform: 'centos', release: '6.9'
# Supports `*` for wildcard matcher in the release
supports platform: 'centos', release: '7.*'
desc '
Resource description ...
'
example '
describe gordon_config do
its("signal") { should eq "on" }
end
'
# Load the configuration file on initialization
def initialize(path = nil)
@path = path || '/etc/gordon.conf'
@params = SimpleConfig.new( read_content )
end
# Expose all parameters of the configuration file.
def method_missing(name)
@params[name]
end
private
def read_content
f = inspec.file(@path)
# Test if the path exist and that it's a file
if f.file?
# Retrieve the file's contents
f.content
else
# If the file doesn't exist, skip all tests that use gordon_config
raise Inspec::Exceptions::ResourceSkipped, "Can't read config at #{@path}"
end
end
end
Let's break down each component of the resource.
class
The class is where the Ruby file is defined.
name
The name is how we will call upon this resource within our controls, in the example above that would be gordon_config
.
supports
Supports are used to define or restrict the Ruby resource to work in specific ways, as shown in the example above that is used to restrict our class to specific platforms.
desc
A simple description of the purpose of this resource.
examples
A simple use case example. The example is usually a describe
block using the resource, given as a multi-line comment.
initialize method
An initilize method is required if your resource needs to be able to accept a parameter when called in a test (e.g. file('this/path/is/a/parameter')
)
functionality methods
These methods return data from the resource so that you can use it in tests.