mirror of
https://github.com/inspec/inspec
synced 2024-11-10 15:14:23 +00:00
Merge pull request #3935 from inspec/cw/add-license-gating
Add license acceptance to InSpec v4
This commit is contained in:
commit
4348e8fa07
7 changed files with 187 additions and 13 deletions
31
Rakefile
31
Rakefile
|
@ -8,6 +8,7 @@ require 'passgen'
|
|||
require 'train'
|
||||
require_relative 'tasks/maintainers'
|
||||
require_relative 'tasks/spdx'
|
||||
require 'fileutils'
|
||||
|
||||
Bundler::GemHelper.install_tasks name: 'inspec'
|
||||
|
||||
|
@ -70,16 +71,38 @@ namespace :test do
|
|||
end or fail 'Failures'
|
||||
end
|
||||
|
||||
task :accept_license do
|
||||
FileUtils.mkdir_p(File.join(Dir.home, '.chef', 'accepted_licenses'))
|
||||
# If the user has not accepted the license, touch the acceptance
|
||||
# file, but also touch a marker that it is only for testing.
|
||||
unless File.exist?(File.join(Dir.home, '.chef', 'accepted_licenses', 'inspec'))
|
||||
puts "\n\nTemporarily accepting Chef user license for the duration of testing...\n"
|
||||
FileUtils.touch(File.join(Dir.home, '.chef', 'accepted_licenses', 'inspec'))
|
||||
FileUtils.touch(File.join(Dir.home, '.chef', 'accepted_licenses', 'inspec.for_testing'))
|
||||
end
|
||||
|
||||
# Regardless of what happens, when this process exits, check for cleanup.
|
||||
at_exit do
|
||||
if File.exist?(File.join(Dir.home, '.chef', 'accepted_licenses', 'inspec.for_testing'))
|
||||
puts "\n\nRemoving temporary Chef user license acceptance file that was placed for test duration.\n"
|
||||
FileUtils.rm_f(File.join(Dir.home, '.chef', 'accepted_licenses', 'inspec'))
|
||||
FileUtils.rm_f(File.join(Dir.home, '.chef', 'accepted_licenses', 'inspec.for_testing'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rake::TestTask.new(:functional) do |t|
|
||||
t.libs << 'test'
|
||||
t.test_files = Dir.glob([
|
||||
'test/functional/**/*_test.rb',
|
||||
'lib/plugins/inspec-*/test/functional/**/*_test.rb',
|
||||
])
|
||||
t.warning = false
|
||||
t.warning = false # This just complains about things in underlying libraries
|
||||
t.verbose = true
|
||||
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
|
||||
end
|
||||
# Inject a prerequisite task
|
||||
task :functional => [:accept_license]
|
||||
|
||||
# Functional tests on Windows take a bit to run. This
|
||||
# optionally takes a env to breake the tests up into 3 workers.
|
||||
|
@ -93,10 +116,12 @@ namespace :test do
|
|||
|
||||
t.libs << 'test'
|
||||
t.test_files = files
|
||||
t.warning = false
|
||||
t.warning = false # This just complains about things in underlying libraries
|
||||
t.verbose = true
|
||||
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
|
||||
end
|
||||
# Inject a prerequisite task
|
||||
task :'functional:windows' => [:accept_license]
|
||||
|
||||
task :resources do
|
||||
tests = Dir['test/resource/*_test.rb']
|
||||
|
@ -122,6 +147,8 @@ namespace :test do
|
|||
FileUtils.rm(destination)
|
||||
end
|
||||
end
|
||||
# Inject a prerequisite task
|
||||
task :'integration' => [:accept_license]
|
||||
|
||||
task :ssh, [:target] do |_t, args|
|
||||
tests_path = File.join(File.dirname(__FILE__), 'test', 'integration', 'test', 'integration', 'default')
|
||||
|
|
|
@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|||
spec.required_ruby_version = '>= 2.3'
|
||||
|
||||
spec.add_dependency 'train-core', '~> 2.0'
|
||||
spec.add_dependency 'license-acceptance', '~> 0.2'
|
||||
spec.add_dependency 'thor', '~> 0.20'
|
||||
spec.add_dependency 'json', '>= 1.8', '< 3.0'
|
||||
spec.add_dependency 'method_source', '~> 0.8'
|
||||
|
|
|
@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
|
|||
spec.add_dependency 'train-aws', '~> 0.1'
|
||||
|
||||
# Implementation dependencies
|
||||
spec.add_dependency 'license-acceptance', '~> 0.2'
|
||||
spec.add_dependency 'thor', '~> 0.20'
|
||||
spec.add_dependency 'json', '>= 1.8', '< 3.0'
|
||||
spec.add_dependency 'method_source', '~> 0.8'
|
||||
|
|
|
@ -39,6 +39,9 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|||
class_option :disable_user_plugins, type: :string, banner: '',
|
||||
desc: 'Disable loading all plugins that the user installed.'
|
||||
|
||||
require 'license_acceptance/cli_flags/thor'
|
||||
include LicenseAcceptance::CLIFlags::Thor
|
||||
|
||||
desc 'json PATH', 'read all tests in PATH and generate a JSON summary'
|
||||
option :output, aliases: :o, type: :string,
|
||||
desc: 'Save the created profile to a path'
|
||||
|
@ -196,6 +199,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|||
3 Fatal deprecation encountered
|
||||
100 Normal exit, at least one test failed
|
||||
101 Normal exit, at least one test skipped but none failed
|
||||
172 Chef License not accepted
|
||||
```
|
||||
|
||||
Below are some examples of using `exec` with different test LOCATIONS:
|
||||
|
@ -373,18 +377,44 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|||
end
|
||||
end
|
||||
|
||||
#=====================================================================#
|
||||
# Pre-Flight Code
|
||||
#=====================================================================#
|
||||
|
||||
help_commands = ['-h', '--help', 'help']
|
||||
version_commands = ['-v', '--version', 'version']
|
||||
commands_exempt_from_license_check = help_commands + version_commands
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
# EULA acceptance
|
||||
#---------------------------------------------------------------------#
|
||||
require 'license_acceptance/acceptor'
|
||||
begin
|
||||
# Handle help commands
|
||||
# This allows you to use any of the normal help commands after the normal args.
|
||||
help_commands = ['-h', '--help', 'help']
|
||||
(help_commands & ARGV).each do |cmd|
|
||||
if (commands_exempt_from_license_check & ARGV.map(&:downcase)).empty? && # Did they use a non-exempt command?
|
||||
!ARGV.empty? # Did they supply at least one command?
|
||||
LicenseAcceptance::Acceptor.check_and_persist('inspec', Inspec::VERSION)
|
||||
end
|
||||
rescue LicenseAcceptance::LicenseNotAcceptedError
|
||||
Inspec::Log.error 'InSpec cannot execute without accepting the license'
|
||||
Inspec::UI.new.exit(:license_not_accepted)
|
||||
end
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
# Adjustments for help handling
|
||||
# This allows you to use any of the normal help commands after the normal args.
|
||||
#---------------------------------------------------------------------#
|
||||
(help_commands & ARGV).each do |cmd|
|
||||
# move the help argument to one place behind the end for Thor to digest
|
||||
if ARGV.size > 1
|
||||
match = ARGV.delete(cmd)
|
||||
ARGV.insert(-2, match)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
# Plugin Loading
|
||||
#---------------------------------------------------------------------#
|
||||
begin
|
||||
# Load v2 plugins. Manually check for plugin disablement.
|
||||
omit_core = ARGV.delete('--disable-core-plugins')
|
||||
omit_user = ARGV.delete('--disable-user-plugins')
|
||||
|
|
|
@ -33,6 +33,7 @@ module Inspec
|
|||
EXIT_USAGE_ERROR = 1
|
||||
EXIT_PLUGIN_ERROR = 2
|
||||
EXIT_FATAL_DEPRECATION = 3
|
||||
EXIT_LICENSE_NOT_ACCEPTED = 172
|
||||
EXIT_FAILED_TESTS = 100
|
||||
EXIT_SKIPPED_TESTS = 101
|
||||
|
||||
|
|
114
test/functional/license_test.rb
Normal file
114
test/functional/license_test.rb
Normal file
|
@ -0,0 +1,114 @@
|
|||
require 'functional/helper'
|
||||
require 'tmpdir'
|
||||
require 'yaml'
|
||||
|
||||
describe 'The license acceptance mechanism' do
|
||||
include FunctionalHelper
|
||||
|
||||
describe 'when the license has not been accepted' do
|
||||
describe 'when the user passes the --chef-license accept flag' do
|
||||
it 'should silently work normally' do
|
||||
Dir.mktmpdir do |tmp_home|
|
||||
run_result = run_inspec_process('shell -c platform.family --chef-license accept', env: { 'HOME' => tmp_home })
|
||||
run_result.stdout.wont_include 'Chef License Acceptance' # --chef-license should not mention accepting the license
|
||||
run_result.stderr.must_equal ''
|
||||
|
||||
run_result.exit_status.must_equal 0
|
||||
end
|
||||
end
|
||||
|
||||
it 'should write a YAML file' do
|
||||
Dir.mktmpdir do |tmp_home|
|
||||
license_persist_path = File.join(tmp_home, '.chef', 'accepted_licenses', 'inspec')
|
||||
|
||||
File.exist?(license_persist_path).must_equal false # Sanity check
|
||||
run_result = run_inspec_process('shell -c platform.family --chef-license accept', env: { 'HOME' => tmp_home })
|
||||
File.exist?(license_persist_path).must_equal true
|
||||
|
||||
license_persist_contents = YAML.load(File.read(license_persist_path))
|
||||
license_persist_contents.keys.must_include 'accepting_product'
|
||||
license_persist_contents['accepting_product'].must_equal 'inspec'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Since the license-acceptance library detects TTYs, and changes behavior
|
||||
# if not found, we can't test interactive acceptance anymore
|
||||
describe 'when no mechanism is used to accept the license and we are non-interactive' do
|
||||
it 'should exit ASAP with code 172' do
|
||||
Dir.mktmpdir do |tmp_home|
|
||||
run_result = run_inspec_process('shell -c platform.family', env: { 'HOME' => tmp_home })
|
||||
# [2019-04-11T11:06:00-04:00] ERROR: InSpec cannot execute without accepting the license
|
||||
run_result.stdout.must_include 'cannot execute'
|
||||
run_result.stdout.must_include 'the license'
|
||||
run_result.stdout.must_include 'ERROR' # From failure message
|
||||
run_result.exit_status.must_equal 172
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when a command is used that should not be gated on licensure' do
|
||||
[
|
||||
'-h', '--help', 'help', '', # Empty invocation is treated as `inspec help`
|
||||
'-v', '--version', 'version',
|
||||
].each do |ungated_invocation|
|
||||
it "should not challenge for a license when running `inspec #{ungated_invocation}`" do
|
||||
Dir.mktmpdir do |tmp_home|
|
||||
run_result = run_inspec_process(ungated_invocation, env: { 'HOME' => tmp_home })
|
||||
run_result.stdout.wont_include 'Chef License Acceptance'
|
||||
run_result.stderr.must_equal ''
|
||||
run_result.exit_status.must_equal 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the license has already been accepted' do
|
||||
describe 'when the license was accepted by touching a blank file' do
|
||||
it 'should silently work normally' do
|
||||
Dir.mktmpdir do |tmp_home|
|
||||
license_persist_dir = File.join(tmp_home, '.chef', 'accepted_licenses')
|
||||
license_persist_path = File.join(tmp_home, '.chef', 'accepted_licenses', 'inspec')
|
||||
|
||||
File.exist?(license_persist_path).must_equal false # Sanity check
|
||||
FileUtils.mkdir_p(license_persist_dir)
|
||||
FileUtils.touch(license_persist_path)
|
||||
File.exist?(license_persist_path).must_equal true # Sanity check
|
||||
|
||||
run_result = run_inspec_process('shell -c platform.family', env: { 'HOME' => tmp_home })
|
||||
run_result.stdout.wont_include 'Chef License Acceptance'
|
||||
run_result.stderr.must_equal ''
|
||||
run_result.exit_status.must_equal 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the license persistance file is a YAML file' do
|
||||
it 'should silently work normally' do
|
||||
Dir.mktmpdir do |tmp_home|
|
||||
license_persist_dir = File.join(tmp_home, '.chef', 'accepted_licenses')
|
||||
license_persist_path = File.join(tmp_home, '.chef', 'accepted_licenses', 'inspec')
|
||||
|
||||
File.exist?(license_persist_path).must_equal false # Sanity check
|
||||
FileUtils.mkdir_p(license_persist_dir)
|
||||
File.write(license_persist_path, <<~EOY)
|
||||
---
|
||||
name: inspec
|
||||
date_accepted: '1979-08-04T16:36:53-05:00'
|
||||
accepting_product: inspec
|
||||
accepting_product_version: 1.2.3
|
||||
user: someone
|
||||
file_format: 1
|
||||
EOY
|
||||
File.exist?(license_persist_path).must_equal true # Sanity check
|
||||
|
||||
run_result = run_inspec_process('shell -c platform.family', env: { 'HOME' => tmp_home })
|
||||
run_result.stdout.wont_include 'Chef License Acceptance'
|
||||
run_result.stderr.must_equal ''
|
||||
run_result.exit_status.must_equal 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue