Merge pull request #5490 from inspec/nm/inspec-automate

Added alias command `automate` for `inspec compliance`
This commit is contained in:
Clinton Wolfe 2021-05-05 12:14:30 -04:00 committed by GitHub
commit e7cc5e94f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 257 additions and 49 deletions

View file

@ -177,17 +177,18 @@ You should now be able to run:
```bash
$ inspec --help
Commands:
inspec archive PATH # archive a profile to tar.gz (default) ...
inspec check PATH # verify all tests at the specified PATH
inspec compliance SUBCOMMAND ... # Chef Compliance commands
inspec detect # detect the target OS
inspec exec PATH(S) # run all test files at the specified PATH.
inspec help [COMMAND] # Describe available commands or one spe...
inspec init TEMPLATE ... # Scaffolds a new project
inspec json PATH # read all tests in PATH and generate a ...
inspec shell # open an interactive debugging shell
inspec supermarket SUBCOMMAND ... # Supermarket commands
inspec version # prints the version of this tool
inspec archive PATH # archive a profile to tar.gz (default) ...
inspec check PATH # verify all tests at the specified PATH
inspec automate SUBCOMMAND ... # Chef Automate commands
inspec compliance SUBCOMMAND ... # Chef Automate commands (backwards compatible alias)
inspec detect # detect the target OS
inspec exec PATH(S) # run all test files at the specified PATH.
inspec help [COMMAND] # Describe available commands or one spe...
inspec init TEMPLATE ... # Scaffolds a new project
inspec json PATH # read all tests in PATH and generate a ...
inspec shell # open an interactive debugging shell
inspec supermarket SUBCOMMAND ... # Supermarket commands
inspec version # prints the version of this tool
Options:
[--diagnose], [--no-diagnose] # Show diagnostics (versions, configurations)

View file

@ -2,11 +2,13 @@
## Purpose
The `compliance` set of subcommands handle user-initiated communication with Chef Automate. The commands are provided so that a user can interact with an Automate installation.
The `automate` set of subcommands handle user-initiated communication with Chef Automate. The commands are provided so that a user can interact with an Automate installation.
`inspec compliance` is somewhat analogous to `knife` in that it can be used to upload, download, and manage profiles for distribution to other clients.
`inspec automate` is somewhat analogous to `knife` in that it can be used to upload, download, and manage profiles for distribution to other clients.
When Automate initiates scans, the `compliance` subcommand is not used.
When Automate initiates scans, the `automate` subcommand is not used.
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way
## Operational Notes
@ -65,8 +67,9 @@ There are several other minor commands not listed here - see `lib/cli.rb` for a
### login
Saves a credentials file locally. Future invocations of `inspec compliance` use the credentials file to authenticate.
Saves a credentials file locally. Future invocations of `inspec automate` or `inspec compliance` use the credentials file to authenticate.
`be inspec automate login --user=admin --token='1234567890asdfghjkl' --insecure https://chef-automate.test` or
`be inspec compliance login --user=admin --token='1234567890asdfghjkl' --insecure https://chef-automate.test`
Here are the results of running login, from `.inspec/compliance/config.json`:

View file

@ -184,10 +184,16 @@ Below are some examples of using `exec` with different test locations:
Chef Automate:
```
inspec compliance login
inspec automate login
inspec exec compliance://username/linux-baseline
```
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way:
```
inspec compliance login
```
Chef Supermarket:
```
inspec exec supermarket://username/linux-baseline

View file

@ -35,11 +35,17 @@ suites:
and then run the following command:
```bash
inspec compliance login https://compliance.test --user admin --insecure --token ''
inspec automate login https://compliance.test --user admin --insecure --token ''
```
where `--insecure` is required when using self-signed certificates.
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way:
```bash
inspec compliance login https://compliance.test --user admin --insecure --token ''
```
Use a compliance profile from the Chef Supermarket:
```YML

View file

@ -218,9 +218,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
Automate:
```
#{Inspec::Dist::EXEC_NAME} compliance login
#{Inspec::Dist::EXEC_NAME} automate login
#{Inspec::Dist::EXEC_NAME} exec compliance://username/linux-baseline
```
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way:
```
#{Inspec::Dist::EXEC_NAME} compliance login
```
Supermarket:
```

View file

@ -117,6 +117,15 @@ module Inspec::Plugin::V2
# `inspec dosomething` => activate the :dosomething hook
activate_me ||= cli_args.include?(act.activator_name.to_s)
# Only one compliance command to be activated at one time.
# Since both commands are defined in the same class,
# activators were not getting fetched uniquely.
if cli_args.include?("automate") && act.activator_name.to_s.eql?("compliance")
activate_me = false
elsif cli_args.include?("compliance") && act.activator_name.to_s.eql?("automate")
activate_me = false
end
# OK, activate.
if activate_me
act.activate

View file

@ -6,24 +6,50 @@ This extensions offers the following features:
- execute profiles directly from Chef Automate/Chef Compliance locally
- upload a local profile to Chef Automate/Chef Compliance
`inspec compliance` is a backwards compatible alias for `inspec automate` and works the same way.
To use the CLI, this InSpec add-on adds the following commands:
* `$ inspec automate login` - authentication of the API token against Chef Automate/Chef Compliance
* `$ inspec automate profiles` - list all available Compliance profiles
* `$ inspec exec compliance://profile` - runs a Compliance profile
* `$ inspec automate upload path/to/local/profile` - uploads a local profile to Chef Automate/Chef Compliance
* `$ inspec automate logout` - logout of Chef Automate/Chef Compliance
Similar to these CLI commands are:
* `$ inspec compliance login` - authentication of the API token against Chef Automate/Chef Compliance
* `$ inspec compliance profiles` - list all available Compliance profiles
* `$ inspec exec compliance://profile` - runs a Compliance profile
* `$ inspec compliance upload path/to/local/profile` - uploads a local profile to Chef Automate/Chef Compliance
* `$ inspec compliance logout` - logout of Chef Automate/Chef Compliance
Compliance profiles can be executed in two ways:
- via compliance exec: `inspec compliance exec profile`
- via compliance exec: `inspec automate exec profile` or `inspec compliance exec profile`
- via compliance scheme: `inspec exec compliance://profile`
## Usage
### Command options
```
$ inspec automate
Commands:
inspec automate download PROFILE # downloads a profile from Chef Compliance
inspec automate exec PROFILE # executes a Chef Compliance profile
inspec automate help [COMMAND] # Describe subcommands or one specific subcommand
inspec automate login SERVER # Log in to a Chef Automate/Chef Compliance SERVER
inspec automate logout # user logout from Chef Compliance
inspec automate profiles # list all available profiles in Chef Compliance
inspec automate upload PATH # uploads a local profile to Chef Compliance
inspec automate version # displays the version of the Chef Compliance server
```
or
```
$ inspec compliance
Commands:
@ -41,6 +67,12 @@ Commands:
You will need an API token for authentication. You can retrieve one via the admin section of your A2 web gui.
```
$ inspec automate login https://automate2.compliance.test --insecure --user 'admin' --token 'zuop..._KzE'
```
or
```
$ inspec compliance login https://automate2.compliance.test --insecure --user 'admin' --token 'zuop..._KzE'
```
@ -63,6 +95,12 @@ Example:
You will need an access token for authentication. You can retrieve one via [UI](https://docs.chef.io/api_delivery.html) or [CLI](https://docs.chef.io/ctl_delivery.html#delivery-token).
```
$ inspec automate login https://automate.compliance.test --insecure --user 'admin' --ent 'brewinc' --token 'zuop..._KzE'
```
or
```
$ inspec compliance login https://automate.compliance.test --insecure --user 'admin' --ent 'brewinc' --token 'zuop..._KzE'
```
@ -75,12 +113,42 @@ You will need an access token for authentication. You can retrieve one via:
You can choose the access token (`--token`) or the refresh token (`--refresh_token`)
```
$ inspec automate login https://compliance.test --user admin --insecure --token '...'
```
or
```
$ inspec compliance login https://compliance.test --user admin --insecure --token '...'
```
### List available profiles via Chef Compliance / Automate
```
$ inspec automate profiles
Available profiles:
-------------------
* base/apache
* base/linux
* base/mysql
* base/postgres
* base/ssh
* base/windows
* cis/cis-centos6-level1
* cis/cis-centos6-level2
* cis/cis-centos7-level1
* cis/cis-centos7-level2
* cis/cis-rhel7-level1
* cis/cis-rhel7-level2
* cis/cis-ubuntu12.04lts-level1
* cis/cis-ubuntu12.04lts-level2
* cis/cis-ubuntu14.04lts-level1
* cis/cis-ubuntu14.04lts-level2
```
or
```
$ inspec compliance profiles
Available profiles:
@ -105,6 +173,47 @@ Available profiles:
### Upload a profile to Chef Compliance / Automate
```
$ inspec automate version
Chef Compliance version: 1.0.11
➜ inspec git:(chris-rock/cc-error-not-loggedin) ✗ b inspec automate upload examples/profile
I, [2016-05-06T14:27:20.907547 #37592] INFO -- : Checking profile in examples/profile
I, [2016-05-06T14:27:20.907668 #37592] INFO -- : Metadata OK.
I, [2016-05-06T14:27:20.968584 #37592] INFO -- : Found 4 controls.
I, [2016-05-06T14:27:20.968638 #37592] INFO -- : Control definitions OK.
Profile is valid
Generate temporary profile archive at /var/folders/jy/2bnrfb4s36jbjtzllvhhyqhw0000gn/T/profile20160506-37592-1tf326f.tar.gz
I, [2016-05-06T14:27:21.020017 #37592] INFO -- : Generate archive /var/folders/jy/2bnrfb4s36jbjtzllvhhyqhw0000gn/T/profile20160506-37592-1tf326f.tar.gz.
I, [2016-05-06T14:27:21.024837 #37592] INFO -- : Finished archive generation.
Start upload to admin/profile
Uploading to Chef Compliance
Successfully uploaded profile
# display all profiles
$ inspec automate profiles
Available profiles:
-------------------
* admin/profile
* base/apache
* base/linux
* base/mysql
* base/postgres
* base/ssh
* base/windows
* cis/cis-centos6-level1
* cis/cis-centos6-level2
* cis/cis-centos7-level1
* cis/cis-centos7-level2
* cis/cis-rhel7-level1
* cis/cis-rhel7-level2
* cis/cis-ubuntu12.04lts-level1
* cis/cis-ubuntu12.04lts-level2
* cis/cis-ubuntu14.04lts-level1
* cis/cis-ubuntu14.04lts-level2
```
or
```
$ inspec compliance version
Chef Compliance version: 1.0.11
@ -168,17 +277,31 @@ $ inspec exec compliance://admin/apache-baseline#2.0.1
```
Download a specific version(2.0.2) of a profile when logged in with Automate:
```
$ inspec automate download compliance://admin/apache-baseline#2.0.2
```
or
```
$ inspec compliance download compliance://admin/apache-baseline#2.0.2
```
### To Logout from Chef Compliance
```
$ inspec automate logout
Successfully logged out
```
or
```
$ inspec compliance logout
Successfully logged out
```
## Integration Tests
At this point of time, InSpec is not able to pick up the token directly, therefore the integration test is semi-automatic at this point of time:

View file

@ -7,6 +7,11 @@ module InspecPlugins
require_relative "inspec-compliance/cli"
InspecPlugins::Compliance::CLI
end
cli_command :automate do
require_relative "inspec-compliance/cli"
InspecPlugins::Compliance::CLI
end
end
autoload :Configuration, "plugins/inspec-compliance/lib/inspec-compliance/configuration"

View file

@ -357,7 +357,7 @@ module InspecPlugins
Inspec::Log.debug(
"Received 200 from #{url}#{compliance_endpoint} - " \
"assuming target is a #{COMPLIANCE_PRODUCT_NAME} server"
"assuming target is a #{AUTOMATE_PRODUCT_NAME} server"
)
true
end

View file

@ -9,7 +9,7 @@ module InspecPlugins
class CannotDetermineServerType < StandardError; end
def login(options)
raise ArgumentError, "Please specify a server using `#{EXEC_NAME} compliance login https://SERVER`" unless options["server"]
raise ArgumentError, "Please specify a server using `#{EXEC_NAME} automate login https://SERVER` or `#{EXEC_NAME} compliance login https://SERVER`" unless options["server"]
options["server"] = URI("https://#{options["server"]}").to_s if URI(options["server"]).scheme.nil?
@ -179,7 +179,7 @@ module InspecPlugins
def self.compliance_verify_thor_options(o)
error_msg = []
error_msg.push("Please specify a server using `#{EXEC_NAME} compliance login https://SERVER`") if o["server"].nil?
error_msg.push("Please specify a server using `#{EXEC_NAME} automate login https://SERVER` or `#{EXEC_NAME} compliance login https://SERVER`") if o["server"].nil?
if o["user"].nil? && o["refresh_token"].nil?
error_msg.push("Please specify a `--user='USER'` or a `--refresh-token='TOKEN'`")

View file

@ -6,13 +6,12 @@ module InspecPlugins
module Compliance
class CLI < Inspec.plugin(2, :cli_command)
include Inspec::Dist
subcommand_desc "compliance SUBCOMMAND", "#{COMPLIANCE_PRODUCT_NAME} commands"
subcommand_desc "automate SUBCOMMAND or compliance SUBCOMMAND", "#{AUTOMATE_PRODUCT_NAME} commands"
# desc "login https://SERVER --insecure --user='USER' --ent='ENTERPRISE' --token='TOKEN'", 'Log in to a Chef Compliance/Chef Automate SERVER'
desc "login", "Log in to a #{COMPLIANCE_PRODUCT_NAME}/#{AUTOMATE_PRODUCT_NAME} SERVER"
desc "login", "Log in to a #{AUTOMATE_PRODUCT_NAME} SERVER"
long_desc <<-LONGDESC
`login` allows you to use InSpec with #{AUTOMATE_PRODUCT_NAME} or a #{COMPLIANCE_PRODUCT_NAME} Server
`login` allows you to use InSpec with #{AUTOMATE_PRODUCT_NAME} Server
You need to a token for communication. More information about token retrieval
is available at:
@ -24,11 +23,11 @@ module InspecPlugins
option :user, type: :string, required: false,
desc: "Username"
option :password, type: :string, required: false,
desc: "Password (#{COMPLIANCE_PRODUCT_NAME} Only)"
desc: "Password (#{AUTOMATE_PRODUCT_NAME} Only)"
option :token, type: :string, required: false,
desc: "Access token"
option :refresh_token, type: :string, required: false,
desc: "#{COMPLIANCE_PRODUCT_NAME} refresh token (#{COMPLIANCE_PRODUCT_NAME} Only)"
desc: "#{AUTOMATE_PRODUCT_NAME} refresh token (#{AUTOMATE_PRODUCT_NAME} Only)"
option :dctoken, type: :string, required: false,
desc: "Data Collector token (#{AUTOMATE_PRODUCT_NAME} Only)"
option :ent, type: :string, required: false,
@ -40,7 +39,7 @@ module InspecPlugins
puts "Stored configuration for Chef #{config["server_type"].capitalize}: #{config["server"]}' with user: '#{config["user"]}'"
end
desc "profiles", "list all available profiles in #{COMPLIANCE_PRODUCT_NAME}"
desc "profiles", "list all available profiles in #{AUTOMATE_PRODUCT_NAME}"
option :owner, type: :string, required: false,
desc: "owner whose profiles to list"
def profiles
@ -65,11 +64,11 @@ module InspecPlugins
exit 1
end
rescue InspecPlugins::Compliance::ServerConfigurationMissing
$stderr.puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} compliance login`"
$stderr.puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
exit 1
end
desc "exec PROFILE", "executes a #{COMPLIANCE_PRODUCT_NAME} profile"
desc "exec PROFILE", "executes a #{AUTOMATE_PRODUCT_NAME} profile"
exec_options
def exec(*tests)
compliance_config = InspecPlugins::Compliance::Configuration.new
@ -91,7 +90,7 @@ module InspecPlugins
exit 1
end
desc "download PROFILE", "downloads a profile from #{COMPLIANCE_PRODUCT_NAME}"
desc "download PROFILE", "downloads a profile from #{AUTOMATE_PRODUCT_NAME}"
option :name, type: :string,
desc: "Name of the archive filename (file type will be added)"
def download(profile_name)
@ -116,12 +115,12 @@ module InspecPlugins
file_name = fetcher.fetch(o.name || id)
puts "Profile stored to #{file_name}"
else
puts "Profile #{profile_name} is not available in #{COMPLIANCE_PRODUCT_NAME}."
puts "Profile #{profile_name} is not available in #{AUTOMATE_PRODUCT_NAME}."
exit 1
end
end
desc "upload PATH", "uploads a local profile to #{COMPLIANCE_PRODUCT_NAME}"
desc "upload PATH", "uploads a local profile to #{AUTOMATE_PRODUCT_NAME}"
option :overwrite, type: :boolean, default: false,
desc: "Overwrite existing profile on Server."
option :owner, type: :string, required: false,
@ -167,7 +166,7 @@ module InspecPlugins
# determine user information
if (config["token"].nil? && config["refresh_token"].nil?) || config["user"].nil?
error.call("Please login via `#{EXEC_NAME} compliance login`")
error.call("Please login via `#{EXEC_NAME} #{subcommand_name} login`")
end
# read profile name from inspec.yml
@ -202,11 +201,8 @@ module InspecPlugins
puts "Start upload to #{config["owner"]}/#{profile_name}"
pname = ERB::Util.url_encode(profile_name)
if InspecPlugins::Compliance::API.is_automate_server?(config) || InspecPlugins::Compliance::API.is_automate2_server?(config)
puts "Uploading to #{AUTOMATE_PRODUCT_NAME}"
else
puts "Uploading to #{COMPLIANCE_PRODUCT_NAME}"
end
puts "Uploading to #{AUTOMATE_PRODUCT_NAME}"
success, msg = InspecPlugins::Compliance::API.upload(config, config["owner"], pname, archive_path)
# delete temp file if it was temporary generated
@ -221,7 +217,7 @@ module InspecPlugins
end
end
desc "version", "displays the version of the #{COMPLIANCE_PRODUCT_NAME} server"
desc "version", "displays the version of the #{AUTOMATE_PRODUCT_NAME} server"
def version
config = InspecPlugins::Compliance::Configuration.new
info = InspecPlugins::Compliance::API.version(config)
@ -233,11 +229,11 @@ module InspecPlugins
exit 1
end
rescue InspecPlugins::Compliance::ServerConfigurationMissing
puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} compliance login`"
puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
exit 1
end
desc "logout", "user logout from #{COMPLIANCE_PRODUCT_NAME}"
desc "logout", "user logout from #{AUTOMATE_PRODUCT_NAME}"
def logout
config = InspecPlugins::Compliance::Configuration.new
unless config.supported?(:oidc) || config["token"].nil? || config["server_type"] == "automate"
@ -258,9 +254,13 @@ module InspecPlugins
def loggedin(config)
serverknown = !config["server"].nil?
puts "You need to login first with `#{EXEC_NAME} compliance login`" unless serverknown
puts "You need to login first with `#{EXEC_NAME} #{subcommand_name} login`" unless serverknown
serverknown
end
def subcommand_name
@_invocations[Inspec::InspecCLI]&.first || "automate"
end
end
# register the subcommand to InSpec CLI registry

View file

@ -34,13 +34,13 @@ module InspecPlugins
if config["token"].nil? && config["refresh_token"].nil?
if config["server_type"] == "automate"
server = "automate"
msg = "#{EXEC_NAME} compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN"
msg = "#{EXEC_NAME} [automate|compliance] login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN"
elsif config["server_type"] == "automate2"
server = "automate2"
msg = "#{EXEC_NAME} compliance login https://your_automate2_server --user USER --token APITOKEN"
msg = "#{EXEC_NAME} [automate|compliance] login https://your_automate2_server --user USER --token APITOKEN"
else
server = "compliance"
msg = "#{EXEC_NAME} compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
msg = "#{EXEC_NAME} [automate|compliance] login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
end
raise Inspec::FetcherFailure, <<~EOF
@ -112,7 +112,7 @@ module InspecPlugins
end
def to_s
"#{COMPLIANCE_PRODUCT_NAME} Profile Loader"
"#{AUTOMATE_PRODUCT_NAME} Profile Loader"
end
private
@ -136,6 +136,7 @@ module InspecPlugins
if m.nil?
raise "Unable to determine compliance profile name. This can be caused by " \
"an incorrect server in your configuration. Try to login to compliance " \
"via the `#{EXEC_NAME} automate login` command or " \
"via the `#{EXEC_NAME} compliance login` command."
end

View file

@ -50,4 +50,54 @@ class ComplianceCli < Minitest::Test
assert_exit_code 0, out # TODO: make this error
end
## testing automate command for compliances
def test_help_output_using_automate_cmd
out = run_inspec_process("automate help")
assert_includes out.stdout, "inspec automate exec PROFILE"
assert_exit_code 0, out
end
def test_logout_command_using_automate_cmd
out = run_inspec_process("automate logout")
assert_includes out.stdout, ""
assert_exit_code 0, out
end
def test_error_login_with_invalid_url_using_automate_cmd
out = run_inspec_process("automate login")
assert_includes out.stderr, 'ERROR: "inspec automate login" was called with no arguments'
assert_exit_code 1, out
end
def test_profile_list_without_auth_using_automate_cmd
out = run_inspec_process("automate profiles")
assert_includes out.stdout, "You need to login first with `inspec automate login`"
assert_exit_code 0, out # TODO: make this error
end
def test_error_upload_without_args_using_automate_cmd
out = run_inspec_process("automate upload")
assert_includes out.stderr, 'ERROR: "inspec automate upload" was called with no arguments'
assert_exit_code 1, out
end
def test_error_upload_with_fake_path_using_automate_cmd
out = run_inspec_process("automate upload /path/to/dir")
assert_includes out.stdout, "You need to login first with `inspec automate login`"
assert_exit_code 0, out # TODO: make this error
end
end