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:
Jared Quick 2018-04-05 08:51:51 -04:00 committed by GitHub
parent d5aac39de1
commit b246cf7d21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 161 additions and 10 deletions

View file

@ -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

View file

@ -10,4 +10,5 @@ module Inspec
class UnsatisfiedVersionSpecification < Error; end
class DuplicateDep < Error; end
class FetcherFailure < Error; end
class ReporterError < Error; end
end

View file

@ -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

View file

@ -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

View 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

View file

@ -3,6 +3,7 @@ module Inspec::Reporters
attr_reader :run_data
def initialize(config)
@config = config
@run_data = config[:run_data]
@output = ''
end

View file

@ -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

View 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