diff options
-rw-r--r-- | dev-repo/README.md | 11 | ||||
-rw-r--r-- | dev-repo/dev-config.rb | 4 | ||||
-rw-r--r-- | lib/chef/audit/audit_reporter.rb | 71 | ||||
-rw-r--r-- | lib/chef/audit/rspec_formatter.rb | 19 | ||||
-rw-r--r-- | lib/chef/audit/runner.rb | 3 | ||||
-rw-r--r-- | lib/chef/config.rb | 1 | ||||
-rw-r--r-- | lib/chef/monologger.rb | 2 |
7 files changed, 76 insertions, 35 deletions
diff --git a/dev-repo/README.md b/dev-repo/README.md index 84fe5f77df..fc29aa0d3f 100644 --- a/dev-repo/README.md +++ b/dev-repo/README.md @@ -1,6 +1,15 @@ # Chef Developer Repo -This repository contains some basic cookbooks to test chef while you're hacking away. You can provision a VM using the kitchen configuration and run these tests like below: +This repository contains some basic cookbooks to test chef while you're hacking away. + +You can run these recipes on your box with: + +``` +$ bundle install +$ bundle exec chef-client -z -o "recipe[audit_test::default]" -c dev-repo/dev-config.rb +``` + +Or you can provision a VM using the kitchen configuration and run these tests: ``` $ kitchen converge chef-ubuntu-1210 diff --git a/dev-repo/dev-config.rb b/dev-repo/dev-config.rb index 4ac411c832..11360e9d93 100644 --- a/dev-repo/dev-config.rb +++ b/dev-repo/dev-config.rb @@ -1,2 +1,2 @@ -cookbook_path "/home/vagrant/chef/dev-repo/cookbooks" -cache_path "/home/vagrant/.cache/chef" +cookbook_path File.join(File.expand_path(File.dirname(__FILE__)), "cookbooks") +cache_path "#{ENV['HOME']}/.cache/chef" diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb index b1c9d30bfc..8552165578 100644 --- a/lib/chef/audit/audit_reporter.rb +++ b/lib/chef/audit/audit_reporter.rb @@ -30,11 +30,7 @@ class Chef 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 @@ -46,7 +42,7 @@ class Chef 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 @@ -57,7 +53,7 @@ class Chef # 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 @@ -88,34 +84,51 @@ class Chef 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 + 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 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_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 + # TODO: We might want to change this to :debug + Chef::Log.info "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}" + compressed_data = encode_gzip(Chef::JSONCompat.to_json(run_data)) + # Since we're posting compressed data we can not directly call post_rest which expects JSON + audit_url = rest_client.create_url(audit_history_url) + begin + rest_client.raw_http_request(:POST, audit_url, headers({'Content-Encoding' => 'gzip'}), compressed_data) + 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 diff --git a/lib/chef/audit/rspec_formatter.rb b/lib/chef/audit/rspec_formatter.rb new file mode 100644 index 0000000000..990c1cd780 --- /dev/null +++ b/lib/chef/audit/rspec_formatter.rb @@ -0,0 +1,19 @@ +require 'rspec/core' + +class Chef + class Audit + class RspecFormatter < RSpec::Core::Formatters::DocumentationFormatter + RSpec::Core::Formatters.register self, :close + + # @api public + # + # Invoked at the very end, `close` allows the formatter to clean + # up resources, e.g. open streams, etc. + # + # @param _notification [NullNotification] (Ignored) + def close(_notification) + # Normally Rspec closes the streams it's given. We don't want it for Chef. + end + end + end +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 diff --git a/lib/chef/config.rb b/lib/chef/config.rb index d3871c38e8..e7edbea59e 100644 --- a/lib/chef/config.rb +++ b/lib/chef/config.rb @@ -319,6 +319,7 @@ class Chef default :client_fork, true default :enable_reporting, true default :enable_reporting_url_fatals, false + default :audit_mode, true # Policyfile is an experimental feature where a node gets its run list and # cookbook version set from a single document on the server instead of diff --git a/lib/chef/monologger.rb b/lib/chef/monologger.rb index 464b21bdd3..f7d226f82e 100644 --- a/lib/chef/monologger.rb +++ b/lib/chef/monologger.rb @@ -1,5 +1,4 @@ require 'logger' - require 'pp' #== MonoLogger @@ -89,4 +88,3 @@ class MonoLogger < Logger end - |