diff options
author | Claire McQuin <claire@getchef.com> | 2014-11-14 12:36:54 -0800 |
---|---|---|
committer | tyler-ball <tyleraball@gmail.com> | 2014-12-17 18:52:02 -0800 |
commit | 7027bb8a8782f934a3239ee45e98fb7fc18d99bc (patch) | |
tree | e380b968dba3bccba98e947ddfa6cf121c926a2e | |
parent | 772232776ed10465708d1ddab7c7238a199f6199 (diff) | |
download | chef-7027bb8a8782f934a3239ee45e98fb7fc18d99bc.tar.gz |
Add a Chef::Audit::Controls object for 'controls'
-rw-r--r-- | lib/chef/audit.rb | 10 | ||||
-rw-r--r-- | lib/chef/audit/controls.rb | 120 | ||||
-rw-r--r-- | lib/chef/audit/runner.rb | 80 | ||||
-rw-r--r-- | lib/chef/client.rb | 16 | ||||
-rw-r--r-- | lib/chef/dsl/audit.rb | 5 | ||||
-rw-r--r-- | spec/unit/client_spec.rb | 9 |
6 files changed, 146 insertions, 94 deletions
diff --git a/lib/chef/audit.rb b/lib/chef/audit.rb index ed8db93d96..1c28e7ea75 100644 --- a/lib/chef/audit.rb +++ b/lib/chef/audit.rb @@ -16,14 +16,6 @@ # limitations under the License. # -require 'rspec' -require 'rspec/its' - -require 'serverspec/matcher' -require 'serverspec/helper' -require 'serverspec/subject' - -require 'specinfra' - require 'chef/dsl/audit' +require 'chef/audit/controls' require 'chef/audit/runner' diff --git a/lib/chef/audit/controls.rb b/lib/chef/audit/controls.rb new file mode 100644 index 0000000000..61e01b70f2 --- /dev/null +++ b/lib/chef/audit/controls.rb @@ -0,0 +1,120 @@ +# +# Author:: Claire McQuin (<claire@getchef.com>) +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/audit/audit_event_proxy' + +class Chef + class Audit + class Controls + + attr_reader :run_context + private :run_context + + ::RSpec::Core::ExampleGroup.define_example_group_method :control + ::RSpec::Core::ExampleGroup.define_example_group_method :__controls__ + + def initialize(run_context, *args, &block) + # To avoid cross-contamination between the configuration in our and other + # spec/spec_helper.rb files and the configuration for audits, we give + # each `controls` group its own RSpec. The files required here manipulate + # RSpec's global configuration, so we wait to load them. + # + # Additionally, name conflicts emerged between Chef's `package` and + # Serverspec's `package`. Requiring serverspec in here eliminates those + # namespace conflict. + # + # TODO: Once we have specs written for audit-mode, I'd like to play with + # which files can be moved around. I'm concerned about how much overhead + # we introduce by sequestering RSpec by `controls` group instead of in + # a common class or shared module. + require 'rspec' + require 'rspec/its' + require 'serverspec/matcher' + require 'serverspec/helper' + require 'serverspec/subject' + require 'specinfra' + + @run_context = run_context + configure + world.register(RSpec::Core::ExampleGroup.__controls__(*args, &block)) + end + + def run + # The first parameter passed to RSpec::Core::Runner.new + # is an instance of RSpec::Core::ConfigurationOptions, which is + # responsible for processing command line options passed through rspec. + # This then gets merged with the configuration. We'll just communicate + # directly with the Configuration here. + RSpec::Core::Runner.new(nil, configuration, world).run_specs(world.ordered_example_groups) + end + + private + def configuration + RSpec.configuration + end + + def world + RSpec.world + end + + # Sets up where output and error streams should stream to, adds formatters + # for people-friendly output of audit results and json for reporting. Also + # configures expectation frameworks. + def configure + # We're setting the output stream, but that will only be used for error situations + # Our formatter forwards events to the Chef event message bus + # TODO so some testing to see if these output to a log file - we probably need + # to register these before any formatters are added. + configuration.output_stream = Chef::Config[:log_location] + configuration.error_stream = Chef::Config[:log_location] + + add_formatters + disable_should_syntax + configure_specinfra + end + + def add_formatters + configuration.add_formatter(RSpec::Core::Formatters::DocumentationFormatter) + configuration.add_formatter(Chef::Audit::AuditEventProxy) + Chef::Audit::AuditEventProxy.events = run_context.events + end + + # Explicitly disable :should syntax. + # + # :should is deprecated in RSpec 3 and we have chosen to explicitly disable it + # in audits. If :should is used in an audit, the audit will fail with error + # message "undefined method `:should`" rather than issue a deprecation warning. + # + # This can be removed when :should is fully removed from RSpec. + def disable_should_syntax + RSpec.configure do |config| + config.expect_with :rspec do |c| + c.syntax = :expect + end + end + end + + def configure_specinfra + # TODO: We may need to change this based on operating system (there is a + # powershell backend) or roll our own. + Specinfra.configuration.backend = :exec + end + + end + end +end diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb index 0758dacd6d..bd8774b9c5 100644 --- a/lib/chef/audit/runner.rb +++ b/lib/chef/audit/runner.rb @@ -16,9 +16,6 @@ # limitations under the License. # -require 'chef/audit' -require 'chef/audit/audit_event_proxy' -require 'chef/audit/rspec_formatter' require 'chef/config' class Chef @@ -33,83 +30,8 @@ class Chef end def run - setup - register_controls_groups - - # The first parameter passed to RSpec::Core::Runner.new - # is an instance of RSpec::Core::ConfigurationOptions, which is - # responsible for processing command line options passed through rspec. - # This then gets merged with the configuration. We'll just communicate - # directly with the Configuration here. - audit_runner = RSpec::Core::Runner.new(nil, configuration, world) - audit_runner.run_specs(world.ordered_example_groups) - end - - private - - # RSpec configuration and world objects are heavy, so let's wait until - # we actually need them. - def configuration - RSpec.configuration - end - - def world - RSpec.world - end - - # Configure audits before run. - # Sets up where output and error streams should stream to, adds formatters - # for people-friendly output of audit results and json for reporting. Also - # configures expectation frameworks. - def setup - # We're setting the output stream, but that will only be used for error situations - # Our formatter forwards events to the Chef event message bus - # TODO so some testing to see if these output to a log file - we probably need - # to register these before any formatters are added. - configuration.output_stream = Chef::Config[:log_location] - configuration.error_stream = Chef::Config[:log_location] - # TODO im pretty sure I only need this because im running locally in rvmsudo - configuration.backtrace_exclusion_patterns.push(Regexp.new("/Users".gsub("/", File::SEPARATOR))) - configuration.backtrace_exclusion_patterns.push(Regexp.new("(eval)")) - configuration.color = Chef::Config[:color] - configuration.expose_dsl_globally = false - - add_formatters - disable_should_syntax - configure_specinfra + run_context.controls_groups.each { |ctls_grp| ctls_grp.run } end - - def add_formatters - configuration.add_formatter(Chef::Audit::RspecFormatter) - configuration.add_formatter(Chef::Audit::AuditEventProxy) - Chef::Audit::AuditEventProxy.events = run_context.events - end - - # Explicitly disable :should syntax. - # - # :should is deprecated in RSpec 3 and we have chosen to explicitly disable it - # in audits. If :should is used in an audit, the audit will fail with error - # message "undefined method `:should`" rather than issue a deprecation warning. - # - # This can be removed when :should is fully removed from RSpec. - def disable_should_syntax - configuration.expect_with :rspec do |c| - c.syntax = :expect - end - end - - def configure_specinfra - # TODO: We may need to change this based on operating system (there is a - # powershell backend) or roll our own. - Specinfra.configuration.backend = :exec - end - - # Register each controls group with the world, which will handle - # the ordering of the audits that will be run. - def register_controls_groups - run_context.controls_groups.each { |ctls_grp| world.register(ctls_grp) } - end - end end end diff --git a/lib/chef/client.rb b/lib/chef/client.rb index 16315b8e08..c7ddded1ff 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -330,6 +330,7 @@ class Chef runner.converge @events.converge_complete rescue Exception => e + Chef::Log.error("Converge failed with error message #{e.message}") @events.converge_failed(e) converge_exception = e end @@ -354,10 +355,12 @@ class Chef audit_exception = nil begin @events.audit_phase_start(run_status) + Chef::Log.info("Starting audit phase") auditor = Chef::Audit::Runner.new(run_context) auditor.run @events.audit_phase_complete rescue Exception => e + Chef::Log.error("Audit phase failed with error message #{e.message}") @events.audit_phase_failed(e) audit_exception = e end @@ -438,8 +441,17 @@ class Chef run_context = setup_run_context - converge_error = converge_and_save(run_context) - audit_error = run_audits(run_context) + unless Chef::Config[:audit_mode] == true + converge_error = converge_and_save(run_context) + else + Chef::Log.debug("Skipping converge. Chef is configured to run audits only.") + end + + unless Chef::Config[:audit_mode] == false + audit_error = run_audits(run_context) + else + Chef::Log.debug("Skipping audits. Chef is configured to converge the node only.") + end if converge_error || audit_error e = Chef::Exceptions::RunFailedWrappingError.new(converge_error, audit_error) diff --git a/lib/chef/dsl/audit.rb b/lib/chef/dsl/audit.rb index 1849b65633..520a667cd4 100644 --- a/lib/chef/dsl/audit.rb +++ b/lib/chef/dsl/audit.rb @@ -23,16 +23,13 @@ class Chef module Audit # Can encompass tests in a `control` block or `describe` block - ::RSpec::Core::ExampleGroup.define_example_group_method :control - ::RSpec::Core::ExampleGroup.define_example_group_method :__controls__ - # Adds the controls group and block (containing controls to execute) to the runner's list of pending examples def controls(*args, &block) raise ::Chef::Exceptions::NoAuditsProvided unless block name = args[0] raise AuditNameMissing if name.nil? || name.empty? - run_context.controls_groups << ::RSpec::Core::ExampleGroup.__controls__(*args, &block) + run_context.controls_groups << Chef::Audit::Controls.new(run_context, args, &block) end end diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb index 10958d628c..eb13efbf76 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -192,6 +192,7 @@ describe Chef::Client do let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") } let(:http_node_save) { double("Chef::REST (node save)") } let(:runner) { double("Chef::Runner") } + let(:audit_runner) { double("Chef::Audit::Runner") } let(:api_client_exists?) { false } @@ -253,6 +254,13 @@ describe Chef::Client do expect_any_instance_of(Chef::ResourceReporter).to receive(:run_completed) end + def stub_for_audit + expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) + expect(audit_runner).to receive(:run).and_return(true) + + expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:audit_phase_complete) + end + def stub_for_node_save allow(node).to receive(:data_for_save).and_return(node.for_json) @@ -282,6 +290,7 @@ describe Chef::Client do stub_for_node_load stub_for_sync_cookbooks stub_for_converge + stub_for_audit stub_for_node_save stub_for_run end |