Plugins API v2: Loader, Base API, and Test Harness (#3278)
* Functional tests for userdir option
* Accepts --config-dir CLI option
* Actually loads a config file from the config dir, more cases to test
* Able to load config and verify contents from config-dir
* Functional tests to ensure precedence for config options
* Enable setting config dir via env var
* .inspec, not .inspec.d
* Begin converting PluginCtl to PluginLoader/Registry
* Able to load and partially validate the plugins.json file
* More work on the plugin loader
* Break the world, move next gen stuff to plugin/
* Be sure to require base cli in bundled plugins
* Move test file
* Revert changes to v1 plugin, so we can have a separate one
* Checkpoint commit
* Move v2 plugin work to v2 area
* Move plugins v1 code into an isolated directory
* rubocop fixes
* Rip out the stuff about a user-dir config file, just use a plugin file
* Two psuedocode test file
* Working base API, moock plugin type, and loader.
* Adjust load path to be more welcoming
* Silence circular depencency warning, which was breaking a unit test
* Linting
* Fix plugin type registry, add tests to cover
* Feedback from Jerry
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
2018-08-16 22:16:32 +00:00
|
|
|
# Functional tests related to plugin facility
|
|
|
|
require 'functional/helper'
|
|
|
|
|
|
|
|
#=========================================================================================#
|
|
|
|
# Loader Errors
|
|
|
|
#=========================================================================================#
|
|
|
|
describe 'plugin loader' do
|
|
|
|
include FunctionalHelper
|
|
|
|
|
|
|
|
it 'handles a corrupt plugins.json correctly' do
|
|
|
|
outcome = inspec_with_env('version', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'corrupt'))
|
|
|
|
outcome.exit_status.must_equal 2
|
|
|
|
outcome.stdout.wont_include('Inspec::Plugin::V2::ConfigError', 'No stacktrace in error by default')
|
|
|
|
outcome.stdout.must_include('Failed to load plugins JSON configuration', 'Friendly message in error')
|
|
|
|
outcome.stdout.must_include('unit/mock/config_dirs/corrupt/plugins.json', 'Location of bad file in error')
|
|
|
|
|
|
|
|
outcome = inspec_with_env('version --debug', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'corrupt'))
|
|
|
|
outcome.exit_status.must_equal 2
|
|
|
|
outcome.stdout.must_include('Inspec::Plugin::V2::ConfigError', 'Include stacktrace in error with --debug')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'handles a misversioned plugins.json correctly' do
|
|
|
|
outcome = inspec_with_env('version', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'bad_plugin_conf_version'))
|
|
|
|
outcome.exit_status.must_equal 2
|
|
|
|
outcome.stdout.wont_include('Inspec::Plugin::V2::ConfigError', 'No stacktrace in error by default')
|
|
|
|
outcome.stdout.must_include('Unsupported plugins.json file version', 'Friendly message in error')
|
|
|
|
outcome.stdout.must_include('unit/mock/config_dirs/bad_plugin_conf_version/plugins.json', 'Location of bad file in error')
|
|
|
|
outcome.stdout.must_include('99.99.9', 'Incorrect version in error')
|
|
|
|
|
|
|
|
outcome = inspec_with_env('version --debug', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'bad_plugin_conf_version'))
|
|
|
|
outcome.exit_status.must_equal 2
|
|
|
|
outcome.stdout.must_include('Inspec::Plugin::V2::ConfigError', 'Include stacktrace in error with --debug')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'handles an unloadable plugin correctly' do
|
|
|
|
outcome = inspec_with_env('version', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'plugin_error_on_load'))
|
|
|
|
outcome.exit_status.must_equal 2
|
|
|
|
outcome.stdout.must_include('ERROR', 'Have an error on stdout')
|
|
|
|
outcome.stdout.must_include('Could not load plugin inspec-divide-by-zero', 'Name the plugin in the stdout error')
|
|
|
|
outcome.stdout.wont_include('ZeroDivisionError', 'No stacktrace in error by default')
|
|
|
|
outcome.stdout.must_include('Errors were encountered while loading plugins', 'Friendly message in error')
|
|
|
|
outcome.stdout.must_include('Plugin name: inspec-divide-by-zero', 'Plugin named in error')
|
|
|
|
outcome.stdout.must_include('divided by 0', 'Exception message in error')
|
|
|
|
|
|
|
|
outcome = inspec_with_env('version --debug', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'plugin_error_on_load'))
|
|
|
|
outcome.exit_status.must_equal 2
|
|
|
|
outcome.stdout.must_include('ZeroDivisionError', 'Include stacktrace in error with --debug')
|
|
|
|
end
|
|
|
|
end
|
2018-08-17 00:22:28 +00:00
|
|
|
|
|
|
|
#=========================================================================================#
|
|
|
|
# CliCommand plugin type
|
|
|
|
#=========================================================================================#
|
|
|
|
describe 'cli command plugins' do
|
|
|
|
include FunctionalHelper
|
|
|
|
|
|
|
|
it 'is able to respond to a plugin-based cli subcommand' do
|
|
|
|
outcome = inspec_with_env('meaningoflife answer', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'meaning_by_path'))
|
|
|
|
outcome.stderr.wont_include 'Could not find command "meaningoflife"'
|
|
|
|
outcome.stderr.must_equal ''
|
|
|
|
outcome.stdout.must_equal ''
|
|
|
|
outcome.exit_status.must_equal 42
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is able to respond to [help subcommand] invocations' do
|
|
|
|
outcome = inspec_with_env('help meaningoflife', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'meaning_by_path'))
|
|
|
|
outcome.exit_status.must_equal 0
|
|
|
|
outcome.stderr.must_equal ''
|
|
|
|
outcome.stdout.must_include 'inspec meaningoflife answer'
|
|
|
|
# Full text:
|
|
|
|
# 'Exits immediately with an exit code reflecting the answer to life the universe, and everything.'
|
|
|
|
# but Thor will ellipsify based on the terminal width
|
|
|
|
outcome.stdout.must_include 'Exits immediately'
|
|
|
|
end
|
|
|
|
|
|
|
|
# This is an important test; usually CLI plugins are only activated when their name is present in ARGV
|
|
|
|
it 'includes plugin-based cli commands in top-level help' do
|
|
|
|
outcome = inspec_with_env('help', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'meaning_by_path'))
|
|
|
|
outcome.exit_status.must_equal 0
|
|
|
|
outcome.stdout.must_include 'inspec meaningoflife'
|
|
|
|
end
|
2018-09-18 04:00:54 +00:00
|
|
|
end
|
2018-09-25 14:29:18 +00:00
|
|
|
|
|
|
|
#=========================================================================================#
|
|
|
|
# inspec plugin command
|
|
|
|
#=========================================================================================#
|
|
|
|
# See lib/plugins/inspec-plugin-manager-cli/test
|
2018-09-25 22:51:38 +00:00
|
|
|
|
|
|
|
#=========================================================================================#
|
|
|
|
# CLI Usage Messaging
|
|
|
|
#=========================================================================================#
|
|
|
|
describe 'plugin cli usage message integration' do
|
|
|
|
include FunctionalHelper
|
|
|
|
|
|
|
|
[' help', ''].each do |invocation|
|
|
|
|
it "includes v2 plugins in `inspec#{invocation}` output" do
|
|
|
|
outcome = inspec(invocation)
|
|
|
|
outcome.stderr.must_equal ''
|
|
|
|
|
|
|
|
# These are some subcommands provided by core v2 plugins
|
|
|
|
['habitat', 'artifact'].each do |subcommand|
|
|
|
|
outcome.stdout.must_include('inspec ' + subcommand)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-09-27 22:46:35 +00:00
|
|
|
|
|
|
|
#=========================================================================================#
|
|
|
|
# Train Plugin Support
|
|
|
|
#=========================================================================================#
|
|
|
|
|
|
|
|
describe 'train plugin support' do
|
|
|
|
describe 'when a train plugin is installed' do
|
|
|
|
include FunctionalHelper
|
|
|
|
it 'can run inspec detect against a URL target' do
|
|
|
|
outcome = inspec_with_env('detect -t test-fixture://', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'train-test-fixture'))
|
|
|
|
outcome.exit_status.must_equal(0)
|
|
|
|
outcome.stderr.must_be_empty
|
|
|
|
lines = outcome.stdout.split("\n")
|
|
|
|
lines.grep(/Name/).first.must_include('test-fixture')
|
|
|
|
lines.grep(/Name/).first.wont_include('train-test-fixture')
|
|
|
|
lines.grep(/Release/).first.must_include('0.1.0')
|
|
|
|
lines.grep(/Families/).first.must_include('os')
|
|
|
|
lines.grep(/Families/).first.must_include('windows')
|
|
|
|
lines.grep(/Families/).first.must_include('unix')
|
|
|
|
lines.grep(/Arch/).first.must_include('mock')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'can run inspec detect against a test-fixture backend' do
|
|
|
|
outcome = inspec_with_env('detect -b test-fixture', INSPEC_CONFIG_DIR: File.join(config_dir_path, 'train-test-fixture'))
|
|
|
|
outcome.exit_status.must_equal(0)
|
|
|
|
outcome.stderr.must_be_empty
|
|
|
|
lines = outcome.stdout.split("\n")
|
|
|
|
lines.grep(/Name/).first.must_include('test-fixture')
|
|
|
|
lines.grep(/Name/).first.wont_include('train-test-fixture')
|
|
|
|
lines.grep(/Release/).first.must_include('0.1.0')
|
|
|
|
lines.grep(/Families/).first.must_include('os')
|
|
|
|
lines.grep(/Families/).first.must_include('windows')
|
|
|
|
lines.grep(/Families/).first.must_include('unix')
|
|
|
|
lines.grep(/Arch/).first.must_include('mock')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'can run inspec shell and read a file' do
|
|
|
|
outcome = inspec_with_env("shell -t test-fixture:// -c 'file(\"any-path\").content'", INSPEC_CONFIG_DIR: File.join(config_dir_path, 'train-test-fixture'))
|
|
|
|
outcome.exit_status.must_equal(0)
|
|
|
|
outcome.stderr.must_be_empty
|
|
|
|
outcome.stdout.chomp.must_equal 'Lorem Ipsum'
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'can run inspec shell and run a command' do
|
|
|
|
outcome = inspec_with_env("shell -t test-fixture:// -c 'command(\"echo hello\").exit_status'", INSPEC_CONFIG_DIR: File.join(config_dir_path, 'train-test-fixture'))
|
|
|
|
outcome.exit_status.must_equal(0)
|
|
|
|
outcome.stderr.must_be_empty
|
|
|
|
outcome.stdout.chomp.must_equal "17"
|
|
|
|
|
|
|
|
outcome = inspec_with_env("shell -t test-fixture:// -c 'command(\"echo hello\").stdout'", INSPEC_CONFIG_DIR: File.join(config_dir_path, 'train-test-fixture'))
|
|
|
|
outcome.exit_status.must_equal(0)
|
|
|
|
outcome.stderr.must_be_empty
|
|
|
|
outcome.stdout.chomp.must_equal "Mock Command Result stdout"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|