inspec-chef 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: afe50e575ce5384475bd4c1f0ba240df92727baf14cde3a96f35e2a474eceb8c
4
+ data.tar.gz: 6f51ad5775fa43d5ea7eff553608f91f24de4e3248ffcd8722fd92c4e393fba7
5
+ SHA512:
6
+ metadata.gz: 645ab50c1f28a99b7b545d205aa460a819bcdc1380853ed6cbdf0538a53dfb140dd08d0733dab722c9a2cc9b9f4198ae808ff5fe0efb8a2e5ac05ca7e7d2b01a
7
+ data.tar.gz: e7a43170a680e81d11416e72aca2b81e85aa961ca6120fe44e2d4a0c366cebab152741336d3ff5e4ad9f06b914e66de73790ca3ae67bf59f53a4cd832b32491d
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
5
+
6
+ gem "inspec-bin"
7
+
8
+ group :development do
9
+ gem "chefstyle", "0.13.0"
10
+ gem "m"
11
+ gem "bundler"
12
+ gem "rake"
13
+ gem "rubocop"
14
+ end
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # inspec-chef
2
+
3
+ Input plugin for InSpec to access Chef Server data within profiles
4
+
5
+ ## Use Case
6
+
7
+ Some systems rely on Chef Databags or Attributes to be provisioned in the right
8
+ state, triggering specific configuration which is then hard to verify using
9
+ InSpec. For example, one system could have an SQL Server 2012 installed
10
+ while the other has SQL Server 2019. While both might perform an identical
11
+ role, the InSpec tests have to distinguish between both and get some
12
+ information on the characteristics to test.
13
+
14
+ As the configuration information is already present in those constructs,
15
+ it makes little sense to manually configure separate profiles.
16
+
17
+ ## Installation
18
+
19
+ Simply execute `inspec plugin install inspec-chef`, which will get
20
+ the plugin from RubyGems and install/register it with InSpec.
21
+
22
+ You can verify successful installation via `inspec plugin list`
23
+
24
+ ## Configuration
25
+
26
+ Each plugin option may be set either as an environment variable, or as a plugin
27
+ option in your Chef InSpec configuration file at ~/.inspec/config.json. For
28
+ example, to set the chef server information in the config file, lay out the
29
+ config file as follows:
30
+
31
+ ```json
32
+ {
33
+ "version": "1.2",
34
+ "plugins":{
35
+ "inspec-chef":{
36
+ "chef_api_endpoint": "https://chef.example.com/organizations/testing",
37
+ "chef_api_client": "workstation",
38
+ "chef_api_key": "/etc/chef/workstation.pem"
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Config file option names are always lowercase.
45
+
46
+ This plugin supports the following options:
47
+
48
+ | Environment Variable | config.json Option | Description |
49
+ | - | - | - |
50
+ | INSPEC_CHEF_ENDPOINT | chef_api_endpoint | The URL of your Chef server, including the organization |
51
+ | INSPEC_CHEF_CLIENT | chef_api_client | The name of the client of the Chef server to connect as |
52
+ | INSPEC_CHEF_KEY | chef_api_key | Path to the private certificate identifying the node |
53
+
54
+ ## Usage
55
+
56
+ When this plugin is loaded, you can use databag items as inputs:
57
+
58
+ ```ruby
59
+ hostname = input('databag://name/item/some/nested/value')
60
+
61
+ describe host(hostname, port: 80, protocol: 'tcp') do
62
+ it { should be_reachable }
63
+ end
64
+ ```
65
+
66
+ In the same way, you can also add attributes of arbitary nodes:
67
+
68
+ ```ruby
69
+ hostname = input('node://name/attributes/some/nested/attribute')
70
+
71
+ describe host(hostname, port: 80, protocol: 'tcp') do
72
+ it { should be_reachable }
73
+ end
74
+ ```
75
+
76
+ InSpec will go through all loaded input plugins by priority and determine the value.
77
+
78
+ Keep in mind, that the node executing your InSpec runs needs to be
79
+ registered with the chef server to be able to access the data. Lookup
80
+ is __not__ done on the clients tested, but the executing workstation.
81
+
82
+ ## Example
83
+
84
+ With this plugin, the input names consist of the name of the databag,
85
+ the item and a path getting a specific value within an item.
86
+ This way, you can have a databag "configuration" with an item "database" like:
87
+
88
+ ```json
89
+ {
90
+ "SQL": {
91
+ "Type": "SQL2019"
92
+ }
93
+ }
94
+ ```
95
+
96
+ and then use `input('databag://configuration/database/SQL/Type')` to get the
97
+ "SQL2019" value out.
98
+
99
+ ## Limitations
100
+
101
+ There currently is no support for arrays or more complex expressions within
102
+ the query, but support via JMESPath expressions is planned.
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+
3
+ # As plugins are usually packaged and distributed as a RubyGem,
4
+ # we have to provide a .gemspec file, which controls the gembuild
5
+ # and publish process. This is a fairly generic gemspec.
6
+
7
+ # It is traditional in a gemspec to dynamically load the current version
8
+ # from a file in the source tree. The next three lines make that happen.
9
+ lib = File.expand_path("../lib", __FILE__)
10
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
11
+ require "inspec-chef/version"
12
+
13
+ Gem::Specification.new do |spec|
14
+ # Importantly, all InSpec plugins must be prefixed with `inspec-` (most
15
+ # plugins) or `train-` (plugins which add new connectivity features).
16
+ spec.name = "inspec-chef"
17
+
18
+ # It is polite to namespace your plugin under InspecPlugins::YourPluginInCamelCase
19
+ spec.version = InspecPlugins::Chef::VERSION
20
+ spec.authors = ["Thomas Heinen"]
21
+ spec.email = ["theinen@tecracer.de"]
22
+ spec.summary = "InSpec input plugin to access Chef Infra Server databags and attributes."
23
+ spec.description = "This plugin allows InSpec 'inputs' to be provided by Chef Server."
24
+ spec.homepage = "https://github.com/tecracer-theinen/inspec-chef"
25
+ spec.license = "Apache-2.0"
26
+
27
+ # Though complicated-looking, this is pretty standard for a gemspec.
28
+ # It just filters what will actually be packaged in the gem (leaving
29
+ # out tests, etc)
30
+ spec.files = %w{
31
+ README.md inspec-chef.gemspec Gemfile
32
+ } + Dir.glob(
33
+ "lib/**/*", File::FNM_DOTMATCH
34
+ ).reject { |f| File.directory?(f) }
35
+ spec.require_paths = ["lib"]
36
+
37
+ spec.add_dependency "chef-api", "~> 0.10"
38
+ spec.add_dependency "jmespath", "~> 1.4"
39
+ end
@@ -0,0 +1,103 @@
1
+ require "chef-api"
2
+ require "jmespath"
3
+ require "uri"
4
+
5
+ module InspecPlugins::Chef
6
+ class Input < Inspec.plugin(2, :input)
7
+ VALID_PATTERNS = [
8
+ Regexp.new("^databag://[^/]+/[^/]+/.+$"),
9
+ Regexp.new("^node://[^/]+/attributes/.+$"),
10
+ ].freeze
11
+
12
+ attr_reader :plugin_conf, :chef_endpoint, :chef_client, :chef_api_key
13
+ attr_reader :chef_api
14
+
15
+ # Set up new class
16
+ def initialize
17
+ @plugin_conf = Inspec::Config.cached.fetch_plugin_config("inspec-chef")
18
+
19
+ @chef_endpoint = fetch_plugin_setting("endpoint")
20
+ @chef_client = fetch_plugin_setting("client")
21
+ @chef_api_key = fetch_plugin_setting("key")
22
+
23
+ if chef_endpoint.nil? || chef_client.nil? || chef_api_key.nil?
24
+ raise "ERROR: Need configuration of chef endpoint, client name and api key."
25
+ end
26
+
27
+ connect_to_chef_server
28
+ end
29
+
30
+ # Fetch method used for Input plugins
31
+ def fetch(_profile_name, input_uri)
32
+ return nil unless valid_plugin_input? input_uri
33
+
34
+ input = parse_input(input_uri)
35
+
36
+ if input[:type] == :databag
37
+ data = get_databag_item(input[:object], input[:item])
38
+ elsif input[:type] == :node
39
+ data = get_attributes(input[:object]) if input[:item] == "attributes"
40
+ end
41
+
42
+ JMESPath.search(input[:query].join("."), data)
43
+ end
44
+
45
+ private
46
+
47
+ # Get plugin setting via environment, config file or default
48
+ def fetch_plugin_setting(setting_name, default = nil)
49
+ env_var_name = "INSPEC_CHEF_#{setting_name.upcase}"
50
+ config_name = "chef_api_#{setting_name.downcase}"
51
+ ENV[env_var_name] || plugin_conf[config_name] || default
52
+ end
53
+
54
+ # Establish a Chef Server connection
55
+ def connect_to_chef_server
56
+ @chef_api ||= ChefAPI::Connection.new(
57
+ endpoint: chef_endpoint,
58
+ client: chef_client,
59
+ key: chef_api_key
60
+ )
61
+ end
62
+
63
+ # Retrieve a Databag item from Chef Server
64
+ def get_databag_item(databag, item)
65
+ chef_api.data_bag_item.fetch(item, bag: databag).data
66
+ end
67
+
68
+ # Retrieve attributes of a node
69
+ def get_attributes(node)
70
+ data = get_search(:node, "name:#{node}")
71
+
72
+ merge_attributes(data)
73
+ end
74
+
75
+ # Low-level Chef search expression
76
+ def get_search(index, expression)
77
+ chef_api.search.query(index, expression).rows.first
78
+ end
79
+
80
+ # Merge attributes in hierarchy like Chef
81
+ def merge_attributes(data)
82
+ data["default"].merge(data["normal"]).merge(data["override"]).merge(data["automatic"])
83
+ end
84
+
85
+ # Verify if input is valid for this plugin
86
+ def valid_plugin_input?(input)
87
+ VALID_PATTERNS.any? { |regex| regex.match? input }
88
+ end
89
+
90
+ # Parse InSpec input name into Databag, Item and search query
91
+ def parse_input(input_uri)
92
+ uri = URI(input_uri)
93
+ item, *components = uri.path.slice(1..-1).split("/")
94
+
95
+ {
96
+ type: uri.scheme.to_sym,
97
+ object: uri.host,
98
+ item: item,
99
+ query: components,
100
+ }
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ # Plugin Definition file
4
+ # The purpose of this file is to declare to InSpec what plugin_types (capabilities)
5
+ # are included in this plugin, and provide hooks that will load them as needed.
6
+
7
+ # It is important that this file load successfully and *quickly*.
8
+ # Your plugin's functionality may never be used on this InSpec run; so we keep things
9
+ # fast and light by only loading heavy things when they are needed.
10
+
11
+ require "inspec-chef/version"
12
+ module InspecPlugins
13
+ module Chef
14
+ class Plugin < ::Inspec.plugin(2)
15
+ # Internal machine name of the plugin. InSpec will use this in errors, etc.
16
+ plugin_name :'inspec-chef'
17
+
18
+ # Define an Input plugin type.
19
+ input :chef do
20
+ require_relative "input.rb"
21
+ InspecPlugins::Chef::Input
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: UTF-8
2
+
3
+ # This file simply makes it easier for CI engines to update
4
+ # the version stamp, and provide a clean way for the gemspec
5
+ # to learn the current version.
6
+ module InspecPlugins
7
+ module Chef
8
+ VERSION = "0.1.0".freeze
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
4
+ require "inspec-chef/plugin"
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: inspec-chef
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Heinen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jmespath
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ description: This plugin allows InSpec 'inputs' to be provided by Chef Server.
42
+ email:
43
+ - theinen@tecracer.de
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - Gemfile
49
+ - README.md
50
+ - inspec-chef.gemspec
51
+ - lib/inspec-chef.rb
52
+ - lib/inspec-chef/input.rb
53
+ - lib/inspec-chef/plugin.rb
54
+ - lib/inspec-chef/version.rb
55
+ homepage: https://github.com/tecracer-theinen/inspec-chef
56
+ licenses:
57
+ - Apache-2.0
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 3.0.3
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: InSpec input plugin to access Chef Infra Server databags and attributes.
78
+ test_files: []