Merge pull request #4401 from inspec/cw/input-from-cli

inputs: Accept bare input from the command line
This commit is contained in:
Clinton Wolfe 2019-08-28 15:41:21 -04:00 committed by GitHub
commit a6bd7ec4b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 132 additions and 12 deletions

View file

@ -44,13 +44,9 @@ When the above profile is executed by using `inspec exec rock_critic`, you would
Test Summary: 0 successful, 1 failure, 0 skipped
```
That result clearly won't do. Let's override the input's default value. Create a file, `custom_amps.yml`:
That result clearly won't do. Let's override the input's default value.
```yaml
amplifier_max_volume: 11
```
We can now run that profile with `inspec exec rock_critic --input-file custom_amps.yaml`:
We can now run that profile with `inspec exec rock_critic --input amplifier_max_volume=11`:
```
11
@ -67,11 +63,12 @@ That said, any profile that uses the DSL keyword `input()` (or the deprecated `a
### How can I set Inputs?
As installed (without specialized plugins), Chef InSpec supports five ways of setting inputs:
As installed (without specialized plugins), Chef InSpec supports several ways of setting inputs:
* Inline in control code, using `input('input_name', value: 42)`.
* In profile `inspec.yml` metadata files
* Using the CLI option `--input-file somefile.yaml`
* Using the CLI option `--input name1=value1 name2=value2...` to read directly from the command line
* Using the CLI option `--input-file somefile.yaml` to read inputs from files
* In kitchen-inspec, using the `verifier/inputs` settings
* In the Audit Cookbook, using the `node[:audit][:inputs]`
@ -83,7 +80,7 @@ In addition, Chef InSpec supports Input Plugins, which can provide optional inte
Briefly:
inline DSL < metadata < ( cli-input-file or kitchen-inspec or audit-cookbook )
inline DSL < metadata < ( cli-input-file or kitchen-inspec or audit-cookbook ) < cli --input
In addition, for inherited profiles:
@ -122,7 +119,7 @@ As packaged, Chef InSpec uses the following priority values:
| CLI `--input-file` option | 40 | No |
| inspec-kitchen `inputs:` section | 40 | No |
| audit cookbook `node[:audit][:inputs]` | 40 | No |
| CLI `--input` option | 50 | No |
### What happened to "Attributes"?
@ -246,13 +243,35 @@ an_input: a_value
another_input: another_value
```
CLI-set inputs have a priority of 40.
CLI-input-file-set inputs have a priority of 40.
As of Chef InSpec 4.3.2, this mechanism has the following limitations:
1. No [input options](#input-options-reference) may be set - only the name and value.
2. Because the CLI is outside the scope of any individual profile and the inputs don't take options, the inputs are clumsily copied into every profile, effectively making the CLI mechanism global.
## Setting Input values using `--input`
You may also provide inputs and values directly on the command line:
```yaml
inspec exec my_profile --input input_name=input_value
```
To set multiple inputs, say:
```yaml
inspec exec my_profile --input input_name1=input_value1 name2=value2
```
Do not repeat the `--input` flag; that will override the previous setting.
CLI-set inputs have a priority of 50.
As of Chef InSpec 4.12, this mechanism has the following limitations:
1. No [input options](#input-options-reference) may be set - only the name and value.
2. Because the CLI is outside the scope of any individual profile and the inputs don't take options, the inputs are clumsily copied into every profile, effectively making the CLI mechanism global.
## Input Options Reference
### Name

View file

@ -133,6 +133,8 @@ module Inspec
option :reporter, type: :array,
banner: "one two:/output/file/path",
desc: "Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, junit, yaml"
option :input, type: :array, banner: "name1=value1 name2=value2",
desc: "Specify one or more inputs directly on the command line, as --input NAME=VALUE"
option :input_file, type: :array,
desc: "Load one or more input files, a YAML file with values for the profile to use"
option :attrs, type: :array,

View file

@ -135,10 +135,37 @@ module Inspec
bind_inputs_from_metadata(profile_name, sources[:profile_metadata])
bind_inputs_from_input_files(profile_name, sources[:cli_input_files])
bind_inputs_from_runner_api(profile_name, sources[:runner_api])
bind_inputs_from_cli_args(profile_name, sources[:cli_input_arg])
end
private
def bind_inputs_from_cli_args(profile_name, input_list)
# TODO: move this into a core plugin
return if input_list.nil?
return if input_list.empty?
# These arrive as an array of "name=value" strings
# If the user used a comma, we'll see unfortunately see it as "name=value," pairs
input_list.each do |pair|
unless pair.include?("=")
if pair.end_with?(".yaml")
raise ArgumentError, "ERROR: --input is used for individual input values, as --input name=value. Use --input-file to load a YAML file."
else
raise ArgumentError, "ERROR: An '=' is required when using --input. Usage: --input input_name1=input_value1 input2=value2"
end
end
input_name, input_value = pair.split("=")
evt = Inspec::Input::Event.new(
value: input_value.chomp(","), # Trim trailing comma if any
provider: :cli,
priority: 50
)
find_or_register_input(input_name, profile_name, event: evt)
end
end
def bind_inputs_from_runner_api(profile_name, input_hash)
# TODO: move this into a core plugin

View file

@ -137,7 +137,8 @@ module Inspec
# Remaining args are possible sources of inputs
cli_input_files: options[:runner_conf][:input_file], # From CLI --input-file
profile_metadata: metadata,
runner_api: options[:runner_conf][:inputs] # This is the route the audit_cookbook and kitchen-inspec take
runner_api: options[:runner_conf][:inputs], # This is the route the audit_cookbook and kitchen-inspec take
cli_input_arg: options[:runner_conf][:input] # The --input name=value CLI option
)
@runner_context =

View file

@ -43,6 +43,11 @@ describe "inputs" do
line = lines.detect { |l| l.include? "--attrs" }
line.wont_be_nil
end
it "includes the --input option" do
result = run_inspec_process("exec help", lock: true) # --no-create-lockfile option breaks usage help
assert_match(/--input\s/, result.stdout) # Careful not to match --input-file
end
end
describe "when using a cli-specified file" do
@ -142,6 +147,56 @@ describe "inputs" do
end
end
describe "when using the --input inline raw input flag CLI option" do
let(:result) { run_inspec_process("exec #{inputs_profiles_path}/cli #{input_opt} #{control_opt}", json: true) }
let(:control_opt) { "" }
describe "when the --input is used once with one value" do
let(:input_opt) { "--input test_input_01=value_from_cli_01" }
let(:control_opt) { "--controls test_control_01" }
it("correctly reads the input") { result.must_have_all_controls_passing }
end
describe "when the --input is used once with two values" do
let(:input_opt) { "--input test_input_01=value_from_cli_01 test_input_02=value_from_cli_02" }
it("correctly reads the input") { result.must_have_all_controls_passing }
end
describe "when the --input is used once with two values and a comma" do
let(:input_opt) { "--input test_input_01=value_from_cli_01, test_input_02=value_from_cli_02" }
it("correctly reads the input") { result.must_have_all_controls_passing }
end
describe "when the --input is used twice with one value each" do
let(:input_opt) { "--input test_input_01=value_from_cli_01 --input test_input_02=value_from_cli_02" }
let(:control_opt) { "--controls test_control_02" }
# Expected, though unfortunate, behavior is to only notice the second input
it("correctly reads the input") { result.must_have_all_controls_passing }
end
describe "when the --input is used with no equal sign" do
let(:input_opt) { "--input value_from_cli_01" }
it "does not run and provides an error message" do
output = result.stdout
assert_includes "ERROR", output
assert_includes "An '=' is required", output
assert_includes "input_name_1=input_value_1", output
assert_equal 1, result.exit_status
end
end
describe "when the --input is used with a .yaml extension" do
let(:input_opt) { "--input myfile.yaml" }
it "does not run and provides an error message" do
output = result.stdout
assert_includes "ERROR", output
assert_includes "individual input values", output
assert_includes "Use --input-file", output
assert_equal 1, result.exit_status
end
end
end
describe "when accessing inputs in a variety of scopes using the DSL" do
it "is able to read the inputs using the input keyword" do
cmd = "exec #{inputs_profiles_path}/scoping"

View file

@ -0,0 +1,10 @@
control "test_control_01" do
describe input("test_input_01", value: "value_from_dsl") do
it { should cmp "value_from_cli_01"}
end
end
control "test_control_02" do
describe input("test_input_02", value: "value_from_dsl") do
it { should cmp "value_from_cli_02"}
end
end

View file

@ -0,0 +1,6 @@
name: cli
license: Apache-2.0
summary: Profile to exercise setting inputs using the --input CLI option
version: 0.1.0
supports:
platform: os