summaryrefslogtreecommitdiff
path: root/lib/chef/audit
diff options
context:
space:
mode:
authortyler-ball <tyleraball@gmail.com>2014-10-31 15:05:10 -0700
committertyler-ball <tyleraball@gmail.com>2014-12-17 18:51:33 -0800
commit772232776ed10465708d1ddab7c7238a199f6199 (patch)
treefa1eca74bf4e831d6a92498c425b18e9d1ee9020 /lib/chef/audit
parent8efee3e8ed41dc7cd4ae0b4a1664467dd403346d (diff)
downloadchef-772232776ed10465708d1ddab7c7238a199f6199.tar.gz
Adding audit mode JSON formatter
First pass at DSL additions Renaming DSL methods to match the spec Creating our own example group class to simplify adding examples to the spec runner Adding logic for exceptions from converge phase not interfering with audit phase and vice-versa Adding error handling so saving node doesn't prevent us from running audit mode - decouples converge phase and audit phase more Updating for github comments Add setup phase to audit-mode. Refactor runner into own class. Fix typo tie things together Adding first round of formatter integration - STDOUT doc formatter Preparing for demo - using rspec documentation formatter for output instead of the proxy Add serverspec types and matchers. add rspec/its Add gems as core dependencies Updating with changes from demo Updating with @mcquin and @lamont comments Getting rid of unused method Wiring audit event proxy to send events correctly to the audit_reporter removing old pry debugging statement Removing unecessary todo Sending to correct server URL Fixing TODOs Adding uncaught error information
Diffstat (limited to 'lib/chef/audit')
-rw-r--r--lib/chef/audit/audit_reporter.rb98
-rw-r--r--lib/chef/audit/control_group_data.rb3
-rw-r--r--lib/chef/audit/runner.rb3
3 files changed, 70 insertions, 34 deletions
diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb
index b1c9d30bfc..5ed1f7bd52 100644
--- a/lib/chef/audit/audit_reporter.rb
+++ b/lib/chef/audit/audit_reporter.rb
@@ -19,22 +19,19 @@
require 'chef/event_dispatch/base'
require 'chef/audit/control_group_data'
+require 'time'
class Chef
class Audit
class AuditReporter < EventDispatch::Base
- attr_reader :rest_client, :audit_data, :ordered_control_groups
- private :rest_client, :audit_data, :ordered_control_groups
+ attr_reader :rest_client, :audit_data, :ordered_control_groups, :run_status
+ private :rest_client, :audit_data, :ordered_control_groups, :run_status
PROTOCOL_VERSION = '0.1.0'
def initialize(rest_client)
- if Chef::Config[:audit_mode] == false
- @audit_enabled = false
- else
- @audit_enabled = true
- end
+ @audit_enabled = Chef::Config[:audit_mode]
@rest_client = rest_client
# Ruby 1.9.3 and above "enumerate their values in the order that the corresponding keys were inserted."
@ordered_control_groups = Hash.new
@@ -43,25 +40,32 @@ class Chef
def audit_phase_start(run_status)
Chef::Log.debug("Audit Reporter starting")
@audit_data = AuditData.new(run_status.node.name, run_status.run_id)
+ @run_status = run_status
end
def audit_phase_complete
- Chef::Log.debug("Audit Reporter completed successfully without errors")
+ Chef::Log.debug("Audit Reporter completed successfully without errors.")
ordered_control_groups.each do |name, control_group|
audit_data.add_control_group(control_group)
end
- post_auditing_data
end
# If the audit phase failed, its because there was some kind of error in the framework
# that runs tests - normal errors are interpreted as EXAMPLE failures and captured.
def audit_phase_failed(error)
# The stacktrace information has already been logged elsewhere
- Chef::Log.error("Audit Reporter failed - sending error to server with available example information")
+ Chef::Log.debug("Audit Reporter failed.")
ordered_control_groups.each do |name, control_group|
audit_data.add_control_group(control_group)
end
- post_auditing_data(error)
+ end
+
+ def run_completed(node)
+ post_auditing_data
+ end
+
+ def run_failed(error)
+ post_reporting_data(error)
end
def control_group_started(name)
@@ -81,41 +85,65 @@ class Chef
control_group.example_failure(example_data, error.message)
end
+ # If @audit_enabled is nil or true, we want to run audits
def auditing_enabled?
- @audit_enabled
+ @audit_enabled != false
end
private
def post_auditing_data(error = nil)
- if auditing_enabled?
- audit_history_url = "controls"
- Chef::Log.info("Sending audit report (run-id: #{audit_data.run_id})")
- run_data = audit_data.to_hash
+ unless auditing_enabled?
+ Chef::Log.debug("Audit Reports are disabled. Skipping sending reports.")
+ return
+ end
- if error
- run_data[:error] = "#{error.class.to_s}: #{error.message}\n#{error.backtrace.join("\n")}"
- end
+ unless run_status
+ Chef::Log.debug("Run failed before audits were initialized, not sending audit report to server")
+ return
+ end
- Chef::Log.debug run_data.inspect
- compressed_data = encode_gzip(Chef::JSONCompat.to_json(run_data))
- Chef::Log.debug("Sending compressed audit data...")
- # Since we're posting compressed data we can not directly call post_rest which expects JSON
+ audit_data.start_time = iso8601ify(run_status.start_time)
+ audit_data.end_time = iso8601ify(run_status.end_time)
+
+ audit_history_url = "controls"
+ Chef::Log.info("Sending audit report (run-id: #{audit_data.run_id})")
+ run_data = audit_data.to_hash
+
+ if error
+ # TODO: Rather than a single string we might want to format the exception here similar to
+ # lib/chef/resource_reporter.rb#83
+ run_data[:error] = "#{error.class.to_s}: #{error.message}\n#{error.backtrace.join("\n")}"
+ end
+
+ Chef::Log.debug "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}"
+ # Since we're posting compressed data we can not directly call post_rest which expects JSON
+ begin
audit_url = rest_client.create_url(audit_history_url)
- begin
- puts Chef::JSONCompat.to_json_pretty(run_data)
- rest_client.raw_http_request(:POST, audit_url, headers({'Content-Encoding' => 'gzip'}), compressed_data)
- rescue StandardError => e
- if e.respond_to? :response
+ rest_client.post(audit_url, run_data, headers)
+ rescue StandardError => e
+ if e.respond_to? :response
+ code = e.response.code.nil? ? "Exception Code Empty" : e.response.code
+
+ # 404 error code is OK. This means the version of server we're running against doesn't support
+ # audit reporting. Don't alarm failure in this case.
+ if code == "404"
+ Chef::Log.debug("Server doesn't support audit reporting. Skipping report.")
+ return
+ else
+ # Save the audit report to local disk
error_file = "failed-audit-data.json"
Chef::FileCache.store(error_file, Chef::JSONCompat.to_json_pretty(run_data), 0640)
- Chef::Log.error("Failed to post audit report to server (HTTP #{e.response.code}), saving to #{Chef::FileCache.load(error_file, false)}")
- else
- Chef::Log.error("Failed to post audit report to server (#{e})")
+ Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}")
end
+ else
+ Chef::Log.error("Failed to post audit report to server (#{e})")
+ end
+
+ if Chef::Config[:enable_reporting_url_fatals]
+ Chef::Log.error("Reporting fatals enabled. Aborting run.")
+ raise
end
- else
- Chef::Log.debug("Server doesn't support audit report, skipping.")
end
end
@@ -130,6 +158,10 @@ class Chef
end
end
+ def iso8601ify(time)
+ time.utc.iso8601.to_s
+ end
+
end
end
end
diff --git a/lib/chef/audit/control_group_data.rb b/lib/chef/audit/control_group_data.rb
index e19a6e1a15..e221ae94cc 100644
--- a/lib/chef/audit/control_group_data.rb
+++ b/lib/chef/audit/control_group_data.rb
@@ -4,6 +4,7 @@ class Chef
class Audit
class AuditData
attr_reader :node_name, :run_id, :control_groups
+ attr_accessor :start_time, :end_time
def initialize(node_name, run_id)
@node_name = node_name
@@ -19,6 +20,8 @@ class Chef
{
:node_name => node_name,
:run_id => run_id,
+ :start_time => start_time,
+ :end_time => end_time,
:control_groups => control_groups.collect { |c| c.to_hash }
}
end
diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb
index 4059741359..0758dacd6d 100644
--- a/lib/chef/audit/runner.rb
+++ b/lib/chef/audit/runner.rb
@@ -18,6 +18,7 @@
require 'chef/audit'
require 'chef/audit/audit_event_proxy'
+require 'chef/audit/rspec_formatter'
require 'chef/config'
class Chef
@@ -79,7 +80,7 @@ class Chef
end
def add_formatters
- configuration.add_formatter(RSpec::Core::Formatters::DocumentationFormatter)
+ configuration.add_formatter(Chef::Audit::RspecFormatter)
configuration.add_formatter(Chef::Audit::AuditEventProxy)
Chef::Audit::AuditEventProxy.events = run_context.events
end