mirror of
https://github.com/inspec/inspec
synced 2024-11-26 22:50:36 +00:00
Add automate reporter (#2902)
* Add automate reporter. * Add ssl flag for automate post. * Rename ssl flag. Signed-off-by: Jared Quick <jquick@chef.io>
This commit is contained in:
parent
d5aac39de1
commit
b246cf7d21
8 changed files with 161 additions and 10 deletions
|
@ -151,18 +151,24 @@ module Inspec
|
|||
return if reporters.nil?
|
||||
|
||||
valid_types = [
|
||||
'automate',
|
||||
'cli',
|
||||
'documentation',
|
||||
'html',
|
||||
'json',
|
||||
'json-min',
|
||||
'json-rspec',
|
||||
'cli',
|
||||
'junit',
|
||||
'html',
|
||||
'documentation',
|
||||
'progress',
|
||||
]
|
||||
|
||||
reporters.each do |k, _v|
|
||||
reporters.each do |k, v|
|
||||
raise NotImplementedError, "'#{k}' is not a valid reporter type." unless valid_types.include?(k)
|
||||
|
||||
next unless k == 'automate'
|
||||
%w{token url}.each do |option|
|
||||
raise Inspec::ReporterError, "You must specify a automate #{option} via the json-config." if v[option].nil?
|
||||
end
|
||||
end
|
||||
|
||||
# check to make sure we are only reporting one type to stdout
|
||||
|
@ -237,10 +243,10 @@ module Inspec
|
|||
|
||||
def merged_opts(type = nil)
|
||||
opts = {}
|
||||
opts[:type] = type unless type.nil?
|
||||
|
||||
# start with default options if we have any
|
||||
opts = BaseCLI.default_options[type] unless type.nil? || BaseCLI.default_options[type].nil?
|
||||
opts['type'] = type unless type.nil?
|
||||
|
||||
# merge in any options from json-config
|
||||
json_config = options_json
|
||||
|
|
|
@ -10,4 +10,5 @@ module Inspec
|
|||
class UnsatisfiedVersionSpecification < Error; end
|
||||
class DuplicateDep < Error; end
|
||||
class FetcherFailure < Error; end
|
||||
class ReporterError < Error; end
|
||||
end
|
||||
|
|
|
@ -69,6 +69,7 @@ module Inspec::Formatters
|
|||
name: platform(:name),
|
||||
release: platform(:release),
|
||||
target: backend_target,
|
||||
uuid: platform(:uuid),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -180,9 +181,17 @@ module Inspec::Formatters
|
|||
end
|
||||
end
|
||||
|
||||
# This formatter runs for all reports and we cannot error on missing fields.
|
||||
# Return nil if not found or Train error. If needed, we will raise an error inside
|
||||
# the proper report.
|
||||
def platform(field)
|
||||
return nil if @backend.nil?
|
||||
@backend.platform[field]
|
||||
begin
|
||||
@backend.platform[field]
|
||||
rescue Train::Error => e
|
||||
Inspec::Log.error(e.message)
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def backend_target
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'inspec/reporters/cli'
|
|||
require 'inspec/reporters/json'
|
||||
require 'inspec/reporters/json_min'
|
||||
require 'inspec/reporters/junit'
|
||||
require 'inspec/reporters/automate'
|
||||
|
||||
module Inspec::Reporters
|
||||
def self.render(reporter, run_data)
|
||||
|
@ -17,10 +18,15 @@ module Inspec::Reporters
|
|||
reporter = Inspec::Reporters::JsonMin.new(config)
|
||||
when 'junit'
|
||||
reporter = Inspec::Reporters::Junit.new(config)
|
||||
when 'automate'
|
||||
reporter = Inspec::Reporters::Automate.new(config)
|
||||
else
|
||||
raise NotImplementedError, "'#{name}' is not a valid reporter type."
|
||||
end
|
||||
|
||||
# optional send_report method on reporter
|
||||
return reporter.send_report if defined?(reporter.send_report)
|
||||
|
||||
reporter.render
|
||||
output = reporter.rendered_output
|
||||
|
||||
|
|
76
lib/inspec/reporters/automate.rb
Normal file
76
lib/inspec/reporters/automate.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'json'
|
||||
require 'net/http'
|
||||
|
||||
module Inspec::Reporters
|
||||
class Automate < Json
|
||||
def initialize(config)
|
||||
super(config)
|
||||
|
||||
# default to not verifying ssl for sending reports
|
||||
@config['verify_ssl'] = @config['verify_ssl'] || false
|
||||
end
|
||||
|
||||
def enriched_report
|
||||
# grab the report from the parent class
|
||||
final_report = report
|
||||
|
||||
# Label this content as an inspec_report
|
||||
final_report[:type] = 'inspec_report'
|
||||
|
||||
final_report[:end_time] = Time.now.utc.strftime('%FT%TZ')
|
||||
final_report[:node_uuid] = @config['node_uuid'] || @run_data[:platform][:uuid]
|
||||
raise Inspec::ReporterError, 'Cannot find a UUID for your node. Please specify one via json-config.' if final_report[:node_uuid].nil?
|
||||
|
||||
final_report[:report_uuid] = uuid_from_string(final_report[:end_time] + final_report[:node_uuid])
|
||||
|
||||
# optional json-config passthrough options
|
||||
%w{node_name environment roles recipies}.each do |option|
|
||||
final_report[option.to_sym] = @config[option] unless @config[option].nil?
|
||||
end
|
||||
final_report
|
||||
end
|
||||
|
||||
def send_report
|
||||
headers = { 'Content-Type' => 'application/json' }
|
||||
headers['x-data-collector-token'] = @config['token']
|
||||
headers['x-data-collector-auth'] = 'version=1.0'
|
||||
|
||||
uri = URI(@config['url'])
|
||||
req = Net::HTTP::Post.new(uri.path, headers)
|
||||
req.body = enriched_report.to_json
|
||||
begin
|
||||
Inspec::Log.debug "Posting report to Chef Automate: #{uri.path}"
|
||||
http = Net::HTTP.new(uri.hostname, uri.port)
|
||||
http.use_ssl = uri.scheme == 'https'
|
||||
if @config['verify_ssl'] == true
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||
else
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
end
|
||||
|
||||
http.request(req)
|
||||
return true
|
||||
rescue => e
|
||||
Inspec::Log.error "send_report: POST to #{uri.path} returned: #{e.message}"
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This hashes the passed string into SHA1.
|
||||
# Then it downgrades the 160bit SHA1 to a 128bit
|
||||
# then we format it as a valid UUIDv5.
|
||||
def uuid_from_string(string)
|
||||
hash = Digest::SHA1.new
|
||||
hash.update(string)
|
||||
ary = hash.digest.unpack('NnnnnN')
|
||||
ary[2] = (ary[2] & 0x0FFF) | (5 << 12)
|
||||
ary[3] = (ary[3] & 0x3FFF) | 0x8000
|
||||
# rubocop:disable Style/FormatString
|
||||
'%08x-%04x-%04x-%04x-%04x%08x' % ary
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@ module Inspec::Reporters
|
|||
attr_reader :run_data
|
||||
|
||||
def initialize(config)
|
||||
@config = config
|
||||
@run_data = config[:run_data]
|
||||
@output = ''
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ describe 'BaseCLI' do
|
|||
Inspec::BaseCLI.stubs(:default_options).returns(default_options)
|
||||
|
||||
opts = cli.send(:merged_opts, :exec)
|
||||
expected = {"backend_cache"=>false, "reporter"=>{"json"=>{"stdout"=>true}}}
|
||||
expected = {"backend_cache"=>false, "reporter"=>{"json"=>{"stdout"=>true}}, "type"=>:exec}
|
||||
opts.must_equal expected
|
||||
end
|
||||
|
||||
|
@ -37,7 +37,7 @@ EOF
|
|||
cli.expects(:options_json).returns(parsed_json)
|
||||
|
||||
opts = cli.send(:merged_opts, :exec)
|
||||
expected = {"backend_cache"=>true, "reporter"=>{"json"=>{"stdout"=>true}}}
|
||||
expected = {"backend_cache"=>true, "reporter"=>{"json"=>{"stdout"=>true}}, "type"=>:exec}
|
||||
opts.must_equal expected
|
||||
end
|
||||
|
||||
|
@ -51,7 +51,7 @@ EOF
|
|||
cli.instance_variable_set(:@options, cli_options)
|
||||
|
||||
opts = cli.send(:merged_opts, :exec)
|
||||
expected = {"backend_cache"=>true, "reporter"=>{"json"=>{"stdout"=>true}}}
|
||||
expected = {"backend_cache"=>true, "reporter"=>{"json"=>{"stdout"=>true}}, "type"=>:exec}
|
||||
opts.must_equal expected
|
||||
end
|
||||
|
||||
|
@ -70,7 +70,7 @@ EOF
|
|||
cli.expects(:options_json).returns(parsed_json)
|
||||
|
||||
opts = cli.send(:merged_opts, :exec)
|
||||
expected = {"backend_cache"=>false, "reporter"=>{"json"=>{"stdout"=>true}}}
|
||||
expected = {"backend_cache"=>false, "reporter"=>{"json"=>{"stdout"=>true}}, "type"=>:exec}
|
||||
opts.must_equal expected
|
||||
end
|
||||
end
|
||||
|
|
52
test/unit/reporters/automate_test.rb
Normal file
52
test/unit/reporters/automate_test.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'helper'
|
||||
|
||||
describe Inspec::Reporters::Automate do
|
||||
let(:path) { File.expand_path(File.dirname(__FILE__)) }
|
||||
let(:options) do
|
||||
{
|
||||
'url' => "https://my-automate-server.mycompany.com/data-collector/v0/",
|
||||
'token' => "kwe09wef9uqwqmpoqwdqd=",
|
||||
'node_uuid' => "22ad2f99-f84f-5456-95a0-7e91b4b66690",
|
||||
'node_name' => "test_node",
|
||||
'environment' => "prod",
|
||||
}
|
||||
end
|
||||
let(:report) do
|
||||
data = JSON.parse(File.read(path + '/../mock/reporters/run_data.json'), symbolize_names: true)
|
||||
options.merge!({ run_data: data })
|
||||
Inspec::Reporters::Automate.new(options)
|
||||
end
|
||||
|
||||
describe '#enriched_report' do
|
||||
it 'returns a enriched report' do
|
||||
report.enriched_report[:node_uuid].must_equal "22ad2f99-f84f-5456-95a0-7e91b4b66690"
|
||||
report.enriched_report[:node_name].must_equal "test_node"
|
||||
report.enriched_report[:environment].must_equal "prod"
|
||||
end
|
||||
end
|
||||
|
||||
describe '#send_report' do
|
||||
it 'returns true for sent report' do
|
||||
headers = {
|
||||
'Content-Type' => 'application/json',
|
||||
'x-data-collector-token' => 'kwe09wef9uqwqmpoqwdqd=',
|
||||
'x-data-collector-auth' => 'version=1.0',
|
||||
}
|
||||
stub = Net::HTTP::Post.new("/data-collector/v0/", headers)
|
||||
Net::HTTP::Post.expects(:new).with("/data-collector/v0/", headers).returns(stub)
|
||||
Net::HTTP.any_instance.stubs(:request).returns(true)
|
||||
report.send_report.must_equal true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#uuid_from_string' do
|
||||
it 'converts a string to a uuid' do
|
||||
end_time = "2018-03-28T14:10:50Z"
|
||||
node_uuid = "22ad2f99-f84f-5456-95a0-7e91b4b66690"
|
||||
assert = "4cd5aaa3-eea0-5aa2-9837-631e10b873b1"
|
||||
report.send(:uuid_from_string, end_time + node_uuid).must_equal assert
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue