mirror of
https://github.com/inspec/inspec
synced 2024-11-26 06:30:26 +00:00
Templatize everything for InSpec plugins
Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
5343c217b0
commit
731b0705f5
7 changed files with 104 additions and 81 deletions
|
@ -1,7 +1,6 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require_relative 'renderer'
|
||||
require 'byebug'
|
||||
|
||||
module InspecPlugins
|
||||
module Init
|
||||
|
@ -17,8 +16,9 @@ module InspecPlugins
|
|||
option :description, type: :string, default: '', desc: 'Multi-paragraph description of the plugin.'
|
||||
option :summary, type: :string, default: 'A plugin with a default summary', desc: 'One-line summary of your plugin'
|
||||
option :license_name, type: :string, default: 'Apache-2.0', desc: 'The name of a license'
|
||||
option :hook, type: :array, default: ['cli_command:my_command'], desc: 'A list of plugin hooks, in the form type1:name1, type2:name2, etc'
|
||||
|
||||
# These vars have calulated defaults
|
||||
# These vars have calculated defaults
|
||||
option :homepage, type: :string, default: '', desc: 'A URL for your project, often a GitHub link'
|
||||
option :module_name, type: :string, default: '', desc: 'Module Name for your plugin package. Will change plugin name to CamelCase by default.'
|
||||
|
||||
|
@ -76,7 +76,29 @@ module InspecPlugins
|
|||
license_name: options[:license_name],
|
||||
license_text: fetch_license_text(options[:license_name]),
|
||||
copyright: options[:copyright],
|
||||
}
|
||||
activators: options[:activators],
|
||||
}.merge(parse_hook_option(options[:hook]))
|
||||
end
|
||||
|
||||
def parse_hook_option(raw_option)
|
||||
hooks_by_type = {}
|
||||
raw_option.each do |entry|
|
||||
parts = entry.split(':')
|
||||
type = parts.first.to_sym
|
||||
name = parts.last
|
||||
if hooks_by_type.key?(type)
|
||||
ui.error 'Inspec plugin generate can currently only generate one hook of each type'
|
||||
ui.exit(:usage_error)
|
||||
end
|
||||
hooks_by_type[type] = name
|
||||
end
|
||||
|
||||
vars = { hooks: hooks_by_type }
|
||||
if hooks_by_type.key?(:cli_command)
|
||||
vars[:command_name_dashes] = hooks_by_type[:cli_command].tr('_', '-')
|
||||
vars[:command_name_snake] = hooks_by_type[:cli_command].tr('-', '_')
|
||||
end
|
||||
vars
|
||||
end
|
||||
|
||||
def fetch_license_text(license_name)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'inspec/resource'
|
||||
|
||||
module InspecPlugins::ResourceLister
|
||||
module InspecPlugins::<%= module_name %>
|
||||
# This class will provide the actual CLI implementation.
|
||||
# Its superclass is provided by another call to Inspec.plugin,
|
||||
# this time with two args. The first arg specifies we are requesting
|
||||
|
@ -15,56 +15,50 @@ module InspecPlugins::ResourceLister
|
|||
# commands, usage information, and options, use the Thor documentation.
|
||||
class CliCommand < Inspec.plugin(2, :cli_command)
|
||||
# This isn't provided by Thor, but is needed by InSpec so that it can
|
||||
# register the subcommand. Args are a usage message, and a short decription.
|
||||
# register the top-level subcommand. That is to say, subcommand_desc
|
||||
# makes `inspec <%= command_name_dashes %> ...` work. Args to sub_command_desc are
|
||||
# a usage message, and a short decription.
|
||||
# These will appear when someone has installed the plugin, and then they
|
||||
# run `inspec help`.
|
||||
subcommand_desc 'list-resources [COMMAND]', 'List resources that InSpec finds.'
|
||||
# Note: if you want your command (or subcommand) to have dashes in it,
|
||||
# use underscores where you want a dash, and Thor will convert them.
|
||||
# Thor will fail to find a command that is directly named with dashes.
|
||||
subcommand_desc '<%= command_name_snake %> [COMMAND]', 'Your Usage Message Here'
|
||||
|
||||
# The usual rhythm for a Thor CLI file is description, options, command method.
|
||||
# Thor just has you call DSL methods in sequence prior to each command.
|
||||
# Let's make a command, 'core', that lists all of the resources included with InSpec.
|
||||
|
||||
# First, provide a usage / description. This will appear in `inspec help list-resources`.
|
||||
desc 'core [OPTIONS]', 'List resources that are included with InSpec.'
|
||||
# Let's make a command, 'do_something'. This will then be available
|
||||
# as `inspec <%= command_name_dashes %> do-something
|
||||
# (Change this method name to be something sensible for your plugin.)
|
||||
|
||||
# Let's include an option, -s, to summarize the list.
|
||||
# First, provide a usage / description. This will appear
|
||||
# in `inspec help <%= command_name_dashes %>`.
|
||||
# As this is a usage message, you should write the command as it should appear
|
||||
# to the user (if you want it to have dashes, use dashes)
|
||||
desc 'do-something WHAT [OPTIONS]', 'Does something'
|
||||
|
||||
# Let's include an option, -s, to summarize
|
||||
# Refer to the Thors docs; there is a lot you can do here.
|
||||
option :summary, desc: 'Include a total at the bottom', \
|
||||
type: :boolean, default: true, aliases: [:s]
|
||||
|
||||
# OK, now the actual method itself. If you provide params, you're telling Thor that
|
||||
# you accept CLI arguments after all options have been consumed. Let's accept a
|
||||
# pattern, assumed to be a wildcard substring. If we provide a default, the CLI arg becomes optional.
|
||||
def core(pattern = /.+/)
|
||||
# The code here will *only* be executed if someone actually runs
|
||||
# `inspec list-resources core`. So, we can lazily wait to load
|
||||
# expensive things here. However, InSpec has in fact already
|
||||
# loaded the Resources, so we don't have anything to load.
|
||||
# you accept CLI arguments after all options have been consumed.
|
||||
# Note again that the method name has an underscore, but when invoked
|
||||
# on the CLI, use a dash.
|
||||
def do_something(what = 'nothing')
|
||||
# The code here will *only* be executed if someone actually
|
||||
# runs `inspec <%= command_name_dashes %> do-something`.
|
||||
|
||||
# If we were passed a CLI arg, wrap the arg in Regexp matchers so
|
||||
# we will match anywhere in the name.
|
||||
unless pattern == /.+/
|
||||
pattern = Regexp.new('.*' + pattern + '.*')
|
||||
end
|
||||
# `what` will be the command line arg
|
||||
# `options` will be a hash of CLI option values
|
||||
|
||||
# This gets a bit into InSpec innards; but this is simply a Hash.
|
||||
registry = Inspec::Resource.default_registry
|
||||
resource_names = registry.keys.grep(pattern).sort
|
||||
# Talk to the user using the `ui` object (see Inspec::UI)
|
||||
# ui.error('Whoops!')
|
||||
|
||||
# One day we'll have nice UI support.
|
||||
resource_names.each { |name| puts name }
|
||||
|
||||
if options[:summary]
|
||||
puts '-' * 30
|
||||
puts "#{resource_names.count} resources total"
|
||||
end
|
||||
ui.warning('This is a generated plugin with a default implementation. Edit lib/<%= plugin_name %>/cli_command.rb to make it do what you want.')
|
||||
ui.exit(:success) # or :usage_error
|
||||
end
|
||||
|
||||
# A neat idea for future work would be to add another command, perhaps
|
||||
# 'resource-pack', which examines a possibly remote resource pack and
|
||||
# enumerates the resources it defines.
|
||||
|
||||
# Another idea might be to fetch a profile, and list the resources actually
|
||||
# used in the controls of the profile, along with counts.
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,17 +38,17 @@ module InspecPlugins
|
|||
|
||||
# We'd like this to be list-resources, but Thor does not support hyphens
|
||||
# see https://github.com/erikhuda/thor/pull/613
|
||||
cli_command :listresources do
|
||||
# Calling this hook doesn't mean list-resources is being executed - just
|
||||
cli_command :<%= command_name_snake %> do
|
||||
# Calling this hook doesn't mean the subcommand is being executed - just
|
||||
# that we should be ready to do so. So, load the file that defines the
|
||||
# functionality.
|
||||
# For example, InSpec will activate this hook when `inspec help` is
|
||||
# executed, so that this plugin's usage message will be included in the help.
|
||||
require 'inspec-resource-lister/cli_command'
|
||||
require '<%= plugin_name %>/cli_command'
|
||||
|
||||
# Having loaded our functionality, return a class that will let the
|
||||
# CLI engine tap into it.
|
||||
InspecPlugins::ResourceLister::CliCommand
|
||||
InspecPlugins::<%= module_name %>::CliCommand
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
# Functional Testing Area for Example Plugins
|
||||
|
||||
## What example tests are provided?
|
||||
|
||||
Here, since this is a CliCommand plugin, we provide one set of functional tests:
|
||||
|
||||
* inspec_resource_lister_test.rb - Runs `inspec resource-lister` in several circumstances, and verifies the output from the process.
|
||||
# Functional Testing Area for Plugins
|
||||
|
||||
## What are functional tests?
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Unit Testing Area for Example Plugins
|
||||
# Unit Testing Area for Plugins
|
||||
|
||||
## What Example Tests are Provided?
|
||||
|
||||
Here, since this is a CliCommand plugin, we provide two sets of unit tests:
|
||||
## What Tests are Provided?
|
||||
|
||||
* plugin_def_test.rb - Would be useful in any plugin. Verifies that the plugin is properly detected and registered.
|
||||
* cli_args_test.rb - Verifies that the expected commands are present, and that they have the expected options and args.
|
||||
<% if hooks.key?(:cli_command) %>
|
||||
* cli_args_test.rb - Tests the CLI options for a CLI Command plugin
|
||||
<% end %>
|
||||
|
||||
## What are Unit Tests?
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
# This unit test performs some tests to verify that the command line options for
|
||||
# inspec-resource-lister are correct.
|
||||
# <%= plugin_name %> are correct.
|
||||
|
||||
# Include our test harness
|
||||
require_relative '../helper'
|
||||
|
||||
# Load the class under test, the CliCommand definition.
|
||||
require 'inspec-resource-lister/cli_command'
|
||||
require '<%= plugin_name %>/cli_command'
|
||||
|
||||
# Because InSpec is a Spec-style test suite, we're going to use MiniTest::Spec
|
||||
# here, for familiar look and feel. However, this isn't InSpec (or RSpec) code.
|
||||
describe InspecPlugins::ResourceLister::CliCommand do
|
||||
describe InspecPlugins::<%= module_name %>::CliCommand do
|
||||
|
||||
# When writing tests, you can use `let` to create variables that you
|
||||
# can reference easily.
|
||||
|
@ -17,14 +17,17 @@ describe InspecPlugins::ResourceLister::CliCommand do
|
|||
# This is the CLI Command implementation class.
|
||||
# It is a subclass of Thor, which is a CLI framework.
|
||||
# This unit test file is mostly about verifying the Thor settings.
|
||||
let(:cli_class) { InspecPlugins::ResourceLister::CliCommand }
|
||||
let(:cli_class) { InspecPlugins::<%= module_name %>::CliCommand }
|
||||
|
||||
# This is a Hash of Structs that tells us details of options for the 'core' subcommand.
|
||||
let(:core_options) { cli_class.all_commands['core'].options }
|
||||
# From this point onwards, this test file assumes you did not rename
|
||||
# the provided 'do_something' subcommand. As you implement your plugin,
|
||||
# modify and add to the lines below to test your actual options.
|
||||
|
||||
# This is a Hash of Structs that tells us details of options for the 'do_something' subcommand.
|
||||
let(:do_something_options) { cli_class.all_commands['do_something'].options }
|
||||
|
||||
# To group tests together, you can nest 'describe' in minitest/spec
|
||||
# (that is discouraged in InSpec control code.)
|
||||
describe 'the core command' do
|
||||
describe 'the do-something subcommand' do
|
||||
|
||||
# Some tests through here use minitest Expectations, which attach to all
|
||||
# Objects, and begin with 'must' (positive) or 'wont' (negative)
|
||||
|
@ -32,32 +35,32 @@ describe InspecPlugins::ResourceLister::CliCommand do
|
|||
|
||||
# Option count OK?
|
||||
it "should take one option" do
|
||||
core_options.count.must_equal(1)
|
||||
do_something_options.count.must_equal(1)
|
||||
end
|
||||
|
||||
# Summary option
|
||||
describe "the summary option" do
|
||||
it "should be present" do
|
||||
core_options.keys.must_include(:summary)
|
||||
do_something_options.keys.must_include(:summary)
|
||||
end
|
||||
it "should have a description" do
|
||||
core_options[:summary].description.wont_be_nil
|
||||
do_something_options[:summary].description.wont_be_nil
|
||||
end
|
||||
it "should not be required" do
|
||||
core_options[:summary].required.wont_equal(true)
|
||||
do_something_options[:summary].required.wont_equal(true)
|
||||
end
|
||||
it "should have a single-letter alias" do
|
||||
core_options[:summary].aliases.must_include(:s)
|
||||
do_something_options[:summary].aliases.must_include(:s)
|
||||
end
|
||||
end
|
||||
|
||||
# Argument count
|
||||
# The 'core' command takes one optional argument. According to the
|
||||
# metaprogramming rules of Ruby, the core() method should thus have an
|
||||
# The 'do-something' command takes one optional argument. According to the
|
||||
# metaprogramming rules of Ruby, the do_something() method should thus have an
|
||||
# arity of -1. See http://ruby-doc.org/core-2.5.1/Method.html#method-i-arity
|
||||
# for how that number is caclulated.
|
||||
it "should take one optional argument" do
|
||||
cli_class.instance_method(:core).arity.must_equal(-1)
|
||||
cli_class.instance_method(:do_something).arity.must_equal(-1)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -58,30 +58,40 @@ class InitPluginCli < MiniTest::Test
|
|||
/\#\s#{plugin}\s=>\s#{module_name}/,
|
||||
/module\s#{module_name}/,
|
||||
/plugin_name\s+:'#{plugin}'/,
|
||||
# TODO: Default assumes cli
|
||||
#cli_command :listresources
|
||||
#require 'inspec-resource-lister/cli_command'
|
||||
#InspecPlugins::ResourceLister::CliCommand
|
||||
# Default assumes one cli hook
|
||||
/cli_command :my_command/,
|
||||
/require\s'#{plugin}\/cli_command'/,
|
||||
/InspecPlugins::#{module_name}::CliCommand/,
|
||||
],
|
||||
File.join(plugin, 'lib', plugin, 'version.rb') => [
|
||||
/module\s#{module_name}/,
|
||||
],
|
||||
File.join(plugin, 'lib', plugin, 'cli_command.rb') => [
|
||||
# TODO: decide on hook approach
|
||||
/module\sInspecPlugins::#{module_name}/,
|
||||
/\#\smakes\s`inspec\smy-command\s\.\.\.`\swork\./,
|
||||
/subcommand_desc\s'my_command\s\[COMMAND\]'/,
|
||||
/\#\sas\s`inspec\smy-command\sdo-something/,
|
||||
/\#\sin\s`inspec\shelp\smy-command`/,
|
||||
/\#\sruns\s`inspec\smy-command\sdo-something`./,
|
||||
/Edit\slib\/#{plugin}\/cli_command\.rb\sto\smake\sit\sdo/,
|
||||
],
|
||||
File.join(plugin, 'test', 'helper.rb') => [], # No interpolation
|
||||
File.join(plugin, 'test', 'functional', 'README.md') => [], # No interpolation
|
||||
File.join(plugin, 'test', 'functional', snake_case + '_test.rb') => [
|
||||
# Whatever goes here
|
||||
],
|
||||
File.join(plugin, 'test', 'unit', 'plugin_def_test.rb') => [
|
||||
/require\s'#{plugin}\/plugin'/,
|
||||
/describe\sInspecPlugins::#{module_name}::Plugin\sdo/,
|
||||
/let\(:plugin_name\)\s\{ \:'#{plugin}\' \}/,
|
||||
/describe InspecPlugins::#{module_name}::Plugin\sdo/,
|
||||
/let\(:plugin_name\) \{ \:'#{plugin}\' \}/,
|
||||
],
|
||||
File.join(plugin, 'test', 'unit', 'cli_args_test.rb') => [
|
||||
# require 'inspec-resource-lister/cli_command'
|
||||
# describe InspecPlugins::ResourceLister::CliCommand do
|
||||
# let(:cli_class) { InspecPlugins::ResourceLister::CliCommand }
|
||||
/require '#{plugin}\/cli_command'/,
|
||||
/describe InspecPlugins::#{module_name}::CliCommand do/,
|
||||
/let\(\:cli_class\) \{ InspecPlugins::#{module_name}::CliCommand \}/,
|
||||
],
|
||||
File.join(plugin, 'test', 'unit', 'README.md') => [
|
||||
/cli_args_test\.rb/,
|
||||
],
|
||||
}.each do |path, regexen|
|
||||
full_path = File.join(dir, path)
|
||||
|
|
Loading…
Reference in a new issue