diff options
39 files changed, 33 insertions, 2997 deletions
@@ -54,6 +54,10 @@ group(:development, :test) do # if you bump the ruby version you should confirm we don't end up with # two rake gems installed again gem "rake", "<= 12.3.0" + gem "rspec-core", "~> 3.5" + gem "rspec-mocks", "~> 3.5" + gem "rspec-expectations", "~> 3.5" + gem "rspec_junit_formatter", "~> 0.2.0" gem "simplecov" gem "webmock" diff --git a/Gemfile.lock b/Gemfile.lock index 02b45bfd74..398b90f02a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,12 +49,6 @@ PATH ohai (~> 15.0) plist (~> 3.2) proxifier (~> 1.0) - rspec-core (~> 3.5) - rspec-expectations (~> 3.5) - rspec-mocks (~> 3.5) - rspec_junit_formatter (~> 0.2.0) - serverspec (~> 2.7) - specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) chef (15.0.181-universal-mingw32) @@ -80,12 +74,6 @@ PATH ohai (~> 15.0) plist (~> 3.2) proxifier (~> 1.0) - rspec-core (~> 3.5) - rspec-expectations (~> 3.5) - rspec-mocks (~> 3.5) - rspec_junit_formatter (~> 0.2.0) - serverspec (~> 2.7) - specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) win32-api (~> 1.5.3) @@ -168,7 +156,7 @@ GEM highline (1.7.10) htmlentities (4.3.4) iniparse (1.4.4) - inspec-core (3.6.6) + inspec-core (3.7.1) addressable (~> 2.4) faraday (>= 0.9.0) hashie (~> 3.4) @@ -185,6 +173,7 @@ GEM rubyzip (~> 1.1) semverse sslshake (~> 1.2) + term-ansicolor thor (~> 0.20) tomlrb (~> 1.2) train-core (~> 1.5, >= 1.7.2) @@ -216,8 +205,6 @@ GEM necromancer (0.4.0) net-http-persistent (2.9.4) net-http-pipeline (1.0.1) - net-scp (1.2.1) - net-ssh (>= 2.6.5) net-sftp (2.1.2) net-ssh (>= 2.6.5) net-ssh (4.2.0) @@ -226,7 +213,6 @@ GEM net-ssh-multi (1.2.1) net-ssh (>= 2.6.5) net-ssh-gateway (>= 1.2.0) - net-telnet (0.1.1) netrc (0.11.0) octokit (4.13.0) sawyer (~> 0.8.0, >= 0.5.3) @@ -295,23 +281,12 @@ GEM addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) semverse (3.0.0) - serverspec (2.41.3) - multi_json - rspec (~> 3.0) - rspec-its - specinfra (~> 2.72) - sfl (2.3) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) slop (3.6.0) - specinfra (2.76.9) - net-scp - net-ssh (>= 2.7) - net-telnet (= 0.1.1) - sfl sslshake (1.3.0) strings (0.1.4) strings-ansi (~> 0.1.0) @@ -321,10 +296,13 @@ GEM structured_warnings (0.3.0) syslog-logger (1.6.8) systemu (2.6.5) + term-ansicolor (1.7.1) + tins (~> 1.0) thor (0.20.3) timers (4.3.0) + tins (1.20.2) tomlrb (1.2.8) - train-core (1.7.2) + train-core (1.7.4) json (>= 1.8, < 3.0) mixlib-shellout (~> 2.0) travis (1.8.9) @@ -337,7 +315,7 @@ GEM pusher-client (~> 0.4) typhoeus (~> 0.6, >= 0.6.8) tty-color (0.4.3) - tty-cursor (0.6.0) + tty-cursor (0.6.1) tty-prompt (0.18.1) necromancer (~> 0.4.0) pastel (~> 0.7.0) @@ -417,6 +395,10 @@ DEPENDENCIES pry-stack_explorer rake (<= 12.3.0) rb-readline + rspec-core (~> 3.5) + rspec-expectations (~> 3.5) + rspec-mocks (~> 3.5) + rspec_junit_formatter (~> 0.2.0) ruby-prof ruby-shadow simplecov diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 29dadf54e0..0c257a49b5 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -482,14 +482,6 @@ module ChefConfig default :ez, false default :enable_reporting, true default :enable_reporting_url_fatals, false - # Possible values for :audit_mode - # :enabled, :disabled, :audit_only, - # - # TODO: 11 Dec 2014: Currently audit-mode is an experimental feature - # and is disabled by default. When users choose to enable audit-mode, - # a warning is issued in application/client#reconfigure. - # This can be removed when audit-mode is enabled by default. - default :audit_mode, :disabled # Chef only needs ohai to run the hostname plugin for the most basic # functionality. If the rest of the ohai plugins are not needed (like in diff --git a/chef.gemspec b/chef.gemspec index 3e41b8e5ba..1f3c6e4291 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -38,13 +38,6 @@ Gem::Specification.new do |s| s.add_dependency "plist", "~> 3.2" s.add_dependency "iniparse", "~> 1.4" s.add_dependency "addressable" - - # Audit mode requires these, so they are non-developmental dependencies now - %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.5" } - s.add_dependency "rspec_junit_formatter", "~> 0.2.0" - s.add_dependency "serverspec", "~> 2.7" - s.add_dependency "specinfra", "~> 2.10" - s.add_dependency "syslog-logger", "~> 1.6" s.add_dependency "uuidtools", "~> 2.1.5" diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index 0e5584ded3..cdeb376f96 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -272,11 +272,6 @@ class Chef::Application::Client < Chef::Application boolean: true end - option :audit_mode, - long: "--audit-mode MODE", - description: "Enable audit-mode with `enabled`. Disable audit-mode with `disabled`. Skip converge and only perform audits with `audit-only`", - proc: lambda { |mo| mo.tr("-", "_").to_sym } - option :minimal_ohai, long: "--minimal-ohai", description: "Only run the bare minimum ohai plugins chef needs to function", @@ -374,13 +369,6 @@ class Chef::Application::Client < Chef::Application config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) @chef_client_json = config_fetcher.fetch_json end - - if mode = config[:audit_mode] || Chef::Config[:audit_mode] - expected_modes = [:enabled, :disabled, :audit_only] - unless expected_modes.include?(mode) - Chef::Application.fatal!(unrecognized_audit_mode(mode)) - end - end end def load_config_file @@ -521,17 +509,6 @@ class Chef::Application::Client < Chef::Application "\nEnable chef-client interval runs by setting `:client_fork = true` in your config file or adding `--fork` to your command line options." end - def audit_mode_settings_explanation - "\n* To enable audit mode after converge, use command line option `--audit-mode enabled` or set `audit_mode :enabled` in your config file." + - "\n* To disable audit mode, use command line option `--audit-mode disabled` or set `audit_mode :disabled` in your config file." + - "\n* To only run audit mode, use command line option `--audit-mode audit-only` or set `audit_mode :audit_only` in your config file." + - "\nAudit mode is disabled by default." - end - - def unrecognized_audit_mode(mode) - "Unrecognized setting #{mode} for audit mode." + audit_mode_settings_explanation - end - def fetch_recipe_tarball(url, path) Chef::Log.trace("Download recipes tarball from #{url} to #{path}") if File.exist?(url) diff --git a/lib/chef/application/exit_code.rb b/lib/chef/application/exit_code.rb index c87592f06b..6c9618fcb4 100644 --- a/lib/chef/application/exit_code.rb +++ b/lib/chef/application/exit_code.rb @@ -34,7 +34,7 @@ class Chef REBOOT_SCHEDULED: 35, REBOOT_NEEDED: 37, REBOOT_FAILED: 41, - AUDIT_MODE_FAILURE: 42, + # 42 was used by audit mode and should not be reused CLIENT_UPGRADED: 213, }.freeze @@ -78,8 +78,6 @@ class Chef VALID_RFC_062_EXIT_CODES[:REBOOT_NEEDED] elsif reboot_failed?(exception) VALID_RFC_062_EXIT_CODES[:REBOOT_FAILED] - elsif audit_failure?(exception) - VALID_RFC_062_EXIT_CODES[:AUDIT_MODE_FAILURE] elsif client_upgraded?(exception) VALID_RFC_062_EXIT_CODES[:CLIENT_UPGRADED] else @@ -105,12 +103,6 @@ class Chef end end - def audit_failure?(exception) - resolve_exception_array(exception).any? do |e| - e.is_a? Chef::Exceptions::AuditError - end - end - def client_upgraded?(exception) resolve_exception_array(exception).any? do |e| e.is_a? Chef::Exceptions::ClientUpgraded diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index 763d52226a..148e7720a5 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -288,9 +288,6 @@ class Chef::Application::Solo < Chef::Application config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) @chef_client_json = config_fetcher.fetch_json end - - # Disable auditing for solo - Chef::Config[:audit_mode] = :disabled end def setup_application diff --git a/lib/chef/audit/audit_event_proxy.rb b/lib/chef/audit/audit_event_proxy.rb deleted file mode 100644 index 1cb8545d28..0000000000 --- a/lib/chef/audit/audit_event_proxy.rb +++ /dev/null @@ -1,93 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Copyright:: Copyright 2014-2016, 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. -# - -RSpec::Support.require_rspec_core "formatters/base_text_formatter" - -class Chef - class Audit - class AuditEventProxy < ::RSpec::Core::Formatters::BaseFormatter - ::RSpec::Core::Formatters.register self, :stop, :example_group_started - - # TODO I don't like this, but I don't see another way to pass this in - # see rspec files configuration.rb#L671 and formatters.rb#L129 - def self.events=(events) - @@events = events - end - - def events - @@events - end - - def example_group_started(notification) - if notification.group.parent_groups.size == 1 - # top level `control_group` block - desc = notification.group.description - Chef::Log.trace("Entered `control_group` block named #{desc}") - events.control_group_started(desc) - end - end - - def stop(notification) - Chef::Log.info("Successfully executed all `control_group` blocks and contained examples") - notification.examples.each do |example| - control_group_name, control_data = build_control_from(example) - e = example.exception - if e - events.control_example_failure(control_group_name, control_data, e) - else - events.control_example_success(control_group_name, control_data) - end - end - end - - private - - def build_control_from(example) - described_class = example.metadata[:described_class] - if described_class - resource_type = described_class.class.name.split(":")[-1] - resource_name = described_class.name - end - - # The following code builds up the context - the list of wrapping `describe` or `control` blocks - describe_groups = [] - group = example.metadata[:example_group] - # If the innermost block has a resource instead of a string, don't include it in context - describe_groups.unshift(group[:description]) if described_class.nil? - group = group[:parent_example_group] - until group.nil? - describe_groups.unshift(group[:description]) - group = group[:parent_example_group] - end - - # We know all of our examples each live in a top-level `control_group` block - get this name now - outermost_group_desc = describe_groups.shift - - [outermost_group_desc, { - name: example.description, - desc: example.full_description, - resource_type: resource_type, - resource_name: resource_name, - context: describe_groups, - line_number: example.metadata[:line_number], - }] - end - - end - end -end diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb deleted file mode 100644 index 40f1f9ffd7..0000000000 --- a/lib/chef/audit/audit_reporter.rb +++ /dev/null @@ -1,176 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# -# Copyright:: Copyright 2014-2018, 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/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, :run_status - private :rest_client, :audit_data, :ordered_control_groups, :run_status - - PROTOCOL_VERSION = "0.1.1".freeze - - def initialize(rest_client) - @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 - @audit_phase_error = nil - end - - def run_context - run_status.run_context - end - - def audit_phase_start(run_status) - Chef::Log.trace("Audit Reporter starting") - @audit_data = AuditData.new(run_status.node.name, run_status.run_id) - @run_status = run_status - end - - def audit_phase_complete(audit_output) - Chef::Log.trace("Audit Reporter completed successfully without errors.") - ordered_control_groups.each_value do |control_group| - audit_data.add_control_group(control_group) - end - 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. - # We still want to send available audit information to the server so we process the - # known control groups. - def audit_phase_failed(error, audit_output) - # The stacktrace information has already been logged elsewhere - @audit_phase_error = error - Chef::Log.trace("Audit Reporter failed.") - ordered_control_groups.each_value do |control_group| - audit_data.add_control_group(control_group) - end - end - - def run_completed(node) - post_auditing_data - end - - def run_failed(error) - # Audit phase errors are captured when audit_phase_failed gets called. - # The error passed here isn't relevant to auditing, so we ignore it. - post_auditing_data - end - - def control_group_started(name) - if ordered_control_groups.key?(name) - raise Chef::Exceptions::AuditControlGroupDuplicate.new(name) - end - metadata = run_context.audits[name].metadata - ordered_control_groups.store(name, ControlGroupData.new(name, metadata)) - end - - def control_example_success(control_group_name, example_data) - control_group = ordered_control_groups[control_group_name] - control_group.example_success(example_data) - end - - def control_example_failure(control_group_name, example_data, error) - control_group = ordered_control_groups[control_group_name] - control_group.example_failure(example_data, error.message) - end - - # If @audit_enabled is nil or true, we want to run audits - def auditing_enabled? - Chef::Config[:audit_mode] != :disabled - end - - private - - def post_auditing_data - unless auditing_enabled? - Chef::Log.trace("Audit Reports are disabled. Skipping sending reports.") - return - end - - unless run_status - Chef::Log.trace("Run failed before audit mode was initialized, not sending audit report to server") - return - end - - audit_data.start_time = iso8601ify(run_status.start_time) - audit_data.end_time = iso8601ify(run_status.end_time) - - audit_history_url = "controls" - Chef::Log.trace("Sending audit report (run-id: #{audit_data.run_id})") - run_data = audit_data.to_h - - if @audit_phase_error - error_info = "#{@audit_phase_error.class}: #{@audit_phase_error.message}" - error_info << "\n#{@audit_phase_error.backtrace.join("\n")}" if @audit_phase_error.backtrace - run_data[:error] = error_info - end - - Chef::Log.trace "Audit Report:\n#{Chef::JSONCompat.to_json_pretty(run_data)}" - begin - rest_client.post(audit_history_url, run_data, headers) - rescue StandardError => e - if e.respond_to? :response - # 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 e.response.code == "404" - Chef::Log.trace("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) - if Chef::Config.chef_zero.enabled - Chef::Log.trace("Saving audit report to #{Chef::FileCache.load(error_file, false)}") - else - Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}") - end - 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 - end - end - - def headers(additional_headers = {}) - options = { "X-Ops-Audit-Report-Protocol-Version" => PROTOCOL_VERSION } - options.merge(additional_headers) - end - - def encode_gzip(data) - "".tap do |out| - Zlib::GzipWriter.wrap(StringIO.new(out)) { |gz| gz << data } - 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 deleted file mode 100644 index 63f301da02..0000000000 --- a/lib/chef/audit/control_group_data.rb +++ /dev/null @@ -1,145 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# -# Copyright:: Copyright 2014-2018, 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 "securerandom" - -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 - @run_id = run_id - @control_groups = [] - end - - def add_control_group(control_group) - control_groups << control_group - end - - def to_h - { - node_name: node_name, - run_id: run_id, - start_time: start_time, - end_time: end_time, - control_groups: control_groups.collect { |c| c.to_h }, - } - end - - alias_method :to_hash, :to_h - end - - class ControlGroupData - attr_reader :name, :status, :number_succeeded, :number_failed, :controls, :metadata - - def initialize(name, metadata = {}) - @status = "success" - @controls = [] - @number_succeeded = 0 - @number_failed = 0 - @name = name - @metadata = metadata - end - - def example_success(control_data) - @number_succeeded += 1 - control = create_control(control_data) - control.status = "success" - controls << control - control - end - - def example_failure(control_data, details) - @number_failed += 1 - @status = "failure" - control = create_control(control_data) - control.details = details if details - control.status = "failure" - controls << control - control - end - - def to_h - # We sort it so the examples appear in the output in the same order - # they appeared in the recipe - controls.sort! { |x, y| x.line_number <=> y.line_number } - h = { - name: name, - status: status, - number_succeeded: number_succeeded, - number_failed: number_failed, - controls: controls.collect { |c| c.to_h }, - } - # If there is a duplicate key, metadata will overwrite it - add_display_only_data(h).merge(metadata) - end - - alias_method :to_hash, :to_h - - private - - def create_control(control_data) - ControlData.new(control_data) - end - - # The id and control sequence number are ephemeral data - they are not needed - # to be persisted and can be regenerated at will. They are only needed - # for display purposes. - def add_display_only_data(group) - group[:id] = SecureRandom.uuid - group[:controls].collect!.with_index do |c, i| - # i is zero-indexed, and we want the display one-indexed - c[:sequence_number] = i + 1 - c - end - group - end - - end - - class ControlData - attr_reader :name, :resource_type, :resource_name, :context, :line_number - attr_accessor :status, :details - - def initialize(control_data = {}) - control_data.each do |k, v| - instance_variable_set("@#{k}", v) - end - end - - def to_h - h = { - name: name, - status: status, - details: details, - resource_type: resource_type, - resource_name: resource_name, - } - h[:context] = context || [] - h - end - - alias_method :to_hash, :to_h - end - - end -end diff --git a/lib/chef/audit/logger.rb b/lib/chef/audit/logger.rb deleted file mode 100644 index 759683ccc8..0000000000 --- a/lib/chef/audit/logger.rb +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright:: Copyright 2014-2016, 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 "stringio" - -class Chef - class Audit - class Logger - def self.puts(message = "") - @buffer ||= StringIO.new - @buffer.puts(message) - - Chef::Log.info(message) - end - - def self.read_buffer - return "" if @buffer.nil? - @buffer.string - end - end - end -end diff --git a/lib/chef/audit/rspec_formatter.rb b/lib/chef/audit/rspec_formatter.rb deleted file mode 100644 index 234202b684..0000000000 --- a/lib/chef/audit/rspec_formatter.rb +++ /dev/null @@ -1,37 +0,0 @@ -# -# Author:: Serdar Sutay (<serdar@chef.io>) -# Copyright:: Copyright 2014-2016, 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 "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 deleted file mode 100644 index 847d5efbd5..0000000000 --- a/lib/chef/audit/runner.rb +++ /dev/null @@ -1,196 +0,0 @@ -# -# Author:: Claire McQuin (<claire@chef.io>) -# Copyright:: Copyright 2014-2018, 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/logger" - -class Chef - class Audit - class Runner - - attr_reader :run_context - private :run_context - - def initialize(run_context) - @run_context = run_context - end - - def run - setup - register_control_groups - do_run - end - - def failed? - RSpec.world.reporter.failed_examples.size > 0 - end - - def num_failed - RSpec.world.reporter.failed_examples.size - end - - def num_total - RSpec.world.reporter.examples.size - end - - def exclusion_pattern - Regexp.new(".+[\\\/]lib[\\\/]chef[\\\/]") - end - - private - - # Prepare to run audits: - # - Require files - # - Configure RSpec - # - Configure Specinfra/Serverspec - def setup - require_deps - configure_rspec - configure_specinfra - end - - # RSpec uses a global configuration object, RSpec.configuration. We found - # there was interference between the configuration for audit-mode and - # the configuration for our own spec tests in these cases: - # 1. Specinfra and Serverspec modify RSpec.configuration when loading. - # 2. Setting output/error streams. - # 3. Adding formatters. - # 4. Defining example group aliases. - # - # Moreover, Serverspec loads its DSL methods into the global namespace, - # which causes conflicts with the Chef namespace for resources and packages. - # - # We wait until we're in the audit-phase of the chef-client run to load - # these files. This helps with the namespacing problems we saw, and - # prevents Specinfra and Serverspec from modifying the RSpec configuration - # used by our spec tests. - def require_deps - require "rspec" - require "rspec/its" - require "specinfra" - require "specinfra/helper" - require "specinfra/helper/set" - require "serverspec/helper" - require "serverspec/matcher" - require "serverspec/subject" - require "chef/audit/audit_event_proxy" - require "chef/audit/rspec_formatter" - - Specinfra::Backend::Cmd.send(:include, Specinfra::Helper::Set) - end - - # Configure RSpec just the way we like it: - # - Set location of error and output streams - # - Add custom audit-mode formatters - # - Explicitly disable :should syntax - # - Set :color option according to chef config - # - Disable exposure of global DSL - def configure_rspec - set_streams - add_formatters - disable_should_syntax - - RSpec.configure do |c| - c.color = Chef::Config[:color] - c.expose_dsl_globally = false - c.project_source_dirs = Array(Chef::Config[:cookbook_path]) - c.backtrace_exclusion_patterns << exclusion_pattern - end - end - - # Set the error and output streams which audit-mode will use to report - # human-readable audit information. - # - # This should always be called before #add_formatters. RSpec won't allow - # the output stream to be changed for a formatter once the formatter has - # been added. - def set_streams - RSpec.configuration.output_stream = Chef::Audit::Logger - RSpec.configuration.error_stream = Chef::Audit::Logger - end - - # Add formatters which we use to - # 1. Output human-readable data to the output stream, - # 2. Collect JSON data to send back to the analytics server. - def add_formatters - RSpec.configuration.add_formatter(Chef::Audit::AuditEventProxy) - RSpec.configuration.add_formatter(Chef::Audit::RspecFormatter) - Chef::Audit::AuditEventProxy.events = run_context.events - end - - # Audit-mode uses RSpec 3. :should syntax is deprecated by default in - # RSpec 3, so we explicitly disable it here. - # - # This can be removed once :should is removed from RSpec. - def disable_should_syntax - RSpec.configure do |config| - config.expect_with :rspec do |c| - c.syntax = :expect - end - end - end - - # Set up the backend for Specinfra/Serverspec. :exec is the local system; on Windows, it is :cmd - def configure_specinfra - if Chef::Platform.windows? - Specinfra.configuration.backend = :cmd - Specinfra.configuration.os = { family: "windows" } - else - Specinfra.configuration.backend = :exec - end - end - - # Iterates through the control groups registered to this run_context, builds an - # example group (RSpec::Core::ExampleGroup) object per control group, and - # registers the group with the RSpec.world. - # - # We could just store an array of example groups and not use RSpec.world, - # but it may be useful later if we decide to apply our own ordering scheme - # or use example group filters. - def register_control_groups - add_example_group_methods - run_context.audits.each do |name, group| # rubocop:disable Performance/HashEachMethods - ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block) - RSpec.world.record(ctl_grp) - end - end - - # Add example group method aliases to RSpec. - # - # __control_group__: Used internally to create example groups from the control - # groups saved in the run_context. - # control: Used within the context of a control group block, like RSpec's - # describe or context. - def add_example_group_methods - RSpec::Core::ExampleGroup.define_example_group_method :__control_group__ - RSpec::Core::ExampleGroup.define_example_group_method :control - end - - # Run the audits! - def do_run - # RSpec::Core::Runner wants to be initialized with an - # RSpec::Core::ConfigurationOptions object, which is used to process - # command line configuration arguments. We directly fiddle with the - # internal RSpec configuration object, so we give nil here and let - # RSpec pick up its own configuration and world. - runner = RSpec::Core::Runner.new(nil) - runner.run_specs(RSpec.world.ordered_example_groups) - end - - end - end -end diff --git a/lib/chef/client.rb b/lib/chef/client.rb index 0dd6b2666f..3c1921e8dc 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -26,7 +26,6 @@ require "chef/deprecated" require "chef/server_api" require "chef/api_client" require "chef/api_client/registration" -require "chef/audit/runner" require "chef/node" require "chef/role" require "chef/file_cache" @@ -48,7 +47,6 @@ require "chef/version" require "chef/action_collection" require "chef/resource_reporter" require "chef/data_collector" -require "chef/audit/audit_reporter" require "chef/run_lock" require "chef/policy_builder" require "chef/request_id" @@ -219,23 +217,6 @@ class Chef # @see #converge_and_save # @see Chef::Runner # - # Phase 4: Audit - # -------------- - # Runs 'control_group' audits in recipes. This entire section can be enabled or disabled with config. - # - # 1. 'control_group' DSL collects audits during Phase 2 - # 2. Audits are run using RSpec - # 3. Errors are collected and reported using the formatters - # - # @see #run_audits - # @see Chef::Audit::Runner#run - # - # @raise [Chef::Exceptions::RunFailedWrappingError] If converge or audit failed. - # - # @see Chef::Config#enforce_path_sanity - # @see Chef::Config#solo - # @see Chef::Config#audit_mode - # # @return Always returns true. # def run @@ -284,16 +265,7 @@ class Chef load_required_recipe(@rest, run_context) unless Chef::Config[:solo_legacy_mode] - if Chef::Config[:audit_mode] != :audit_only - converge_error = converge_and_save(run_context) - end - - if Chef::Config[:why_run] == true - # why_run should probably be renamed to why_converge - logger.debug("Not running controls in 'why-run' mode - this mode is used to see potential converge changes") - elsif Chef::Config[:audit_mode] != :disabled - audit_error = run_audits(run_context) - end + converge_error = converge_and_save(run_context) # Raise converge_error so run_failed reporters/events are processed. raise converge_error if converge_error @@ -324,22 +296,11 @@ class Chef runlock.release end - # Raise audit, converge, and other errors here so that we exit + # Raise converge, and other errors here so that we exit # with the proper exit status code and everything gets raised # as a RunFailedWrappingError - if run_error || converge_error || audit_error - error = if Chef::Config[:audit_mode] == :disabled - run_error || converge_error - else - e = if run_error == converge_error - Chef::Exceptions::RunFailedWrappingError.new(converge_error, audit_error) - else - Chef::Exceptions::RunFailedWrappingError.new(run_error, converge_error, audit_error) - end - e.fill_backtrace - e - end - + if run_error || converge_error + error = run_error || converge_error Chef::Application.debug_stacktrace(error) raise error end @@ -349,7 +310,7 @@ class Chef # # Private API - # TODO make this stuff protected or private + # @todo make this stuff protected or private # # @api private @@ -417,8 +378,7 @@ class Chef # @api private def register_reporters [ - Chef::ResourceReporter.new(rest_clean), - Chef::Audit::AuditReporter.new(rest_clean), + Chef::ResourceReporter.new(rest_clean) ].each do |r| events.register(r) end @@ -695,14 +655,9 @@ class Chef # # @param run_context The run context. # - # @return The thrown exception, if we are in audit mode. `nil` means the - # converge was successful or ended early. - # - # @raise Any converge exception, unless we are in audit mode, in which case - # we *return* the exception. + # @raise Any converge exception # # @see Chef::Runner#converge - # @see Chef::Config#audit_mode # @see Chef::EventDispatch#converge_start # @see Chef::EventDispatch#converge_complete # @see Chef::EventDispatch#converge_failed @@ -720,7 +675,6 @@ class Chef events.converge_complete rescue Exception => e events.converge_failed(e) - raise e if Chef::Config[:audit_mode] == :disabled converge_exception = e end end @@ -732,15 +686,10 @@ class Chef # # @param run_context The run context. # - # @return The thrown exception, if we are in audit mode. `nil` means the - # converge was successful or ended early. - # - # @raise Any converge or node save exception, unless we are in audit mode, - # in which case we *return* the exception. + # @raise Any converge or node save exception # # @see #converge # @see #save_updated_mode - # @see Chef::Config#audit_mode # # @api private # @@ -755,51 +704,13 @@ class Chef begin save_updated_node rescue Exception => e - raise e if Chef::Config[:audit_mode] == :disabled - converge_exception = e + raise e end end converge_exception end # - # Run the audit phase. - # - # Triggers the audit_phase_start, audit_phase_complete and - # audit_phase_failed events. - # - # @param run_context The run context. - # - # @return Any thrown exceptions. `nil` if successful. - # - # @see Chef::Audit::Runner#run - # @see Chef::EventDispatch#audit_phase_start - # @see Chef::EventDispatch#audit_phase_complete - # @see Chef::EventDispatch#audit_phase_failed - # - # @api private - # - def run_audits(run_context) - begin - events.audit_phase_start(run_status) - logger.info("Starting audit phase") - auditor = Chef::Audit::Runner.new(run_context) - auditor.run - if auditor.failed? - audit_exception = Chef::Exceptions::AuditsFailed.new(auditor.num_failed, auditor.num_total) - @events.audit_phase_failed(audit_exception, Chef::Audit::Logger.read_buffer) - else - @events.audit_phase_complete(Chef::Audit::Logger.read_buffer) - end - rescue Exception => e - logger.error("Audit phase failed with error message: #{e.message}") - @events.audit_phase_failed(e, Chef::Audit::Logger.read_buffer) - audit_exception = e - end - audit_exception - end - - # # Expands the run list. # # @return [Chef::RunListExpansion] The expanded run list. diff --git a/lib/chef/dsl/audit.rb b/lib/chef/dsl/audit.rb deleted file mode 100644 index 927523e976..0000000000 --- a/lib/chef/dsl/audit.rb +++ /dev/null @@ -1,51 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Copyright:: Copyright 2014-2016, 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/exceptions" - -class Chef - module DSL - module Audit - - # Can encompass tests in a `control` block or `describe` block - # Adds the controls group and block (containing controls to execute) to the runner's list of pending examples - def control_group(*args, &block) - raise Chef::Exceptions::NoAuditsProvided unless block - - name = args[0] - if name.nil? || name.empty? - raise Chef::Exceptions::AuditNameMissing - elsif run_context.audits.key?(name) - raise Chef::Exceptions::AuditControlGroupDuplicate.new(name) - end - - # This DSL will only work in the Recipe class because that exposes the cookbook_name - cookbook_name = self.cookbook_name - metadata = { - cookbook_name: cookbook_name, - cookbook_version: run_context.cookbook_collection[cookbook_name].version, - recipe_name: recipe_name, - line_number: block.source_location[1], - } - - run_context.audits[name] = Struct.new(:args, :block, :metadata).new(args, block, metadata) - end - - end - end -end diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb index 9da812cec0..4d68a36a81 100644 --- a/lib/chef/dsl/recipe.rb +++ b/lib/chef/dsl/recipe.rb @@ -24,7 +24,6 @@ require "chef/dsl/data_query" require "chef/dsl/include_recipe" require "chef/dsl/registry_helper" require "chef/dsl/reboot_pending" -require "chef/dsl/audit" require "chef/dsl/powershell" require "chef/dsl/core" require "chef/mixin/lazy_module_include" @@ -55,7 +54,6 @@ class Chef include Chef::DSL::IncludeRecipe include Chef::DSL::RegistryHelper include Chef::DSL::RebootPending - include Chef::DSL::Audit include Chef::DSL::Powershell include Chef::DSL::Resources include Chef::DSL::Definitions diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb index 3b0b70c9b9..be51cf362f 100644 --- a/lib/chef/event_dispatch/base.rb +++ b/lib/chef/event_dispatch/base.rb @@ -292,37 +292,6 @@ class Chef def converge_failed(exception) end - ################################## - # Audit Mode Events - # This phase is currently experimental and these event APIs are subject to change - ################################## - - # Called before audit phase starts - def audit_phase_start(run_status) - end - - # Called when audit phase successfully finishes - def audit_phase_complete(audit_output) - end - - # Called if there is an uncaught exception during the audit phase. The audit runner should - # be catching and handling errors from the examples, so this is only uncaught errors (like - # bugs in our handling code) - def audit_phase_failed(exception, audit_output) - end - - # Signifies the start of a `control_group` block with a defined name - def control_group_started(name) - end - - # An example in a `control_group` block completed successfully - def control_example_success(control_group_name, example_data) - end - - # An example in a `control_group` block failed with the provided error - def control_example_failure(control_group_name, example_data, error) - end - # TODO: need events for notification resolve? # def notifications_resolved # end diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 41e0cdb33e..0a6c7dc635 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -443,26 +443,7 @@ class Chef end end - class AuditError < RuntimeError; end - - class AuditControlGroupDuplicate < AuditError - def initialize(name) - super "Control group with name '#{name}' has already been defined" - end - end - class AuditNameMissing < AuditError; end - class NoAuditsProvided < AuditError - def initialize - super "You must provide a block with controls" - end - end - class AuditsFailed < AuditError - def initialize(num_failed, num_total) - super "Audit phase found failures - #{num_failed}/#{num_total} controls failed" - end - end - - # If a converge or audit fails, we want to wrap the output from those errors into 1 error so we can + # If a converge fails, we want to wrap the output from those errors into 1 error so we can # see both issues in the output. It is possible that nil will be provided. You must call `fill_backtrace` # to correctly populate the backtrace with the wrapped backtraces. class RunFailedWrappingError < RuntimeError diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb index 936738f147..0b0a589d56 100644 --- a/lib/chef/formatters/doc.rb +++ b/lib/chef/formatters/doc.rb @@ -8,8 +8,7 @@ class Chef # show context. class Doc < Formatters::Base - attr_reader :start_time, :end_time, :successful_audits, :failed_audits - private :successful_audits, :failed_audits + attr_reader :start_time, :end_time cli_name(:doc) @@ -18,8 +17,6 @@ class Chef @updated_resources = 0 @up_to_date_resources = 0 - @successful_audits = 0 - @failed_audits = 0 @start_time = Time.now @end_time = @start_time @skipped_resources = 0 @@ -51,10 +48,6 @@ class Chef @up_to_date_resources + @updated_resources + @skipped_resources end - def total_audits - successful_audits + failed_audits - end - def run_completed(node) @end_time = Time.now # Print out deprecations. @@ -85,9 +78,6 @@ class Chef puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated" else puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{pretty_elapsed_time}" - if total_audits > 0 - puts_line " #{successful_audits}/#{total_audits} controls succeeded" - end end end @@ -97,9 +87,6 @@ class Chef puts_line "Chef Client failed. #{@updated_resources} resources would have been updated" else puts_line "Chef Client failed. #{@updated_resources} resources updated in #{pretty_elapsed_time}" - if total_audits > 0 - puts_line " #{successful_audits} controls succeeded" - end end end @@ -239,37 +226,6 @@ class Chef converge_complete end - # Called before audit phase starts - def audit_phase_start(run_status) - puts_line "Starting audit phase" - end - - def audit_phase_complete(audit_output) - puts_line audit_output - puts_line "Auditing complete" - end - - def audit_phase_failed(error, audit_output) - puts_line audit_output - puts_line "" - puts_line "Audit phase exception:" - indent - puts_line (error.message).to_s - if error.backtrace - error.backtrace.each do |l| - puts_line l - end - end - end - - def control_example_success(control_group_name, example_data) - @successful_audits += 1 - end - - def control_example_failure(control_group_name, example_data, error) - @failed_audits += 1 - end - # Called before action is executed on a resource. def resource_action_start(resource, action, notification_type = nil, notifier = nil) if resource.cookbook_name && resource.recipe_name @@ -284,7 +240,7 @@ class Chef @current_recipe = resource_recipe indent end - # TODO: info about notifies + # @todo info about notifies start_line "* #{resource} action #{action}", stream: resource indent end diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb index f72a22db2d..e0b32d2b9d 100644 --- a/lib/chef/mixin/properties.rb +++ b/lib/chef/mixin/properties.rb @@ -83,7 +83,7 @@ class Chef # is part of object identity. Defaults to `false`. # @option options [Boolean] :sensitive `true` if this property could # contain sensitive information and whose value should be redacted - # in any resource reporting / auditing output. Defaults to `false`. + # in any resource reporting output. Defaults to `false`. # # @example Bare property # property :x @@ -176,9 +176,6 @@ class Chef # by providing additional options for a package manager to use when # installing a package. # - # This list is used by the Chef client auditing system to extract - # information from resources to describe changes made to the system. - # # This method is unnecessary when declaring properties with `property`; # properties are added to state_properties by default, and can be turned off # with `desired_state: false`. diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb index e407a0e7be..bba109360d 100644 --- a/lib/chef/run_context.rb +++ b/lib/chef/run_context.rb @@ -106,11 +106,6 @@ class Chef attr_accessor :action_collection # - # The list of control groups to execute during the audit phase - # - attr_reader :audits - - # # Pointer back to the Chef::Runner that created this # attr_accessor :runner @@ -204,7 +199,6 @@ class Chef # Initialize state that applies to both Chef::RunContext and Chef::ChildRunContext # def initialize_child_state - @audits = {} @resource_collection = Chef::ResourceCollection.new(self) @before_notification_collection = Hash.new { |h, k| h[k] = [] } @immediate_notification_collection = Hash.new { |h, k| h[k] = [] } @@ -645,8 +639,6 @@ class Chef end CHILD_STATE = %w{ - audits - audits= create_child add_delayed_action delayed_actions diff --git a/spec/functional/audit/rspec_formatter_spec.rb b/spec/functional/audit/rspec_formatter_spec.rb deleted file mode 100644 index f30f7bbd8a..0000000000 --- a/spec/functional/audit/rspec_formatter_spec.rb +++ /dev/null @@ -1,54 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Author:: Claire McQuin (<claire@chef.io>) -# -# Copyright:: Copyright 2014-2016, 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 "spec_helper" -require "rspec/core/sandbox" -require "chef/audit/runner" -require "rspec/support/spec/in_sub_process" -require "rspec/support/spec/stderr_splitter" -require "chef/audit/rspec_formatter" - -describe Chef::Audit::RspecFormatter do - include RSpec::Support::InSubProcess - - let(:events) { double("events").as_null_object } - let(:audits) { {} } - let(:run_context) { instance_double(Chef::RunContext, events: events, audits: audits) } - let(:runner) { Chef::Audit::Runner.new(run_context) } - - let(:output) { double("output") } - # aggressively define this so we can mock out the new call later - let!(:formatter) { Chef::Audit::RspecFormatter.new(output) } - - around(:each) do |ex| - RSpec::Core::Sandbox.sandboxed { ex.run } - end - - it "should not close the output using our formatter" do - in_sub_process do - expect_any_instance_of(Chef::Audit::RspecFormatter).to receive(:new).and_return(formatter) - expect(formatter).to receive(:close).and_call_original - expect(output).to_not receive(:close) - - runner.run - end - end - -end diff --git a/spec/functional/audit/runner_spec.rb b/spec/functional/audit/runner_spec.rb deleted file mode 100644 index f80256fa5c..0000000000 --- a/spec/functional/audit/runner_spec.rb +++ /dev/null @@ -1,121 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Copyright:: Copyright 2014-2016, 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 "spec_helper" -require "rspec/core/sandbox" -require "chef/audit/runner" -require "rspec/support/spec/in_sub_process" -require "rspec/support/spec/stderr_splitter" -require "tempfile" - -## -# This functional test ensures that our runner can be setup to not interfere with existing RSpec -# configuration and world objects. When normally running Chef, there is only 1 RSpec instance -# so this isn't needed. In unit testing the Runner should be mocked appropriately. - -describe Chef::Audit::Runner do - - # The functional tests must be run in a sub_process. Including Serverspec includes the Serverspec DSL - this - # conflicts with our `package` DSL (among others) when we try to test `package` inside an RSpec example. - # Our DSL leverages `method_missing` while the Serverspec DSL defines a method on the RSpec::Core::ExampleGroup. - # The defined method wins our and returns before our `method_missing` DSL can be called. - # - # Running in a sub_process means the serverspec libraries will only be included in a forked process, not the main one. - include RSpec::Support::InSubProcess - - let(:events) { double("events").as_null_object } - let(:runner) { Chef::Audit::Runner.new(run_context) } - let(:stdout) { StringIO.new } - - around(:each) do |ex| - RSpec::Core::Sandbox.sandboxed { ex.run } - end - - describe "#run" do - - let(:audits) { {} } - let(:run_context) { instance_double(Chef::RunContext, events: events, audits: audits) } - let(:control_group_name) { "control_group_name" } - - # Set cookbook path to include our parent, so that it will recognize this - # rspec file as one that should show up in the backtrace. - before(:each) { Chef::Config[:cookbook_path] = File.dirname(__FILE__) } - - shared_context "passing audit" do - let(:audits) do - should_pass = lambda do - it "should pass" do - expect(2 - 2).to eq(0) - end - end - { control_group_name => Struct.new(:args, :block).new([control_group_name], should_pass) } - end - end - - shared_context "failing audit" do - let(:audits) do - should_fail = lambda do - it "should fail" do - expect(2 - 1).to eq(0) - end - end - { control_group_name => Struct.new(:args, :block).new([control_group_name], should_fail) } - end - end - - describe "log location is stdout" do - before do - allow(Chef::Log).to receive(:info) do |msg| - stdout.puts(msg) - end - end - - it "Correctly runs an empty controls block" do - in_sub_process do - runner.run - end - end - - context "there is a single successful control" do - include_context "passing audit" - it "correctly runs" do - in_sub_process do - runner.run - - expect(stdout.string).to match(/1 example, 0 failures/) - end - end - end - - context "there is a single failing control" do - include_context "failing audit" - it "correctly runs" do - in_sub_process do - runner.run - - expect(stdout.string).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/) - expect(stdout.string).to match(/1 example, 1 failure/) - expect(stdout.string).to match(/# control_group_name should fail/) - end - end - end - end - - end - -end diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb index d15a7d2afa..afe94f854e 100644 --- a/spec/integration/client/client_spec.rb +++ b/spec/integration/client/client_spec.rb @@ -407,45 +407,6 @@ describe "chef-client" do end end - when_the_repository "has a cookbook with only an audit recipe" do - - before do - file "config/client.rb", <<~EOM - local_mode true - cookbook_path "#{path_to('cookbooks')}" - audit_mode :enabled - EOM - end - - it "should exit with a zero code when there is not an audit failure" do - file "cookbooks/audit_test/recipes/succeed.rb", <<~RECIPE - control_group "control group without top level control" do - it "should succeed" do - expect(2 - 2).to eq(0) - end - end - RECIPE - - result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::succeed' -l info", cwd: chef_dir) - expect(result.error?).to be_falsey - expect(result.stdout).to include("Successfully executed all `control_group` blocks and contained examples") - end - - it "should exit with a non-zero code when there is an audit failure" do - file "cookbooks/audit_test/recipes/fail.rb", <<~RECIPE - control_group "control group without top level control" do - it "should fail" do - expect(2 - 2).to eq(1) - end - end - RECIPE - - result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'audit_test::fail'", cwd: chef_dir) - expect(result.error?).to be_truthy - expect(result.stdout).to include("Failure/Error: expect(2 - 2).to eq(1)") - end - end - when_the_repository "has a cookbook that deploys a file" do before do file "cookbooks/x/recipes/default.rb", <<~RECIPE diff --git a/spec/integration/client/exit_code_spec.rb b/spec/integration/client/exit_code_spec.rb index a6e070f9a5..2e29502070 100644 --- a/spec/integration/client/exit_code_spec.rb +++ b/spec/integration/client/exit_code_spec.rb @@ -34,14 +34,6 @@ describe "chef-client" do EOM end - def setup_client_rb_with_audit_mode - file "config/client.rb", <<~EOM - local_mode true - cookbook_path "#{path_to('cookbooks')}" - audit_mode :audit_only - EOM - end - def run_chef_client_and_expect_exit_code(exit_code) shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", cwd: chef_dir, @@ -63,25 +55,6 @@ describe "chef-client" do end end - context "with an audit recipe" do - context "which fails" do - before do - file "cookbooks/x/recipes/default.rb", <<~RECIPE - control_group "control group without top level control" do - it "should fail" do - expect(4 - 4).to eq(1) - end - end - RECIPE - end - - it "exits with AUDIT_MODE_FAILURE, 42" do - setup_client_rb_with_audit_mode - run_chef_client_and_expect_exit_code 42 - end - end - end - context "with a recipe" do context "which throws an error" do before { file "cookbooks/x/recipes/default.rb", "raise 'BOOM'" } diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb index 40f690abb1..f58d1ed079 100644 --- a/spec/unit/application/client_spec.rb +++ b/spec/unit/application/client_spec.rb @@ -337,75 +337,6 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config end end - describe "audit mode" do - shared_examples "experimental feature" do - before do - allow(Chef::Log).to receive(:warn) - end - end - - shared_examples "unrecognized setting" do - it "fatals with a message including the incorrect setting" do - expect(Chef::Application).to receive(:fatal!).with(/Unrecognized setting #{mode} for audit mode/) - app.reconfigure - end - end - - shared_context "set via config file" do - before do - Chef::Config[:audit_mode] = mode - end - end - - shared_context "set via command line" do - before do - ARGV.replace(["--audit-mode", mode]) - end - end - - describe "enabled via config file" do - include_context "set via config file" do - let(:mode) { :enabled } - include_examples "experimental feature" - end - end - - describe "enabled via command line" do - include_context "set via command line" do - let(:mode) { "enabled" } - include_examples "experimental feature" - end - end - - describe "audit_only via config file" do - include_context "set via config file" do - let(:mode) { :audit_only } - include_examples "experimental feature" - end - end - - describe "audit-only via command line" do - include_context "set via command line" do - let(:mode) { "audit-only" } - include_examples "experimental feature" - end - end - - describe "unrecognized setting via config file" do - include_context "set via config file" do - let(:mode) { :derp } - include_examples "unrecognized setting" - end - end - - describe "unrecognized setting via command line" do - include_context "set via command line" do - let(:mode) { "derp" } - include_examples "unrecognized setting" - end - end - end - describe "when both the pidfile and lockfile opts are set to the same value" do before do diff --git a/spec/unit/application/exit_code_spec.rb b/spec/unit/application/exit_code_spec.rb index 7783cf3ed7..e8a0072ff3 100644 --- a/spec/unit/application/exit_code_spec.rb +++ b/spec/unit/application/exit_code_spec.rb @@ -49,10 +49,6 @@ describe Chef::Application::ExitCode do expect(valid_rfc_exit_codes.include?(3)).to eq(true) end - it "validates a AUDIT_MODE_FAILURE return code of 42" do - expect(valid_rfc_exit_codes.include?(42)).to eq(true) - end - it "validates a REBOOT_SCHEDULED return code of 35" do expect(valid_rfc_exit_codes.include?(35)).to eq(true) end @@ -98,12 +94,6 @@ describe Chef::Application::ExitCode do expect(exit_codes.normalize_exit_code(Exception.new("BOOM"))).to eq(1) end - it "returns AUDIT_MODE_FAILURE when there is an audit error" do - audit_error = Chef::Exceptions::AuditError.new("BOOM") - runtime_error = Chef::Exceptions::RunFailedWrappingError.new(audit_error) - expect(exit_codes.normalize_exit_code(runtime_error)).to eq(42) - end - it "returns REBOOT_SCHEDULED when there is an reboot requested" do reboot_error = Chef::Exceptions::Reboot.new("BOOM") runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error) diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb index 3f7c203c67..939300b7e4 100644 --- a/spec/unit/application/solo_spec.rb +++ b/spec/unit/application/solo_spec.rb @@ -49,11 +49,6 @@ describe Chef::Application::Solo do expect(Chef::Config[:solo]).to be_truthy end - it "should set audit-mode to :disabled" do - app.reconfigure - expect(Chef::Config[:audit_mode]).to be :disabled - end - describe "when configured to not fork the client process" do before do Chef::Config[:client_fork] = false diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index b8d7242466..ef28dbb21b 100644 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -305,8 +305,8 @@ describe Chef::Application do describe "when a standard exit code is supplied" do it "should exit with the given exit code" do - expect(Process).to receive(:exit).with(42).and_return(true) - Chef::Application.fatal! "blah", 42 + expect(Process).to receive(:exit).with(41).and_return(true) + Chef::Application.fatal! "blah", 41 end end diff --git a/spec/unit/audit/audit_event_proxy_spec.rb b/spec/unit/audit/audit_event_proxy_spec.rb deleted file mode 100644 index a9b27f238e..0000000000 --- a/spec/unit/audit/audit_event_proxy_spec.rb +++ /dev/null @@ -1,318 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Author:: Claire McQuin (<claire@chef.io>) -# -# Copyright:: Copyright 2014-2016, 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 "spec_helper" -require "chef/audit/audit_event_proxy" - -describe Chef::Audit::AuditEventProxy do - - let(:stdout) { StringIO.new } - let(:events) { double("Chef::Events") } - let(:audit_event_proxy) { Chef::Audit::AuditEventProxy.new(stdout) } - - before do - Chef::Audit::AuditEventProxy.events = events - end - - describe "#example_group_started" do - - let(:description) { "poots" } - let(:group) do - double("ExampleGroup", parent_groups: parents, - description: description) end - let(:notification) { double("Notification", group: group) } - - context "when notified from a top-level example group" do - - let(:parents) { [double("ExampleGroup")] } - - it "notifies control_group_started event" do - expect(Chef::Log).to receive(:trace) - .with("Entered \`control_group\` block named poots") - expect(events).to receive(:control_group_started) - .with(description) - audit_event_proxy.example_group_started(notification) - end - end - - context "when notified from an inner-level example group" do - - let(:parents) { [double("ExampleGroup"), double("OuterExampleGroup")] } - - it "does nothing" do - expect(events).to_not receive(:control_group_started) - audit_event_proxy.example_group_started(notification) - end - end - end - - describe "#stop" do - - let(:examples) { [] } - let(:notification) { double("Notification", examples: examples) } - let(:exception) { nil } - let(:example) { double("Example", exception: exception) } - let(:control_group_name) { "audit test" } - let(:control_data) { double("ControlData") } - - before do - allow(Chef::Log).to receive(:info) # silence messages to output stream - end - - it "sends a message that audits completed" do - expect(Chef::Log).to receive(:info).with("Successfully executed all \`control_group\` blocks and contained examples") - audit_event_proxy.stop(notification) - end - - context "when an example succeeded" do - - let(:examples) { [example] } - let(:excpetion) { nil } - - before do - allow(audit_event_proxy).to receive(:build_control_from) - .with(example) - .and_return([control_group_name, control_data]) - end - - it "notifies events" do - expect(events).to receive(:control_example_success) - .with(control_group_name, control_data) - audit_event_proxy.stop(notification) - end - end - - context "when an example failed" do - - let(:examples) { [example] } - let(:exception) { double("ExpectationNotMet") } - - before do - allow(audit_event_proxy).to receive(:build_control_from) - .with(example) - .and_return([control_group_name, control_data]) - end - - it "notifies events" do - expect(events).to receive(:control_example_failure) - .with(control_group_name, control_data, exception) - audit_event_proxy.stop(notification) - end - end - - describe "#build_control_from" do - - let(:examples) { [example] } - - let(:example) do - double("Example", metadata: metadata, - description: example_description, - full_description: full_description, exception: nil) end - - let(:metadata) do - { - described_class: described_class, - example_group: example_group, - line_number: line, - } - end - - let(:example_group) do - { - description: group_description, - parent_example_group: parent_group, - } - end - - let(:parent_group) do - { - description: control_group_name, - parent_example_group: nil, - } - end - - let(:line) { 27 } - - let(:control_data) do - { - name: example_description, - desc: full_description, - resource_type: resource_type, - resource_name: resource_name, - context: context, - line_number: line, - } - end - - shared_examples "built control" do - - before do - if described_class - allow(described_class).to receive(:instance_variable_get) - .with(:@name) - .and_return(resource_name) - allow(described_class.class).to receive(:name) - .and_return(described_class.class) - end - end - - it "returns the controls block name and example metadata for reporting" do - expect(events).to receive(:control_example_success) - .with(control_group_name, control_data) - audit_event_proxy.stop(notification) - end - end - - describe "a top-level example" do - # controls "port 111" do - # it "has nobody listening" do - # expect(port("111")).to_not be_listening - # end - # end - - # Description parts - let(:group_description) { "port 111" } - let(:example_description) { "has nobody listening" } - let(:full_description) { group_description + " " + example_description } - - # Metadata fields - let(:described_class) { nil } - - # Example group (metadata[:example_group]) fields - let(:parent_group) { nil } - - # Expected returns - let(:control_group_name) { group_description } - - # Control data fields - let(:resource_type) { nil } - let(:resource_name) { nil } - let(:context) { [] } - - include_examples "built control" - end - - describe "an example with an implicit subject" do - # controls "application ports" do - # control port(111) do - # it { is_expected.to_not be_listening } - # end - # end - - # Description parts - let(:control_group_name) { "application ports" } - let(:group_description) { "#{resource_type} #{resource_name}" } - let(:example_description) { "should not be listening" } - let(:full_description) do - [control_group_name, group_description, - example_description].join(" ") end - - # Metadata fields - let(:described_class) do - double("Serverspec::Type::Port", - class: "Serverspec::Type::Port", name: resource_name) end - - # Control data fields - let(:resource_type) { "Port" } - let(:resource_name) { "111" } - let(:context) { [] } - - include_examples "built control" - end - - describe "an example in a nested context" do - # controls "application ports" do - # control "port 111" do - # it "is not listening" do - # expect(port(111)).to_not be_listening - # end - # end - # end - - # Description parts - let(:control_group_name) { "application ports" } - let(:group_description) { "port 111" } - let(:example_description) { "is not listening" } - let(:full_description) do - [control_group_name, group_description, - example_description].join(" ") end - - # Metadata fields - let(:described_class) { nil } - - # Control data fields - let(:resource_type) { nil } - let(:resource_name) { nil } - let(:context) { [group_description] } - - include_examples "built control" - end - - describe "an example in a nested context including Serverspec" do - # controls "application directory" do - # control file("/tmp/audit") do - # describe file("/tmp/audit/test_file") do - # it "is a file" do - # expect(subject).to be_file - # end - # end - # end - # end - - # Description parts - let(:control_group_name) { "application directory" } - let(:outer_group_description) { "File \"tmp/audit\"" } - let(:group_description) { "#{resource_type} #{resource_name}" } - let(:example_description) { "is a file" } - let(:full_description) do - [control_group_name, outer_group_description, - group_description, example_description].join(" ") end - - # Metadata parts - let(:described_class) do - double("Serverspec::Type::File", - class: "Serverspec::Type::File", name: resource_name) end - - # Example group parts - let(:parent_group) do - { - description: outer_group_description, - parent_example_group: control_group, - } - end - - let(:control_group) do - { - description: control_group_name, - parent_example_group: nil, - } - end - - # Control data parts - let(:resource_type) { "File" } - let(:resource_name) { "/tmp/audit/test_file" } - let(:context) { [outer_group_description] } - - include_examples "built control" - end - end - end - -end diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb deleted file mode 100644 index 0a023babd0..0000000000 --- a/spec/unit/audit/audit_reporter_spec.rb +++ /dev/null @@ -1,435 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Author:: Claire McQuin (<claire@chef.io>) -# -# Copyright:: Copyright 2014-2018, 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 "spec_helper" - -describe Chef::Audit::AuditReporter do - - let(:rest) { double("rest") } - let(:reporter) { described_class.new(rest) } - let(:node) { double("node", name: "sofreshsoclean") } - let(:run_id) { 0 } - let(:start_time) { Time.new(2014, 12, 3, 9, 31, 05, "-08:00") } - let(:end_time) { Time.new(2014, 12, 3, 9, 36, 14, "-08:00") } - let(:run_status) do - instance_double(Chef::RunStatus, node: node, run_id: run_id, - start_time: start_time, end_time: end_time) end - - describe "#audit_phase_start" do - - it "notifies audit phase start to trace log" do - expect(Chef::Log).to receive(:trace).with(/Audit Reporter starting/) - reporter.audit_phase_start(run_status) - end - - it "initializes an AuditData object" do - expect(Chef::Audit::AuditData).to receive(:new).with(run_status.node.name, run_status.run_id) - reporter.audit_phase_start(run_status) - end - - it "saves the run status" do - reporter.audit_phase_start(run_status) - expect(reporter.instance_variable_get(:@run_status)).to eq run_status - end - end - - describe "#run_completed" do - - let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) } - let(:run_data) { audit_data.to_h } - - before do - allow(reporter).to receive(:auditing_enabled?).and_return(true) - allow(reporter).to receive(:run_status).and_return(run_status) - allow(rest).to receive(:post).and_return(true) - allow(reporter).to receive(:audit_data).and_return(audit_data) - allow(reporter).to receive(:run_status).and_return(run_status) - allow(audit_data).to receive(:to_h).and_return(run_data) - end - - describe "a successful run with auditing enabled" do - it "sets run start and end times" do - iso_start_time = "2014-12-03T17:31:05Z" - iso_end_time = "2014-12-03T17:36:14Z" - - reporter.run_completed(node) - expect(audit_data.start_time).to eq iso_start_time - expect(audit_data.end_time).to eq iso_end_time - end - - it "posts audit data to server endpoint" do - headers = { - "X-Ops-Audit-Report-Protocol-Version" => Chef::Audit::AuditReporter::PROTOCOL_VERSION, - } - - expect(rest).to receive(:post) - .with("controls", run_data, headers) - reporter.run_completed(node) - end - - context "when audit phase failed" do - - let(:audit_error) do - double("AuditError", class: "Chef::Exceptions::AuditError", - message: "Audit phase failed with error message: derpderpderp", - backtrace: ["/path/recipe.rb:57", "/path/library.rb:106"]) end - - before do - reporter.instance_variable_set(:@audit_phase_error, audit_error) - end - - it "reports an error" do - reporter.run_completed(node) - expect(run_data).to have_key(:error) - expect(run_data).to have_key(:error) - expect(run_data[:error]).to eq <<~EOM.strip! - Chef::Exceptions::AuditError: Audit phase failed with error message: derpderpderp - /path/recipe.rb:57 - /path/library.rb:106 - EOM - end - - end - - context "when unable to post to server" do - - let(:error) do - e = StandardError.new - e.set_backtrace(caller) - e - end - - before do - expect(rest).to receive(:post).and_raise(error) - allow(error).to receive(:respond_to?).and_call_original - end - - context "the error is an http error" do - - let(:response) { double("response", code: code) } - - before do - expect(Chef::Log).to receive(:trace).with(/Sending audit report/) - expect(Chef::Log).to receive(:trace).with(/Audit Report/) - allow(error).to receive(:response).and_return(response) - expect(error).to receive(:respond_to?).with(:response).and_return(true) - end - - context "when the code is 404" do - - let(:code) { "404" } - - it "logs that the server doesn't support audit reporting" do - expect(Chef::Log).to receive(:trace).with(/Server doesn't support audit reporting/) - reporter.run_completed(node) - end - end - - shared_examples "non-404 error code" do - - it "saves the error report" do - expect(Chef::FileCache).to receive(:store) - .with("failed-audit-data.json", an_instance_of(String), 0640) - .and_return(true) - expect(Chef::FileCache).to receive(:load) - .with("failed-audit-data.json", false) - .and_return(true) - expect(Chef::Log).to receive(:error).with(/Failed to post audit report to server/) - reporter.run_completed(node) - end - - end - - context "when the code is not 404" do - include_examples "non-404 error code" do - let(:code) { "505" } - end - end - - context "when there is no code" do - include_examples "non-404 error code" do - let(:code) { nil } - end - end - - end - - context "the error is not an http error" do - - it "logs the error" do - expect(error).to receive(:respond_to?).with(:response).and_return(false) - expect(Chef::Log).to receive(:error).with(/Failed to post audit report to server/) - reporter.run_completed(node) - end - - end - - context "when reporting url fatals are enabled" do - - before do - allow(Chef::Config).to receive(:[]) - .with(:enable_reporting_url_fatals) - .and_return(true) - end - - it "raises the error" do - expect(error).to receive(:respond_to?).with(:response).and_return(false) - allow(Chef::Log).to receive(:error).and_return(true) - expect(Chef::Log).to receive(:error).with(/Reporting fatals enabled. Aborting run./) - expect { reporter.run_completed(node) }.to raise_error(error) - end - - end - end - end - - context "when auditing is not enabled" do - - before do - allow(Chef::Log).to receive(:trace) - end - - it "doesn't send reports" do - expect(reporter).to receive(:auditing_enabled?).and_return(false) - expect(Chef::Log).to receive(:trace).with("Audit Reports are disabled. Skipping sending reports.") - reporter.run_completed(node) - end - - end - - context "when the run fails before audits" do - - before do - allow(Chef::Log).to receive(:trace) - end - - it "doesn't send reports" do - expect(reporter).to receive(:auditing_enabled?).and_return(true) - expect(reporter).to receive(:run_status).and_return(nil) - expect(Chef::Log).to receive(:trace).with("Run failed before audit mode was initialized, not sending audit report to server") - reporter.run_completed(node) - end - - end - end - - describe "#run_failed" do - - let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) } - let(:run_data) { audit_data.to_h } - - let(:audit_error) do - double("AuditError", class: "Chef::Exceptions::AuditError", - message: "Audit phase failed with error message: derpderpderp", - backtrace: ["/path/recipe.rb:57", "/path/library.rb:106"]) end - - let(:run_error) do - double("RunError", class: "Chef::Exceptions::RunError", - message: "This error shouldn't be reported.", - backtrace: ["fix it", "fix it", "fix it"]) end - - before do - allow(reporter).to receive(:auditing_enabled?).and_return(true) - allow(reporter).to receive(:run_status).and_return(run_status) - allow(reporter).to receive(:audit_data).and_return(audit_data) - allow(audit_data).to receive(:to_h).and_return(run_data) - end - - context "when no prior exception is stored" do - it "reports no error" do - expect(rest).to receive(:post) - reporter.run_failed(run_error) - expect(run_data).to_not have_key(:error) - end - end - - context "when some prior exception is stored" do - before do - reporter.instance_variable_set(:@audit_phase_error, audit_error) - end - - it "reports the prior error" do - expect(rest).to receive(:post) - reporter.run_failed(run_error) - expect(run_data).to have_key(:error) - expect(run_data[:error]).to eq <<~EOM.strip! - Chef::Exceptions::AuditError: Audit phase failed with error message: derpderpderp - /path/recipe.rb:57 - /path/library.rb:106 - EOM - end - end - end - - shared_context "audit data" do - - let(:control_group_foo) do - instance_double(Chef::Audit::ControlGroupData, - metadata: double("foo metadata")) end - let(:control_group_bar) do - instance_double(Chef::Audit::ControlGroupData, - metadata: double("bar metadata")) end - - let(:ordered_control_groups) do - { - "foo" => control_group_foo, - "bar" => control_group_bar, - } - end - - let(:audit_data) do - instance_double(Chef::Audit::AuditData, - add_control_group: true) end - - let(:run_context) do - instance_double(Chef::RunContext, - audits: ordered_control_groups) end - - before do - allow(reporter).to receive(:ordered_control_groups).and_return(ordered_control_groups) - allow(reporter).to receive(:audit_data).and_return(audit_data) - allow(reporter).to receive(:run_status).and_return(run_status) - allow(run_status).to receive(:run_context).and_return(run_context) - end - end - - describe "#audit_phase_complete" do - include_context "audit data" - - it "notifies audit phase finished to trace log" do - expect(Chef::Log).to receive(:trace).with(/Audit Reporter completed/) - reporter.audit_phase_complete("Output from audit mode") - end - - it "collects audit data" do - ordered_control_groups.each_value do |group| - expect(audit_data).to receive(:add_control_group).with(group) - end - reporter.audit_phase_complete("Output from audit mode") - end - end - - describe "#audit_phase_failed" do - include_context "audit data" - - let(:error) { double("Exception") } - - it "notifies audit phase failed to trace log" do - expect(Chef::Log).to receive(:trace).with(/Audit Reporter failed/) - reporter.audit_phase_failed(error, "Output from audit mode") - end - - it "collects audit data" do - ordered_control_groups.each_value do |group| - expect(audit_data).to receive(:add_control_group).with(group) - end - reporter.audit_phase_failed(error, "Output from audit mode") - end - end - - describe "#control_group_started" do - include_context "audit data" - - let(:name) { "bat" } - let(:control_group) do - instance_double(Chef::Audit::ControlGroupData, - metadata: double("metadata")) end - - before do - allow(Chef::Audit::ControlGroupData).to receive(:new) - .with(name, control_group.metadata) - .and_return(control_group) - end - - it "stores the control group" do - expect(ordered_control_groups).to receive(:key?).with(name).and_return(false) - allow(run_context.audits).to receive(:[]).with(name).and_return(control_group) - expect(ordered_control_groups).to receive(:store) - .with(name, control_group) - .and_call_original - reporter.control_group_started(name) - expect(ordered_control_groups[name]).to eq control_group - end - - context "when a control group with the same name has been seen" do - it "raises an exception" do - expect(ordered_control_groups).to receive(:key?).with(name).and_return(true) - expect { reporter.control_group_started(name) }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate) - end - end - end - - describe "#control_example_success" do - include_context "audit data" - - let(:name) { "foo" } - let(:example_data) { double("example data") } - - it "notifies the control group the example succeeded" do - expect(control_group_foo).to receive(:example_success).with(example_data) - reporter.control_example_success(name, example_data) - end - end - - describe "#control_example_failure" do - include_context "audit data" - - let(:name) { "bar" } - let(:example_data) { double("example data") } - let(:error) { double("Exception", message: "oopsie") } - - it "notifies the control group the example failed" do - expect(control_group_bar).to receive(:example_failure) - .with(example_data, error.message) - reporter.control_example_failure(name, example_data, error) - end - end - - describe "#auditing_enabled?" do - shared_examples "enabled?" do |true_or_false| - - it "returns #{true_or_false}" do - expect(Chef::Config).to receive(:[]) - .with(:audit_mode) - .and_return(audit_setting) - expect(reporter.auditing_enabled?).to be true_or_false - end - end - - context "when auditing is disabled" do - include_examples "enabled?", false do - let(:audit_setting) { :disabled } - end - end - - context "when auditing in audit-only mode" do - include_examples "enabled?", true do - let(:audit_setting) { :audit_only } - end - end - - context "when auditing is enabled" do - include_examples "enabled?", true do - let(:audit_setting) { :enabled } - end - end - end - -end diff --git a/spec/unit/audit/control_group_data_spec.rb b/spec/unit/audit/control_group_data_spec.rb deleted file mode 100644 index e8af8be90d..0000000000 --- a/spec/unit/audit/control_group_data_spec.rb +++ /dev/null @@ -1,482 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Author:: Claire McQuin (<claire@chef.io>) -# -# Copyright:: Copyright 2014-2018, 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 "spec_helper" -require "securerandom" - -describe Chef::Audit::AuditData do - - let(:node_name) { "noodles" } - let(:run_id) { SecureRandom.uuid } - let(:audit_data) { described_class.new(node_name, run_id) } - - let(:control_group_1) { double("control group 1") } - let(:control_group_2) { double("control group 2") } - - describe "#add_control_group" do - context "when no control groups have been added" do - it "stores the control group" do - audit_data.add_control_group(control_group_1) - expect(audit_data.control_groups).to include(control_group_1) - end - - end - - context "when adding additional control groups" do - - before do - audit_data.add_control_group(control_group_1) - end - - it "stores the control group" do - audit_data.add_control_group(control_group_2) - expect(audit_data.control_groups).to include(control_group_2) - end - - it "stores all control groups" do - audit_data.add_control_group(control_group_2) - expect(audit_data.control_groups).to include(control_group_1) - end - end - end - - describe "#to_h" do - - let(:audit_data_hash) { audit_data.to_h } - - it "returns a hash" do - expect(audit_data_hash).to be_a(Hash) - end - - it "describes a Chef::Audit::AuditData object" do - keys = [:node_name, :run_id, :start_time, :end_time, :control_groups] - expect(audit_data_hash.keys).to match_array(keys) - end - - describe ":control_groups" do - - let(:control_hash_1) { { name: "control group 1" } } - let(:control_hash_2) { { name: "control group 2" } } - - let(:control_groups) { audit_data_hash[:control_groups] } - - context "with no control groups added" do - it "is an empty list" do - expect(control_groups).to eq [] - end - end - - context "with one control group added" do - - before do - allow(audit_data).to receive(:control_groups).and_return([control_group_1]) - end - - it "is a one-element list containing the control group hash" do - expect(control_group_1).to receive(:to_h).once.and_return(control_hash_1) - expect(control_groups.size).to eq 1 - expect(control_groups).to include(control_hash_1) - end - end - - context "with multiple control groups added" do - - before do - allow(audit_data).to receive(:control_groups).and_return([control_group_1, control_group_2]) - end - - it "is a list of control group hashes" do - expect(control_group_1).to receive(:to_h).and_return(control_hash_1) - expect(control_group_2).to receive(:to_h).and_return(control_hash_2) - expect(control_groups.size).to eq 2 - expect(control_groups).to include(control_hash_1) - expect(control_groups).to include(control_hash_2) - end - end - end - end -end - -describe Chef::Audit::ControlData do - - let(:name) { "ramen" } - let(:resource_type) { double("Service") } - let(:resource_name) { "mysql" } - let(:context) { nil } - let(:line_number) { 27 } - - let(:control_data) do - described_class.new(name: name, - resource_type: resource_type, resource_name: resource_name, - context: context, line_number: line_number) end - - describe "#to_h" do - - let(:control_data_hash) { control_data.to_h } - - it "returns a hash" do - expect(control_data_hash).to be_a(Hash) - end - - it "describes a Chef::Audit::ControlData object" do - keys = [:name, :resource_type, :resource_name, :context, :status, :details] - expect(control_data_hash.keys).to match_array(keys) - end - - context "when context is nil" do - - it "sets :context to an empty array" do - expect(control_data_hash[:context]).to eq [] - end - - end - - context "when context is non-nil" do - - let(:context) { ["outer"] } - - it "sets :context to its value" do - expect(control_data_hash[:context]).to eq context - end - end - end -end - -describe Chef::Audit::ControlGroupData do - - let(:name) { "balloon" } - let(:control_group_data) { described_class.new(name) } - - shared_context "control data" do - - let(:name) { "" } - let(:resource_type) { nil } - let(:resource_name) { nil } - let(:context) { nil } - let(:line_number) { 0 } - - let(:control_data) do - { - name: name, - resource_type: resource_type, - resource_name: resource_name, - context: context, - line_number: line_number, - } - end - - end - - shared_context "control" do - include_context "control data" - - let(:control) do - Chef::Audit::ControlData.new(name: name, - resource_type: resource_type, resource_name: resource_name, - context: context, line_number: line_number) end - - before do - allow(Chef::Audit::ControlData).to receive(:new) - .with(name: name, resource_type: resource_type, - resource_name: resource_name, context: context, - line_number: line_number) - .and_return(control) - end - end - - describe "#new" do - it "has status \"success\"" do - expect(control_group_data.status).to eq "success" - end - end - - describe "#example_success" do - include_context "control" - - def notify_success - control_group_data.example_success(control_data) - end - - it "increments the number of successful audits" do - num_success = control_group_data.number_succeeded - notify_success - expect(control_group_data.number_succeeded).to eq (num_success + 1) - end - - it "does not increment the number of failed audits" do - num_failed = control_group_data.number_failed - notify_success - expect(control_group_data.number_failed).to eq (num_failed) - end - - it "marks the audit's status as success" do - notify_success - expect(control.status).to eq "success" - end - - it "does not modify its own status" do - expect(control_group_data).to_not receive(:status=) - status = control_group_data.status - notify_success - expect(control_group_data.status).to eq status - end - - it "saves the control" do - controls = control_group_data.controls - expect(controls).to_not include(control) - notify_success - expect(controls).to include(control) - end - end - - describe "#example_failure" do - include_context "control" - - let(:details) { "poop" } - - def notify_failure - control_group_data.example_failure(control_data, details) - end - - it "does not increment the number of successful audits" do - num_success = control_group_data.number_succeeded - notify_failure - expect(control_group_data.number_succeeded).to eq num_success - end - - it "increments the number of failed audits" do - num_failed = control_group_data.number_failed - notify_failure - expect(control_group_data.number_failed).to eq (num_failed + 1) - end - - it "marks the audit's status as failure" do - notify_failure - expect(control.status).to eq "failure" - end - - it "marks its own status as failure" do - notify_failure - expect(control_group_data.status).to eq "failure" - end - - it "saves the control" do - controls = control_group_data.controls - expect(controls).to_not include(control) - notify_failure - expect(controls).to include(control) - end - - context "when details are not provided" do - - let(:details) { nil } - - it "does not save details to the control" do - default_details = control.details - expect(control).to_not receive(:details=) - notify_failure - expect(control.details).to eq default_details - end - end - - context "when details are provided" do - - let(:details) { "yep that didn't work" } - - it "saves details to the control" do - notify_failure - expect(control.details).to eq details - end - end - end - - shared_examples "multiple audits" do |success_or_failure| - include_context "control" - - let(:num_success) { 0 } - let(:num_failure) { 0 } - - before do - if num_failure == 0 - num_success.times { control_group_data.example_success(control_data) } - elsif num_success == 0 - num_failure.times { control_group_data.example_failure(control_data, nil) } - end - end - - it "counts the number of successful audits" do - expect(control_group_data.number_succeeded).to eq num_success - end - - it "counts the number of failed audits" do - expect(control_group_data.number_failed).to eq num_failure - end - - it "marks its status as \"#{success_or_failure}\"" do - expect(control_group_data.status).to eq success_or_failure - end - end - - context "when all audits pass" do - include_examples "multiple audits", "success" do - let(:num_success) { 3 } - end - end - - context "when one audit fails" do - shared_examples "mixed audit results" do - include_examples "multiple audits", "failure" do - - let(:audit_results) { [] } - let(:num_success) { audit_results.count("success") } - let(:num_failure) { 1 } - - before do - audit_results.each do |result| - if result == "success" - control_group_data.example_success(control_data) - else - control_group_data.example_failure(control_data, nil) - end - end - end - end - end - - context "and it's the first audit" do - include_examples "mixed audit results" do - let(:audit_results) { %w{failure success success} } - end - end - - context "and it's an audit in the middle" do - include_examples "mixed audit results" do - let(:audit_results) { %w{success failure success} } - end - end - - context "and it's the last audit" do - include_examples "mixed audit results" do - let(:audit_results) { %w{success success failure} } - end - end - end - - context "when all audits fail" do - include_examples "multiple audits", "failure" do - let(:num_failure) { 3 } - end - end - - describe "#to_h" do - - let(:control_group_data_hash) { control_group_data.to_h } - - it "returns a hash" do - expect(control_group_data_hash).to be_a(Hash) - end - - it "describes a Chef::Audit::ControlGroupData object" do - keys = [:name, :status, :number_succeeded, :number_failed, - :controls, :id] - expect(control_group_data_hash.keys).to match_array(keys) - end - - describe ":controls" do - - let(:control_group_controls) { control_group_data_hash[:controls] } - - context "with no controls added" do - it "is an empty list" do - expect(control_group_controls).to eq [] - end - end - - context "with one control added" do - include_context "control" - - let(:control_list) { [control_data] } - let(:control_hash) { control.to_h } - - before do - expect(control_group_data).to receive(:controls).twice.and_return(control_list) - expect(control_data).to receive(:to_h).and_return(control_hash) - end - - it "is a one-element list containing the control hash" do - expect(control_group_controls.size).to eq 1 - expect(control_group_controls).to include(control_hash) - end - - it "adds a sequence number to the control" do - control_group_data.to_h - expect(control_hash).to have_key(:sequence_number) - end - - end - - context "with multiple controls added" do - - let(:control_hash_1) { { line_number: 27 } } - let(:control_hash_2) { { line_number: 13 } } - let(:control_hash_3) { { line_number: 35 } } - - let(:control_1) do - double("control 1", - line_number: control_hash_1[:line_number], - to_h: control_hash_1) end - let(:control_2) do - double("control 2", - line_number: control_hash_2[:line_number], - to_h: control_hash_2) end - let(:control_3) do - double("control 3", - line_number: control_hash_3[:line_number], - to_h: control_hash_3) end - - let(:control_list) { [control_1, control_2, control_3] } - let(:ordered_control_hashes) { [control_hash_2, control_hash_1, control_hash_3] } - - before do - # Another way to do this would be to call #example_success - # or #example_failure per control hash, but we'd have to - # then stub #create_control and it's a lot of extra stubbing work. - # We can't stub the controls reader to return a list of - # controls because of the call to sort! and the following - # reading of controls. - control_group_data.instance_variable_set(:@controls, control_list) - end - - it "is a list of control group hashes ordered by line number" do - expect(control_group_controls.size).to eq 3 - expect(control_group_controls).to eq ordered_control_hashes - end - - it "assigns sequence numbers in order" do - control_group_data.to_h - ordered_control_hashes.each_with_index do |control_hash, idx| - expect(control_hash[:sequence_number]).to eq idx + 1 - end - end - end - end - end - -end diff --git a/spec/unit/audit/logger_spec.rb b/spec/unit/audit/logger_spec.rb deleted file mode 100644 index 51a32d906e..0000000000 --- a/spec/unit/audit/logger_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright:: Copyright 2014-2016, 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 "spec_helper" - -describe Chef::Audit::Logger do - - before(:each) do - Chef::Audit::Logger.instance_variable_set(:@buffer, nil) - end - - it "calling puts creates @buffer and adds the message" do - Chef::Audit::Logger.puts("Output message") - expect(Chef::Audit::Logger.read_buffer).to eq("Output message\n") - end - - it "calling puts multiple times adds to the message" do - Chef::Audit::Logger.puts("Output message") - Chef::Audit::Logger.puts("Output message") - Chef::Audit::Logger.puts("Output message") - expect(Chef::Audit::Logger.read_buffer).to eq("Output message\nOutput message\nOutput message\n") - end - - it "calling it before @buffer is set returns an empty string" do - expect(Chef::Audit::Logger.read_buffer).to eq("") - end - -end diff --git a/spec/unit/audit/rspec_formatter_spec.rb b/spec/unit/audit/rspec_formatter_spec.rb deleted file mode 100644 index 8c266fbb8b..0000000000 --- a/spec/unit/audit/rspec_formatter_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Author:: Claire McQuin (<claire@chef.io>) -# -# Copyright:: Copyright 2014-2016, 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 "spec_helper" -require "chef/audit/rspec_formatter" - -describe Chef::Audit::RspecFormatter do - let(:formatter) { Chef::Audit::RspecFormatter.new(nil) } - it "should respond to close" do - expect(formatter).to respond_to(:close) - end -end diff --git a/spec/unit/audit/runner_spec.rb b/spec/unit/audit/runner_spec.rb deleted file mode 100644 index 902ede62ed..0000000000 --- a/spec/unit/audit/runner_spec.rb +++ /dev/null @@ -1,144 +0,0 @@ -# -# Author:: Tyler Ball (<tball@chef.io>) -# Copyright:: Copyright 2014-2016, 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 "spec_helper" -require "rspec/core/sandbox" -require "chef/audit/runner" -require "chef/audit/audit_event_proxy" -require "chef/audit/rspec_formatter" -require "rspec/support/spec/in_sub_process" -require "rspec/support/spec/stderr_splitter" - -describe Chef::Audit::Runner do - include RSpec::Support::InSubProcess - - let(:events) { double("events") } - let(:run_context) { instance_double(Chef::RunContext, events: events) } - let(:runner) { Chef::Audit::Runner.new(run_context) } - - around(:each) do |ex| - RSpec::Core::Sandbox.sandboxed { ex.run } - end - - context "when we run in audit mode" do - let(:paths) { [ "/opt/chef/lib/chef/", 'C:\windows/here/lib/chef/' , "/opt/chef/extra/folders/lib/chef/"] } - it "excludes the current path from backtrace" do - paths.each do |path| - expect(runner.exclusion_pattern).to match(path) - end - end - end - - describe "#initialize" do - it "correctly sets the run_context during initialization" do - expect(runner.instance_variable_get(:@run_context)).to eq(run_context) - end - end - - context "during #run" do - - describe "#setup" do - let(:log_location) { File.join(Dir.tmpdir, "audit_log") } - let(:color) { false } - - before do - Chef::Config[:log_location] = log_location - Chef::Config[:color] = color - end - - it "sets all the config values" do - # This runs the Serverspec includes - we don't want these hanging around in all subsequent tests so - # we run this in a forked process. Keeps Serverspec files from getting loaded into main process. - in_sub_process do - runner.send(:setup) - - expect(RSpec.configuration.output_stream).to eq(Chef::Audit::Logger) - expect(RSpec.configuration.error_stream).to eq(Chef::Audit::Logger) - - expect(RSpec.configuration.formatters.size).to eq(2) - expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::AuditEventProxy)) - expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::RspecFormatter)) - expect(Chef::Audit::AuditEventProxy.class_variable_get(:@@events)).to eq(run_context.events) - - expect(RSpec.configuration.expectation_frameworks).to eq([RSpec::Matchers]) - expect(RSpec::Matchers.configuration.syntax).to eq([:expect]) - - expect(RSpec.configuration.color).to eq(color) - expect(RSpec.configuration.expose_dsl_globally?).to eq(false) - expect(RSpec.configuration.backtrace_exclusion_patterns).to include(runner.exclusion_pattern) - - expect(Specinfra.configuration.backend).to eq(:exec) - end - end - end - - describe "#register_control_groups" do - let(:audits) { [] } - let(:run_context) { instance_double(Chef::RunContext, audits: audits) } - - it "adds the control group aliases" do - runner.send(:register_control_groups) - - expect(RSpec::Core::DSL.example_group_aliases).to include(:__control_group__) - expect(RSpec::Core::DSL.example_group_aliases).to include(:control) - end - - context "audits exist" do - let(:audits) { { "audit_name" => group } } - let(:group) { Struct.new(:args, :block).new(["group_name"], nil) } - - it "sends the audits to the world" do - runner.send(:register_control_groups) - - expect(RSpec.world.example_groups.size).to eq(1) - # For whatever reason, `kind_of` is not working - # expect(RSpec.world.example_groups).to include(kind_of(RSpec::Core::ExampleGroup)) => FAIL - g = RSpec.world.example_groups[0] - expect(g.ancestors).to include(RSpec::Core::ExampleGroup) - expect(g.description).to eq("group_name") - end - end - end - - describe "#do_run" do - let(:rspec_runner) { instance_double(RSpec::Core::Runner) } - - it "executes the runner" do - expect(RSpec::Core::Runner).to receive(:new).with(nil).and_return(rspec_runner) - expect(rspec_runner).to receive(:run_specs).with([]) - - runner.send(:do_run) - end - end - end - - describe "counters" do - it "correctly calculates failed?" do - expect(runner.failed?).to eq(false) - end - - it "correctly calculates num_failed" do - expect(runner.num_failed).to eq(0) - end - - it "correctly calculates num_total" do - expect(runner.num_total).to eq(0) - end - end - -end diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb index 370d5d34e7..41091f5716 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -108,8 +108,7 @@ shared_context "a client run" do let(:http_node_save) { double("Chef::ServerAPI (node save)") } let(:reporting_rest_client) { double("Chef::ServerAPI (reporting client)") } - let(:runner) { instance_double("Chef::Runner") } - let(:audit_runner) { instance_double("Chef::Audit::Runner", failed?: false) } + let(:runner) { instance_double("Chef::Runner") } def stub_for_register # --Client.register @@ -131,7 +130,6 @@ shared_context "a client run" do expect(client.events).to receive(:register).with(instance_of(Chef::DataCollector::Reporter)) expect(client.events).to receive(:register).with(instance_of(Chef::ResourceReporter)) expect(client.events).to receive(:register).with(instance_of(Chef::ActionCollection)) - expect(client.events).to receive(:register).with(instance_of(Chef::Audit::AuditReporter)) end def stub_for_node_load @@ -174,10 +172,6 @@ shared_context "a client run" do # define me end - def stub_for_audit - # define me - end - def stub_for_node_save # define me end @@ -190,7 +184,6 @@ shared_context "a client run" do Chef::Config[:client_fork] = enable_fork Chef::Config[:cache_path] = windows? ? 'C:\chef' : "/var/chef" Chef::Config[:why_run] = false - Chef::Config[:audit_mode] = :enabled Chef::Config[:chef_guid] = "default-guid" stub_rest_clean @@ -200,7 +193,6 @@ shared_context "a client run" do stub_for_sync_cookbooks stub_for_required_recipe stub_for_converge - stub_for_audit stub_for_node_save expect_any_instance_of(Chef::RunLock).to receive(:acquire) @@ -248,52 +240,6 @@ shared_context "converge failed" do end end -shared_context "audit phase completed" do - def stub_for_audit - # -- Client#run_audits - expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) - expect(audit_runner).to receive(:run).and_return(true) - expect(client.events).to receive(:audit_phase_complete) - end -end - -shared_context "audit phase failed with error" do - let(:audit_error) do - err = RuntimeError.new("Unexpected audit error") - err.set_backtrace([ "/path/recipe.rb:57", "/path/recipe.rb:55" ]) - err - end - - def stub_for_audit - expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) - expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!") - expect(audit_runner).to receive(:run).and_raise(audit_error) - expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!") - end -end - -shared_context "audit phase completed with failed controls" do - let(:audit_runner) do - instance_double("Chef::Audit::Runner", failed?: true, - num_failed: 1, num_total: 3) end - - let(:audit_error) do - err = Chef::Exceptions::AuditsFailed.new(audit_runner.num_failed, audit_runner.num_total) - err.set_backtrace([ "/path/recipe.rb:108", "/path/recipe.rb:103" ]) - err - end - - def stub_for_audit - expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner) - expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!") - expect(audit_runner).to receive(:run) - expect(Chef::Exceptions::AuditsFailed).to receive(:new).with( - audit_runner.num_failed, audit_runner.num_total - ).and_return(audit_error) - expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!") - end -end - shared_context "run completed" do def stub_for_run expect(client).to receive(:run_completed_successfully) @@ -318,7 +264,7 @@ end shared_examples "a completed run" do include_context "run completed" - it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges, and runs audits" do + it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges" do # This is what we're testing. expect(client.run).to be true @@ -328,28 +274,6 @@ shared_examples "a completed run" do end end -shared_examples "a completed run with audit failure" do - include_context "run completed" - - before do - expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError) - end - - it "converges, runs audits, saves the node and raises the error in a wrapping error" do - expect { client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| - expect(error.wrapped_errors.size).to eq(run_errors.size) - run_errors.each do |run_error| - expect(error.wrapped_errors).to include(run_error) - expect(error.backtrace).to include(*run_error.backtrace) - end - end - - # fork is stubbed, so we can see the outcome of the run - expect(node.automatic_attrs[:platform]).to eq(platform) - expect(node.automatic_attrs[:platform_version]).to eq(platform_version) - end -end - shared_examples "a failed run" do include_context "run failed" @@ -497,7 +421,6 @@ describe Chef::Client do shared_examples_for "a successful client run" do include_context "a client run" include_context "converge completed" - include_context "audit phase completed" include_examples "a completed run" end @@ -577,52 +500,11 @@ describe Chef::Client do describe "when converge completes successfully" do include_context "a client run" include_context "converge completed" - context "when audit mode is enabled" do - describe "when audit phase errors" do - include_context "audit phase failed with error" - include_examples "a completed run with audit failure" do - let(:run_errors) { [audit_error] } - end - end - - describe "when audit phase completed" do - include_context "audit phase completed" - include_examples "a completed run" - end - - describe "when audit phase completed with failed controls" do - include_context "audit phase completed with failed controls" - include_examples "a completed run with audit failure" do - let(:run_errors) { [audit_error] } - end - end - end end describe "when converge errors" do include_context "a client run" include_context "converge failed" - - describe "when audit phase errors" do - include_context "audit phase failed with error" - include_examples "a failed run" do - let(:run_errors) { [converge_error, audit_error] } - end - end - - describe "when audit phase completed" do - include_context "audit phase completed" - include_examples "a failed run" do - let(:run_errors) { [converge_error] } - end - end - - describe "when audit phase completed with failed controls" do - include_context "audit phase completed with failed controls" - include_examples "a failed run" do - let(:run_errors) { [converge_error, audit_error] } - end - end end end @@ -903,28 +785,5 @@ describe Chef::Client do # fail on the first thing in begin block allow_any_instance_of(Chef::RunLock).to receive(:save_pid).and_raise(NoMethodError) end - - context "when audit mode is enabled" do - before do - Chef::Config[:audit_mode] = :enabled - end - it "should run exception handlers on early fail" do - expect(subject).to receive(:run_failed) - expect { subject.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error| - expect(error.wrapped_errors.size).to eq 1 - expect(error.wrapped_errors).to include(NoMethodError) - end - end - end - - context "when audit mode is disabled" do - before do - Chef::Config[:audit_mode] = :disabled - end - it "should run exception handlers on early fail" do - expect(subject).to receive(:run_failed) - expect { subject.run }.to raise_error(NoMethodError) - end - end end end diff --git a/spec/unit/dsl/audit_spec.rb b/spec/unit/dsl/audit_spec.rb deleted file mode 100644 index e24fbc4589..0000000000 --- a/spec/unit/dsl/audit_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ - -require "spec_helper" -require "chef/dsl/audit" - -class AuditDSLTester < Chef::Recipe - include Chef::DSL::Audit -end - -class BadAuditDSLTester - include Chef::DSL::Audit -end - -describe Chef::DSL::Audit do - let(:auditor) { AuditDSLTester.new("cookbook_name", "recipe_name", run_context) } - let(:run_context) { instance_double(Chef::RunContext, audits: audits, cookbook_collection: cookbook_collection) } - let(:audits) { {} } - let(:cookbook_collection) { {} } - - it "raises an error when a block of audits is not provided" do - expect { auditor.control_group "name" }.to raise_error(Chef::Exceptions::NoAuditsProvided) - end - - it "raises an error when no audit name is given" do - expect { auditor.control_group {} }.to raise_error(Chef::Exceptions::AuditNameMissing) - end - - context "audits already populated" do - let(:audits) { { "unique" => {} } } - - it "raises an error if the audit name is a duplicate" do - expect { auditor.control_group("unique") {} }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate) - end - end - - context "included in a class without recipe DSL" do - let(:auditor) { BadAuditDSLTester.new } - - it "fails because it relies on the recipe DSL existing" do - expect { auditor.control_group("unique") {} }.to raise_error(NoMethodError, /undefined method `cookbook_name'/) - end - end - -end diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb index 2d5bb81c95..1bc84745ad 100644 --- a/spec/unit/recipe_spec.rb +++ b/spec/unit/recipe_spec.rb @@ -577,11 +577,6 @@ describe Chef::Recipe do end describe "included DSL" do - it "should include features from Chef::DSL::Audit" do - expect(recipe.singleton_class.included_modules).to include(Chef::DSL::Audit) - expect(recipe.respond_to?(:control_group)).to be true - end - it "should respond to :ps_credential from Chef::DSL::Powershell" do expect(recipe.respond_to?(:ps_credential)).to be true end diff --git a/spec/unit/run_context/child_run_context_spec.rb b/spec/unit/run_context/child_run_context_spec.rb index be45aaa1cc..a5a4aea4a1 100644 --- a/spec/unit/run_context/child_run_context_spec.rb +++ b/spec/unit/run_context/child_run_context_spec.rb @@ -44,13 +44,6 @@ describe Chef::RunContext::ChildRunContext do expect(child.parent_run_context).to eq run_context end - it "audits is not the same as the parent" do - expect(child.audits.object_id).not_to eq run_context.audits.object_id - child.audits["hi"] = "lo" - expect(child.audits["hi"]).to eq("lo") - expect(run_context.audits["hi"]).not_to eq("lo") - end - it "resource_collection is not the same as the parent" do expect(child.resource_collection.object_id).not_to eq run_context.resource_collection.object_id f = Chef::Resource::File.new("hi", child) |