inspec/Rakefile

420 lines
15 KiB
Ruby
Raw Normal View History

2015-09-03 17:25:43 +00:00
#!/usr/bin/env rake
# encoding: utf-8
require 'bundler'
require 'bundler/gem_helper'
require 'rake/testtask'
require 'passgen'
require 'train'
2016-09-22 19:26:19 +00:00
require_relative 'tasks/maintainers'
require_relative 'tasks/spdx'
require 'fileutils'
2015-09-03 17:25:43 +00:00
Bundler::GemHelper.install_tasks name: 'inspec'
def prompt(message)
print(message)
STDIN.gets.chomp
end
# The docs tasks rely on ruby-progressbar. If we can't load it, then don't
# load the docs tasks. This is necessary to allow this Rakefile to work
# when the "tests" gem group in the Gemfile has been excluded, such as
# during an appbundle-updater run.
begin
require 'ruby-progressbar'
require_relative 'tasks/docs'
rescue LoadError
puts 'docs tasks are unavailable because the ruby-progressbar gem is not available.'
2015-09-03 17:25:43 +00:00
end
begin
require 'git'
require_relative 'tasks/contrib'
rescue LoadError
puts 'contrib tasks are unavailable because the git gem is not available.'
end
# Rubocop
begin
require 'rubocop/rake_task'
RuboCop::RakeTask.new(:lint)
rescue LoadError
puts 'rubocop is not available. Install the rubocop gem to run the lint tests.'
end
# update command output for demo
2016-08-25 15:24:22 +00:00
desc 'Run inspec commands and save results to www/app/responses'
task :update_demo do
ruby 'www/tutorial/scripts/build_simulator_runtime.rb'
ruby 'www/tutorial/scripts/run_simulator_recording.rb'
end
2015-09-03 17:25:43 +00:00
# run tests
task default: [:lint, :test]
Rake::TestTask.new do |t|
t.libs << 'test'
t.test_files = Dir.glob([
'test/unit/**/*_test.rb',
'lib/plugins/inspec-*/test/unit/**/*_test.rb',
])
t.warning = false
t.verbose = true
2015-09-03 18:35:23 +00:00
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
end
namespace :test do
task :isolated do
Dir.glob('test/unit/*_test.rb').all? do |file|
sh(Gem.ruby, '-w', '-Ilib:test', file)
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
2016-03-16 07:22:46 +00:00
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 # This just complains about things in underlying libraries
2016-03-16 07:22:46 +00:00
t.verbose = true
t.ruby_opts = ['--dev'] if defined?(JRUBY_VERSION)
end
# Inject a prerequisite task
task :functional => [:accept_license]
2016-03-16 07:22:46 +00:00
# Functional tests on Windows take a bit to run. This
# optionally takes a env to breake the tests up into 3 workers.
Rake::TestTask.new(:'functional:windows') do |t, args|
files = Dir.glob('test/functional/*_test.rb').sort
if ENV['WORKER_NUMBER']
count = (files.count / 3).abs+1
start = (ENV['WORKER_NUMBER'].to_i - 1) * count
files = files[start..start+count-1]
end
t.libs << 'test'
t.test_files = files
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']
return if tests.empty?
sh(Gem.ruby, 'test/docker_test.rb', *tests)
end
2015-10-21 20:52:41 +00:00
task :integration, [:os] do |task, args|
2016-05-10 17:23:11 +00:00
concurrency = ENV['CONCURRENCY'] || 1
os = args[:os] || ENV['OS'] || ''
ENV['DOCKER'] = 'true' if ENV['docker'].nil?
puts "Building current InSpec gem for audit cookbook testing..."
output = %x[gem build inspec-core.gemspec]
puts output
gem_name = output.split("\n")[-1].split(':')[1].strip
path = File.dirname(__FILE__)
File.rename(File.join(path, gem_name), File.join(path, 'inspec-core-local.gem'))
destination = File.join(path, 'test', 'cookbooks', 'os_prepare', 'files', 'inspec-core-local.gem')
begin
FileUtils.cp(File.join(path, 'inspec-core-local.gem'), destination)
sh("bundle exec kitchen test -c #{concurrency} #{os}")
ensure
FileUtils.rm(destination)
end
2015-10-21 20:52:41 +00:00
end
# Inject a prerequisite task
task :'integration' => [:accept_license]
2015-12-18 21:54:56 +00:00
task :ssh, [:target] do |_t, args|
tests_path = File.join(File.dirname(__FILE__), 'test', 'integration', 'test', 'integration', 'default')
key_files = ENV['key_files'] || File.join(ENV['HOME'], '.ssh', 'id_rsa')
sh_cmd = "bin/inspec exec #{tests_path}/"
sh_cmd += ENV['test'] ? "#{ENV['test']}_spec.rb" : '*'
2015-12-19 00:43:23 +00:00
sh_cmd += " --sudo" unless args[:target].split('@')[0] == 'root'
2015-12-18 21:54:56 +00:00
sh_cmd += " -t ssh://#{args[:target]}"
sh_cmd += " --key_files=#{key_files}"
sh_cmd += " --format=#{ENV['format']}" if ENV['format']
sh('sh', '-c', sh_cmd)
end
project_dir = File.dirname(__FILE__)
namespace :aws do
['default', 'minimal'].each do |account|
integration_dir = File.join(project_dir, 'test', 'integration', 'aws', account)
attribute_file = File.join(integration_dir, '.attribute.yml')
task :"setup:#{account}", :tf_workspace do |t, args|
tf_workspace = args[:tf_workspace] || ENV['INSPEC_TERRAFORM_ENV']
abort("You must either call the top-level test:aws:#{account} task, or set the INSPEC_TERRAFORM_ENV variable.") unless tf_workspace
puts "----> Setup"
abort("You must set the environment variable AWS_REGION") unless ENV['AWS_REGION']
puts "----> Checking for required AWS profile..."
sh("aws configure get aws_access_key_id --profile inspec-aws-test-#{account} > /dev/null")
sh("cd #{integration_dir}/build/ && terraform init -upgrade")
sh("cd #{integration_dir}/build/ && terraform workspace new #{tf_workspace}")
sh("cd #{integration_dir}/build/ && AWS_PROFILE=inspec-aws-test-#{account} terraform plan -out inspec-aws-#{account}.plan")
sh("cd #{integration_dir}/build/ && AWS_PROFILE=inspec-aws-test-#{account} terraform apply -auto-approve inspec-aws-#{account}.plan")
Rake::Task["test:aws:dump_attrs:#{account}"].execute
end
task :"dump_attrs:#{account}" do
sh("cd #{integration_dir}/build/ && AWS_PROFILE=inspec-aws-test-#{account} terraform output > #{attribute_file}")
raw_output = File.read(attribute_file)
yaml_output = raw_output.gsub(" = ", " : ")
File.open(attribute_file, "w") {|file| file.puts yaml_output}
end
task :"run:#{account}" do
puts "----> Run"
sh("bundle exec inspec exec #{integration_dir}/verify -t aws://${AWS_REGION}/inspec-aws-test-#{account} --attrs #{attribute_file}")
end
task :"cleanup:#{account}", :tf_workspace do |t, args|
tf_workspace = args[:tf_workspace] || ENV['INSPEC_TERRAFORM_ENV']
abort("You must either call the top-level test:aws:#{account} task, or set the INSPEC_TERRAFORM_ENV variable.") unless tf_workspace
puts "----> Cleanup"
sh("cd #{integration_dir}/build/ && AWS_PROFILE=inspec-aws-test-#{account} terraform destroy -force")
sh("cd #{integration_dir}/build/ && terraform workspace select default")
sh("cd #{integration_dir}/build && terraform workspace delete #{tf_workspace}")
end
task :"#{account}" do
tf_workspace = ENV['INSPEC_TERRAFORM_ENV'] || prompt("Please enter a workspace for your integration tests to run in: ")
begin
Rake::Task["test:aws:setup:#{account}"].execute({:tf_workspace => tf_workspace})
Rake::Task["test:aws:run:#{account}"].execute
rescue
abort("Integration testing has failed for the #{account} account")
ensure
Rake::Task["test:aws:cleanup:#{account}"].execute({:tf_workspace => tf_workspace})
end
end
end
end
desc "Perform AWS Integration Tests"
task aws: [:'aws:default', :'aws:minimal']
namespace :azure do
# Specify the directory for the integration tests
integration_dir = File.join(project_dir, 'test', 'integration', 'azure')
tf_vars_file = File.join(integration_dir, 'build', 'terraform.tfvars')
attribute_file = File.join(integration_dir, '.attribute.yml')
task :setup, :tf_workspace do |t, args|
tf_workspace = args[:tf_workspace] || ENV['INSPEC_TERRAFORM_ENV']
abort("You must either call the top-level test:azure task, or set the INSPEC_TERRAFORM_ENV variable.") unless tf_workspace
puts '----> Setup Terraform Workspace'
sh("cd #{integration_dir}/build/ && terraform init -upgrade")
sh("cd #{integration_dir}/build/ && terraform workspace new #{tf_workspace}")
Rake::Task["test:azure:vars"].execute
Rake::Task["test:azure:plan"].execute
Rake::Task["test:azure:apply"].execute
end
desc "Generate terraform.tfvars file"
task :vars do |t, args|
next if File.exist?(tf_vars_file)
puts '----> Generating Vars'
# Generate Azure crendentials
connection = Train.create('azure').connection
creds = connection.options
# Determine the storage account name and the admin password
sa_name = (0...15).map { (65 + rand(26)).chr }.join.downcase
admin_password = Passgen::generate(length: 12, uppercase: true, lowercase: true, symbols: true, digits: true)
# Use the first 4 characters of the storage account to create a suffix
suffix = sa_name[0..3]
content = <<~VARS
subscription_id = "#{creds[:subscription_id]}"
client_id = "#{creds[:client_id]}"
client_secret = "#{creds[:client_secret]}"
tenant_id = "#{creds[:tenant_id]}"
storage_account_name = "#{sa_name}"
admin_password = "#{admin_password}"
suffix = "#{suffix}"
VARS
content << "location = \"#{ENV['AZURE_LOCATION']}\"\n" if ENV['AZURE_LOCATION']
File.write(tf_vars_file, content)
end
desc "generate plan from state using terraform.tfvars file"
task :plan, [:tf_workspace] => [:vars] do |t, args|
tf_workspace = args[:tf_workspace] || ENV['INSPEC_TERRAFORM_ENV']
abort("You must set the INSPEC_TERRAFORM_ENV variable.") unless tf_workspace
puts '----> Generating Plan'
result = sh("cd #{integration_dir}/build/ && terraform workspace select #{tf_workspace}")
sh("cd #{integration_dir}/build/ && terraform plan -out inspec-azure.plan")
end
desc "apply terraform plan"
task :apply, [:tf_workspace] => [:plan] do |t, args|
tf_workspace = args[:tf_workspace] || ENV['INSPEC_TERRAFORM_ENV']
abort("You must set the INSPEC_TERRAFORM_ENV variable.") unless tf_workspace
puts '----> Applying Plan'
sh("cd #{integration_dir}/build/ && terraform workspace select #{tf_workspace}")
sh("cd #{integration_dir}/build/ && terraform apply inspec-azure.plan")
Rake::Task["test:azure:dump_attrs"].execute
end
task :"dump_attrs" do
sh("cd #{integration_dir}/build/ && terraform output > #{attribute_file}")
raw_output = File.read(attribute_file)
yaml_output = raw_output.gsub(" = ", " : ")
File.open(attribute_file, "w") {|file| file.puts yaml_output}
end
task :run do
puts '----> Run'
sh("bundle exec inspec exec #{integration_dir}/verify -t azure://1e0b427a-d58b-494e-ae4f-ee558463ebbf")
end
task :cleanup, :tf_workspace do |t, args|
tf_workspace = args[:tf_workspace] || ENV['INSPEC_TERRAFORM_ENV']
abort("You must either call the top-level test:azure task, or set the INSPEC_TERRAFORM_ENV variable.") unless tf_workspace
puts '----> Cleanup'
sh("cd #{integration_dir}/build/ && terraform destroy -force ")
sh("cd #{integration_dir}/build/ && terraform workspace select default")
sh("cd #{integration_dir}/build && terraform workspace delete #{tf_workspace}")
File.delete(tf_vars_file)
end
end
desc "Perform Azure Integration Tests"
task :azure do
tf_workspace = ENV['INSPEC_TERRAFORM_ENV'] || prompt("Please enter a workspace for your integration tests to run in: ")
begin
Rake::Task["test:azure:setup"].execute({:tf_workspace => tf_workspace})
Rake::Task["test:azure:run"].execute
rescue
abort("Integration testing has failed")
ensure
Rake::Task["test:azure:cleanup"].execute({:tf_workspace => tf_workspace})
end
end
end
2015-11-20 21:38:12 +00:00
# Print the current version of this gem or update it.
#
# @param [Type] target the new version you want to set, or nil if you only want to show
def inspec_version(target = nil)
path = 'lib/inspec/version.rb'
require_relative path.sub(/.rb$/, '')
nu_version = target.nil? ? '' : " -> #{target}"
puts "Inspec: #{Inspec::VERSION}#{nu_version}"
unless target.nil?
raw = File.read(path)
2016-01-15 20:41:20 +00:00
nu = raw.sub(/VERSION.*/, "VERSION = '#{target}'.freeze")
File.write(path, nu)
load(path)
end
end
# Check if a command is available
#
# @param [Type] x the command you are interested in
# @param [Type] msg the message to display if the command is missing
def require_command(x, msg = nil)
return if system("command -v #{x} || exit 1")
msg ||= 'Please install it first!'
puts "\033[31;1mCan't find command #{x.inspect}. #{msg}\033[0m"
exit 1
end
# Check if a required environment variable has been set
#
# @param [String] x the variable you are interested in
# @param [String] msg the message you want to display if the variable is missing
def require_env(x, msg = nil)
exists = `env | grep "^#{x}="`
return unless exists.empty?
puts "\033[31;1mCan't find environment variable #{x.inspect}. #{msg}\033[0m"
exit 1
end
# Check the requirements for running an update of this repository.
def check_update_requirements
require_command 'git'
end
# Show the current version of this gem.
desc 'Show the version of this gem'
task :version do
inspec_version
end
desc 'Release a new docker image'
task :release_docker do
version = Inspec::VERSION
cmd = "rm *.gem; gem build *gemspec && "\
"mv *.gem inspec.gem && "\
"docker build -t chef/inspec:#{version} . && "\
"docker push chef/inspec:#{version} && "\
"docker tag chef/inspec:#{version} chef/inspec:latest &&"\
"docker push chef/inspec:latest"
puts "--> #{cmd}"
sh('sh', '-c', cmd)
end
desc 'Release the website [deprecated]'
task :www do
puts 'The Rake tasks for releasing the website are now in the www/ directory.'
puts 'Run `cd www` and then `rake --tasks` for a list of the www-related tasks available.'
exit(1)
end
namespace :www do
desc 'Release the website [deprecated]'
task :release do
puts 'The Rake tasks for releasing the website are now in the www/ directory.'
puts 'Run `cd www` and then `rake --tasks` for a list of the www-related tasks available.'
exit(1)
end
end