From 8ab9066cc6e728587f9b21ddca4c8b2741df6c65 Mon Sep 17 00:00:00 2001 From: John Keiser Date: Thu, 19 Mar 2015 21:29:02 -0700 Subject: [PATCH 1/8] Pass hash options through to with_chef_server correctly --- lib/cheffish/rspec/chef_run_support.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cheffish/rspec/chef_run_support.rb b/lib/cheffish/rspec/chef_run_support.rb index c5a6637..5595b21 100644 --- a/lib/cheffish/rspec/chef_run_support.rb +++ b/lib/cheffish/rspec/chef_run_support.rb @@ -9,9 +9,9 @@ module RSpec module ChefRunSupport include ChefZero::RSpec - def when_the_chef_12_server(*args, &block) + def when_the_chef_12_server(*args, **options, &block) if Gem::Version.new(ChefZero::VERSION) >= Gem::Version.new('3.1') - when_the_chef_server(*args, :osc_compat => false, :single_org => false, &block) + when_the_chef_server(*args, :osc_compat => false, :single_org => false, **options, &block) end end From 4e9b3b781dc522669b2c7f1615348398db09db5b Mon Sep 17 00:00:00 2001 From: John Keiser Date: Tue, 24 Mar 2015 08:25:59 -0700 Subject: [PATCH 2/8] Add Chef output/logs to failures, allow individually wrapped chef runs --- lib/cheffish/basic_chef_client.rb | 99 ++++++++++++++--- lib/cheffish/rspec/chef_run_support.rb | 130 ++++++++++++----------- lib/cheffish/rspec/chef_run_wrapper.rb | 105 ++++++++++++++++++ lib/cheffish/rspec/matchers.rb | 14 +++ lib/cheffish/rspec/recipe_run_wrapper.rb | 22 ++++ 5 files changed, 293 insertions(+), 77 deletions(-) create mode 100644 lib/cheffish/rspec/chef_run_wrapper.rb create mode 100644 lib/cheffish/rspec/recipe_run_wrapper.rb diff --git a/lib/cheffish/basic_chef_client.rb b/lib/cheffish/basic_chef_client.rb index 9269d49..eb3e9af 100644 --- a/lib/cheffish/basic_chef_client.rb +++ b/lib/cheffish/basic_chef_client.rb @@ -13,7 +13,7 @@ module Cheffish class BasicChefClient include Chef::DSL::Recipe - def initialize(node = nil, events = nil) + def initialize(node = nil, events = nil, **chef_config) if !node node = Chef::Node.new node.name 'basic_chef_client' @@ -21,34 +21,53 @@ def initialize(node = nil, events = nil) node.automatic[:platform_version] = Cheffish::VERSION end - @event_catcher = BasicChefClientEvents.new - dispatcher = Chef::EventDispatch::Dispatcher.new(@event_catcher) - dispatcher.register(events) if events - @run_context = Chef::RunContext.new(node, {}, dispatcher) - @updated = [] - @cookbook_name = 'basic_chef_client' + # Decide on the config we want for this chef client + @chef_config = chef_config + + with_chef_config do + @cookbook_name = 'basic_chef_client' + @event_catcher = BasicChefClientEvents.new + dispatcher = Chef::EventDispatch::Dispatcher.new(@event_catcher) + case events + when nil + when Array + events.each { |e| dispatcher.register(e) } if events + else + dispatcher.register(events) + end + @run_context = Chef::RunContext.new(node, {}, dispatcher) + @updated = [] + @cookbook_name = 'basic_chef_client' + end end extend Forwardable # Stuff recipes need + attr_reader :chef_config attr_reader :run_context attr_accessor :cookbook_name attr_accessor :recipe_name def_delegators :@run_context, :resource_collection, :immediate_notifications, :delayed_notifications def add_resource(resource) - resource.run_context = run_context - run_context.resource_collection.insert(resource) + with_chef_config do + resource.run_context = run_context + run_context.resource_collection.insert(resource) + end end def load_block(&block) - @recipe_name = 'block' - instance_eval(&block) + with_chef_config do + @recipe_name = 'block' + instance_eval(&block) + end end def converge - Chef::Runner.new(self).converge + with_chef_config do + Chef::Runner.new(self).converge + end end def updates @@ -63,15 +82,21 @@ def updated? # add_resource() method. def self.build_resource(type, name, created_at=nil, &resource_attrs_block) created_at ||= caller[0] - result = BasicChefClient.new.build_resource(type, name, created_at, &resource_attrs_block) + result = BasicChefClient.new.tap do |client| + client.with_chef_config do + client.build_resource(type, name, created_at, &resource_attrs_block) + end + end result end def self.inline_resource(provider, provider_action, *resources, &block) events = ProviderEventForwarder.new(provider, provider_action) client = BasicChefClient.new(provider.node, events) - resources.each do |resource| - client.add_resource(resource) + client.with_chef_config do + resources.each do |resource| + client.add_resource(resource) + end end client.load_block(&block) if block client.converge @@ -85,6 +110,50 @@ def self.converge_block(node = nil, events = nil, &block) client.updated? end + def with_chef_config(&block) + old_chef_config = Chef::Config.save + if chef_config[:log_location] + old_loggers = Chef::Log.loggers + Chef::Log.init(chef_config[:log_location]) + end + if chef_config[:log_level] + old_level = Chef::Log.level + Chef::Log.level(chef_config[:log_level]) + end + # if chef_config[:stdout] + # old_stdout = $stdout + # $stdout = chef_config[:stdout] + # end + # if chef_config[:stderr] + # old_stderr = $stderr + # $stderr = chef_config[:stderr] + # end + begin + deep_merge_config(chef_config, Chef::Config) + block.call + ensure + # $stdout = old_stdout if chef_config[:stdout] + # $stderr = old_stderr if chef_config[:stderr] + if old_loggers + Chef::Log.logger = old_loggers.shift + old_loggers.each { |l| Chef::Log.loggers.push(l) } + elsif chef_config[:log_level] + Chef::Log.level = old_level + end + Chef::Config.restore(old_chef_config) + end + end + + def deep_merge_config(src, dest) + src.each do |name, value| + if value.is_a?(Hash) && dest[name].is_a?(Hash) + deep_merge_config(value, dest[name]) + else + dest[name] = value + end + end + end + class BasicChefClientEvents < Chef::EventDispatch::Base def initialize @updates = [] diff --git a/lib/cheffish/rspec/chef_run_support.rb b/lib/cheffish/rspec/chef_run_support.rb index 5595b21..8a95434 100644 --- a/lib/cheffish/rspec/chef_run_support.rb +++ b/lib/cheffish/rspec/chef_run_support.rb @@ -3,70 +3,24 @@ require 'cheffish/rspec/repository_support' require 'uri' require 'cheffish/basic_chef_client' +require 'cheffish/rspec/chef_run_wrapper' +require 'cheffish/rspec/recipe_run_wrapper' module Cheffish module RSpec module ChefRunSupport include ChefZero::RSpec - - def when_the_chef_12_server(*args, **options, &block) - if Gem::Version.new(ChefZero::VERSION) >= Gem::Version.new('3.1') - when_the_chef_server(*args, :osc_compat => false, :single_org => false, **options, &block) - end - end + include RepositorySupport def self.extended(klass) klass.class_eval do - extend RepositorySupport - - def rest - ::Chef::ServerAPI.new - end - - def get(path, *args) - if path[0] == '/' - path = URI.join(rest.url, path) - end - rest.get(path, *args) - end - - def chef_run - converge if !@converged - event_sink.events - end - - def event_sink - @event_sink ||= EventSink.new - end - - def basic_chef_client - @basic_chef_client ||= begin - ::Cheffish::BasicChefClient.new(nil, event_sink) - end - end - - def load_recipe(&block) - basic_chef_client.load_block(&block) - end - - def run_recipe(&block) - load_recipe(&block) - converge - end - - def reset_chef_client - @event_sink = nil - @basic_chef_client = nil - @converged = false - end + include ChefRunSupportInstanceMethods + end + end - def converge - if @converged - raise "Already converged! Cannot converge twice, that's bad mojo." - end - @converged = true - basic_chef_client.converge - end + def when_the_chef_12_server(*args, **options, &block) + if Gem::Version.new(ChefZero::VERSION) >= Gem::Version.new('3.1') + when_the_chef_server(*args, :osc_compat => false, :single_org => false, **options, &block) end end @@ -89,18 +43,70 @@ def with_converge(&block) end end - class EventSink - def initialize - @events = [] + module ChefRunSupportInstanceMethods + def rest + ::Chef::ServerAPI.new end - attr_reader :events + def get(path, *args) + if path[0] == '/' + path = URI.join(rest.url, path) + end + rest.get(path, *args) + end - def method_missing(method, *args) - @events << [ method, *args ] + def chef_config + {} + end + + def expect_recipe(&recipe) + expect(recipe(&recipe)) + end + + def recipe(&recipe) + RecipeRunWrapper.new(chef_config, &recipe) + end + + def chef_client + @chef_client ||= ChefRunWrapper.new(chef_config) + end + + def chef_run + chef_client.converge if !@converged + event_sink.events + end + + def event_sink + chef_client.event_sink + end + + def basic_chef_client + chef_client.client end - end + def load_recipe(&recipe) + chef_client.client.load_block(&recipe) + end + + def run_recipe(&recipe) + load_recipe(&recipe) + converge + end + + def reset_chef_client + @event_sink = nil + @basic_chef_client = nil + @converged = false + end + + def converge + if @converged + raise "Already converged! Cannot converge twice, that's bad mojo." + end + @converged = true + chef_client.converge + end + end end end end diff --git a/lib/cheffish/rspec/chef_run_wrapper.rb b/lib/cheffish/rspec/chef_run_wrapper.rb new file mode 100644 index 0000000..c03d338 --- /dev/null +++ b/lib/cheffish/rspec/chef_run_wrapper.rb @@ -0,0 +1,105 @@ +require 'cheffish/basic_chef_client' + +module Cheffish + module RSpec + class ChefRunWrapper + def initialize(chef_config) + @chef_config = chef_config + end + + attr_reader :chef_config + + def client + @client ||= begin + @stdout = StringIO.new + @stderr = StringIO.new + @logs = StringIO.new + @client = ::Cheffish::BasicChefClient.new(nil, + [ event_sink, Chef::Formatters.new(:doc, stdout, stderr) ], + { + stdout: stdout, + stderr: stderr, + log_location: logs, + log_level: :debug, + verbose_logging: false + }.merge(chef_config) + ) + end + end + + def event_sink + @event_sink ||= EventSink.new + end + + # + # output + # + attr_reader :stdout + attr_reader :stderr + attr_reader :logs + + def resources + client.resource_collection + end + + def converge + client.converge + end + + def reset + @client = nil + @converged = nil + @stdout = nil + @stderr = nil + @logs = nil + end + + def converged? + @converged + end + + def updated? + client.updated? + end + + def up_to_date? + !client.updated? + end + + def output_for_failure_message + message = "" + if stdout && !stdout.string.empty? + message << "--- ---\n" + message << "--- Chef Client Output ---\n" + message << "--- ---\n" + message << stdout.string + end + if stderr && !stderr.string.empty? + message << "--- ---\n" + message << "--- Chef Client Error Output ---\n" + message << "--- ---\n" + message << stderr.string + end + if logs && !logs.string.empty? + message << "--- ---\n" + message << "--- Chef Client Logs ---\n" + message << "--- ---\n" + message << logs.string + end + message + end + + class EventSink + def initialize + @events = [] + end + + attr_reader :events + + def method_missing(method, *args) + @events << [ method, *args ] + end + end + end + end +end diff --git a/lib/cheffish/rspec/matchers.rb b/lib/cheffish/rspec/matchers.rb index bbb0ec3..a55826f 100644 --- a/lib/cheffish/rspec/matchers.rb +++ b/lib/cheffish/rspec/matchers.rb @@ -25,6 +25,20 @@ end end +RSpec::Matchers.define :be_idempotent do + match do |recipe| + @recipe = recipe + recipe.reset + recipe.converge + recipe.up_to_date? + end + + failure_message { + "#{@recipe} is not idempotent! Converging it a second time caused updates.\n#{@recipe.output_for_failure_message}" + } +end + + RSpec::Matchers.define :update_acls do |acl_paths, expected_acls| errors = [] diff --git a/lib/cheffish/rspec/recipe_run_wrapper.rb b/lib/cheffish/rspec/recipe_run_wrapper.rb new file mode 100644 index 0000000..f3bb566 --- /dev/null +++ b/lib/cheffish/rspec/recipe_run_wrapper.rb @@ -0,0 +1,22 @@ +require 'cheffish/rspec/chef_run_wrapper' + +module Cheffish + module RSpec + class RecipeRunWrapper < ChefRunWrapper + def initialize(chef_config, &recipe) + super(chef_config) + @recipe = recipe + end + + attr_reader :recipe + + def client + if !@client + super + @client.load_block(&recipe) + end + @client + end + end + end +end From 418d3b89b069ed5cfd99704a6098b617ea575a0a Mon Sep 17 00:00:00 2001 From: John Keiser Date: Thu, 26 Mar 2015 15:10:53 -0700 Subject: [PATCH 3/8] Add ability to stream to stdout as well as capture output --- lib/cheffish/rspec/chef_run_wrapper.rb | 59 +++++++++++++++++--------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/lib/cheffish/rspec/chef_run_wrapper.rb b/lib/cheffish/rspec/chef_run_wrapper.rb index c03d338..b57ed56 100644 --- a/lib/cheffish/rspec/chef_run_wrapper.rb +++ b/lib/cheffish/rspec/chef_run_wrapper.rb @@ -4,25 +4,36 @@ module Cheffish module RSpec class ChefRunWrapper def initialize(chef_config) - @chef_config = chef_config + @chef_config = chef_config || {} end attr_reader :chef_config + class StringIOTee < StringIO + def initialize(*streams) + super() + @streams = streams.flatten.select { |s| !s.nil? } + end + + attr_reader :streams + + def write(*args, &block) + super + streams.each { |s| s.write(*args, &block) } + end + end + def client @client ||= begin - @stdout = StringIO.new - @stderr = StringIO.new - @logs = StringIO.new + chef_config = self.chef_config.dup + chef_config[:log_level] ||= :debug if !chef_config.has_key?(:log_level) + chef_config[:verbose_logging] = false if !chef_config.has_key?(:verbose_logging) + chef_config[:stdout] = StringIOTee.new(chef_config[:stdout]) + chef_config[:stderr] = StringIOTee.new(chef_config[:stderr]) + chef_config[:log_location] = StringIOTee.new(chef_config[:log_location]) @client = ::Cheffish::BasicChefClient.new(nil, - [ event_sink, Chef::Formatters.new(:doc, stdout, stderr) ], - { - stdout: stdout, - stderr: stderr, - log_location: logs, - log_level: :debug, - verbose_logging: false - }.merge(chef_config) + [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ], + chef_config ) end end @@ -34,9 +45,15 @@ def event_sink # # output # - attr_reader :stdout - attr_reader :stderr - attr_reader :logs + def stdout + @client ? client.chef_config[:stdout].string : nil + end + def stderr + @client ? client.chef_config[:stderr].string : nil + end + def logs + @client ? client.chef_config[:log_location].string : nil + end def resources client.resource_collection @@ -68,19 +85,21 @@ def up_to_date? def output_for_failure_message message = "" - if stdout && !stdout.string.empty? + if stdout && !stdout.empty? message << "--- ---\n" message << "--- Chef Client Output ---\n" message << "--- ---\n" - message << stdout.string + message << stdout + message << "\n" if !stdout.end_with?("\n") end - if stderr && !stderr.string.empty? + if stderr && !stderr.empty? message << "--- ---\n" message << "--- Chef Client Error Output ---\n" message << "--- ---\n" - message << stderr.string + message << stderr + message << "\n" if !stderr.end_with?("\n") end - if logs && !logs.string.empty? + if logs && !logs.empty? message << "--- ---\n" message << "--- Chef Client Logs ---\n" message << "--- ---\n" From 7254f7c36423731c51a33bd9640eed93c652b325 Mon Sep 17 00:00:00 2001 From: John Keiser Date: Thu, 26 Mar 2015 15:11:16 -0700 Subject: [PATCH 4/8] Don't crash when reading an existing DSA key --- lib/cheffish/key_formatter.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/cheffish/key_formatter.rb b/lib/cheffish/key_formatter.rb index 2c4011b..e9187e5 100644 --- a/lib/cheffish/key_formatter.rb +++ b/lib/cheffish/key_formatter.rb @@ -24,7 +24,7 @@ def self.decode(str, pass_phrase=nil, filename='') end key_format[:type] = type_of(key) - key_format[:size] = size_of(key) + key_format[:size] = size_of(key) if size_of(key) key_format[:pass_phrase] = pass_phrase if pass_phrase # TODO cipher, exponent @@ -102,8 +102,12 @@ def self.type_of(key) end def self.size_of(key) - # TODO DSA -- this is RSA only - key.n.num_bytes * 8 + case key.class + when OpenSSL::PKey::RSA + key.n.num_bytes * 8 + else + nil + end end end end From 78f2a41e666ed7a81a3db443a995118d199ed0de Mon Sep 17 00:00:00 2001 From: John Keiser Date: Thu, 26 Mar 2015 15:12:12 -0700 Subject: [PATCH 5/8] Add more logging when keys are selected or corrupt --- lib/chef/provider/private_key.rb | 1 + lib/cheffish.rb | 18 ++++++++++-------- lib/cheffish/rspec/chef_run_wrapper.rb | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/chef/provider/private_key.rb b/lib/chef/provider/private_key.rb index 3a36b20..a1261da 100644 --- a/lib/chef/provider/private_key.rb +++ b/lib/chef/provider/private_key.rb @@ -209,6 +209,7 @@ def load_current_resource end rescue # If there's an error reading, we assume format and type are wrong and don't futz with them + Chef::Log.warn("Error reading #{new_path}: #{$!}") end else resource.action :delete diff --git a/lib/cheffish.rb b/lib/cheffish.rb index ae608e6..2331e1b 100644 --- a/lib/cheffish.rb +++ b/lib/cheffish.rb @@ -1,11 +1,3 @@ -require 'chef/run_list/run_list_item' -require 'cheffish/basic_chef_client' -require 'cheffish/server_api' -require 'chef/knife' -require 'chef/config_fetcher' -require 'chef/log' -require 'chef/application' - module Cheffish NAME_REGEX = /^[.\-[:alnum:]_]+$/ @@ -92,8 +84,10 @@ def self.get_private_key(name, config = profiled_config) def self.get_private_key_with_path(name, config = profiled_config) if config[:private_keys] && config[:private_keys][name] if config[:private_keys][name].is_a?(String) + Chef::Log.info("Got key #{name} from Chef::Config.private_keys.#{name}, which points at #{config[:private_keys[name]]}. Reading key from there ...") return [ IO.read(config[:private_keys][name]), config[:private_keys][name] ] else + Chef::Log.info("Got key #{name} raw from Chef::Config.private_keys.#{name}.") return [ config[:private_keys][name].to_pem, nil ] end elsif config[:private_key_paths] @@ -104,6 +98,7 @@ def self.get_private_key_with_path(name, config = profiled_config) if ext == '' || ext == '.pem' key_name = key[0..-(ext.length+1)] if key_name == name + Chef::Log.info("Reading key #{name} from file #{private_key_path}/#{key}") return [ IO.read("#{private_key_path}/#{key}"), "#{private_key_path}/#{key}" ] end end @@ -223,4 +218,11 @@ def remove_role(*roles) # Include all recipe objects so require 'cheffish' brings in the whole recipe DSL +require 'chef/run_list/run_list_item' +require 'cheffish/basic_chef_client' +require 'cheffish/server_api' +require 'chef/knife' +require 'chef/config_fetcher' +require 'chef/log' +require 'chef/application' require 'cheffish/recipe_dsl' diff --git a/lib/cheffish/rspec/chef_run_wrapper.rb b/lib/cheffish/rspec/chef_run_wrapper.rb index b57ed56..2c6987f 100644 --- a/lib/cheffish/rspec/chef_run_wrapper.rb +++ b/lib/cheffish/rspec/chef_run_wrapper.rb @@ -103,7 +103,7 @@ def output_for_failure_message message << "--- ---\n" message << "--- Chef Client Logs ---\n" message << "--- ---\n" - message << logs.string + message << logs end message end From 7ffbeb90776a3819f3cc73881e798689898feb80 Mon Sep 17 00:00:00 2001 From: John Keiser Date: Mon, 30 Mar 2015 09:38:43 -0700 Subject: [PATCH 6/8] Add new "resource" executable to create resources --- bin/resource | 20 ++++ lib/cheffish/chef_run.rb | 131 +++++++++++++++++++++++++ lib/cheffish/rspec/chef_run_wrapper.rb | 125 +---------------------- 3 files changed, 154 insertions(+), 122 deletions(-) create mode 100755 bin/resource create mode 100644 lib/cheffish/chef_run.rb diff --git a/bin/resource b/bin/resource new file mode 100755 index 0000000..5879645 --- /dev/null +++ b/bin/resource @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby +require 'cheffish/chef_run' + +def post(resource_type, name, properties) + chef_run = Cheffish::ChefRun.new + begin + r = chef_run.client.build_resource(resource_type, name) do + properties.each { |attr, value| public_send(attr, value) } + end + chef_run.client.add_resource(r) + chef_run.converge + puts "CODE: #{chef_run.updated? ? 201 : 200}" + puts "STDOUT: #{chef_run.stdout}\nSTDERR: #{chef_run.stderr}\nLOGS: #{chef_run.logs}" + rescue + puts "CODE: #{400}" + puts "ERROR: #{$!}\nBACKTRACE: #{$!.backtrace}\nSTDOUT: #{chef_run.stdout}\nSTDERR: #{chef_run.stderr}\nLOGS: #{chef_run.logs}" + end +end + +post(ARGV.shift, ARGV.shift, Hash[*ARGV]) diff --git a/lib/cheffish/chef_run.rb b/lib/cheffish/chef_run.rb new file mode 100644 index 0000000..9518248 --- /dev/null +++ b/lib/cheffish/chef_run.rb @@ -0,0 +1,131 @@ +require 'cheffish/basic_chef_client' + +module Cheffish + class ChefRun + # + # @param chef_config A hash with symbol keys that looks suspiciously similar to `Chef::Config`. + # Some possible options: + # - stdout: - where to stream stdout to + # - stderr: - where to stream stderr to + # - log_level: :debug|:info|:warn|:error|:fatal + # - log_location: - where to stream logs to + # - verbose_logging: true|false - true if you want verbose logging in :debug + # + def initialize(chef_config={}) + @chef_config = chef_config || {} + end + + attr_reader :chef_config + + class StringIOTee < StringIO + def initialize(*streams) + super() + @streams = streams.flatten.select { |s| !s.nil? } + end + + attr_reader :streams + + def write(*args, &block) + super + streams.each { |s| s.write(*args, &block) } + end + end + + def client + @client ||= begin + chef_config = self.chef_config.dup + chef_config[:log_level] ||= :debug if !chef_config.has_key?(:log_level) + chef_config[:verbose_logging] = false if !chef_config.has_key?(:verbose_logging) + chef_config[:stdout] = StringIOTee.new(chef_config[:stdout]) + chef_config[:stderr] = StringIOTee.new(chef_config[:stderr]) + chef_config[:log_location] = StringIOTee.new(chef_config[:log_location]) + @client = ::Cheffish::BasicChefClient.new(nil, + [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ], + chef_config + ) + end + end + + def event_sink + @event_sink ||= EventSink.new + end + + # + # output + # + def stdout + @client ? client.chef_config[:stdout].string : nil + end + def stderr + @client ? client.chef_config[:stderr].string : nil + end + def logs + @client ? client.chef_config[:log_location].string : nil + end + + def resources + client.resource_collection + end + + def converge + client.converge + end + + def reset + @client = nil + @converged = nil + @stdout = nil + @stderr = nil + @logs = nil + end + + def converged? + @converged + end + + def updated? + client.updated? + end + + def up_to_date? + !client.updated? + end + + def output_for_failure_message + message = "" + if stdout && !stdout.empty? + message << "--- ---\n" + message << "--- Chef Client Output ---\n" + message << "--- ---\n" + message << stdout + message << "\n" if !stdout.end_with?("\n") + end + if stderr && !stderr.empty? + message << "--- ---\n" + message << "--- Chef Client Error Output ---\n" + message << "--- ---\n" + message << stderr + message << "\n" if !stderr.end_with?("\n") + end + if logs && !logs.empty? + message << "--- ---\n" + message << "--- Chef Client Logs ---\n" + message << "--- ---\n" + message << logs + end + message + end + + class EventSink + def initialize + @events = [] + end + + attr_reader :events + + def method_missing(method, *args) + @events << [ method, *args ] + end + end + end +end diff --git a/lib/cheffish/rspec/chef_run_wrapper.rb b/lib/cheffish/rspec/chef_run_wrapper.rb index 2c6987f..76e2470 100644 --- a/lib/cheffish/rspec/chef_run_wrapper.rb +++ b/lib/cheffish/rspec/chef_run_wrapper.rb @@ -1,124 +1,5 @@ -require 'cheffish/basic_chef_client' +require 'cheffish/chef_run' -module Cheffish - module RSpec - class ChefRunWrapper - def initialize(chef_config) - @chef_config = chef_config || {} - end - - attr_reader :chef_config - - class StringIOTee < StringIO - def initialize(*streams) - super() - @streams = streams.flatten.select { |s| !s.nil? } - end - - attr_reader :streams - - def write(*args, &block) - super - streams.each { |s| s.write(*args, &block) } - end - end - - def client - @client ||= begin - chef_config = self.chef_config.dup - chef_config[:log_level] ||= :debug if !chef_config.has_key?(:log_level) - chef_config[:verbose_logging] = false if !chef_config.has_key?(:verbose_logging) - chef_config[:stdout] = StringIOTee.new(chef_config[:stdout]) - chef_config[:stderr] = StringIOTee.new(chef_config[:stderr]) - chef_config[:log_location] = StringIOTee.new(chef_config[:log_location]) - @client = ::Cheffish::BasicChefClient.new(nil, - [ event_sink, Chef::Formatters.new(:doc, chef_config[:stdout], chef_config[:stderr]) ], - chef_config - ) - end - end - - def event_sink - @event_sink ||= EventSink.new - end - - # - # output - # - def stdout - @client ? client.chef_config[:stdout].string : nil - end - def stderr - @client ? client.chef_config[:stderr].string : nil - end - def logs - @client ? client.chef_config[:log_location].string : nil - end - - def resources - client.resource_collection - end - - def converge - client.converge - end - - def reset - @client = nil - @converged = nil - @stdout = nil - @stderr = nil - @logs = nil - end - - def converged? - @converged - end - - def updated? - client.updated? - end - - def up_to_date? - !client.updated? - end - - def output_for_failure_message - message = "" - if stdout && !stdout.empty? - message << "--- ---\n" - message << "--- Chef Client Output ---\n" - message << "--- ---\n" - message << stdout - message << "\n" if !stdout.end_with?("\n") - end - if stderr && !stderr.empty? - message << "--- ---\n" - message << "--- Chef Client Error Output ---\n" - message << "--- ---\n" - message << stderr - message << "\n" if !stderr.end_with?("\n") - end - if logs && !logs.empty? - message << "--- ---\n" - message << "--- Chef Client Logs ---\n" - message << "--- ---\n" - message << logs - end - message - end - - class EventSink - def initialize - @events = [] - end - - attr_reader :events - - def method_missing(method, *args) - @events << [ method, *args ] - end - end - end - end +module Cheffish::RSpec + ChefRunWrapper = Cheffish::ChefRun end From 62e49a0a3bce9462f5c4748b985ef21a96e676fa Mon Sep 17 00:00:00 2001 From: tyler-ball Date: Mon, 6 Apr 2015 12:46:56 -0500 Subject: [PATCH 7/8] Using latest released version of chef-zero --- cheffish.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cheffish.gemspec b/cheffish.gemspec index 470d220..37bc6c4 100644 --- a/cheffish.gemspec +++ b/cheffish.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |s| s.email = 'jkeiser@opscode.com' s.homepage = 'http://wiki.opscode.com/display/chef' - s.add_dependency 'chef-zero', '~> 4.0' + s.add_dependency 'chef-zero', '~> 4.2' s.add_dependency 'chef' , '~> 12.1' s.add_development_dependency 'rake' From ade163c0e48a42689e4ca229f6491cc3b875d759 Mon Sep 17 00:00:00 2001 From: tyler-ball Date: Mon, 6 Apr 2015 14:06:23 -0500 Subject: [PATCH 8/8] Fixing failing private key tests - letting the chef_run tell us if it converged or not --- lib/cheffish/chef_run.rb | 15 +++++++++++++-- lib/cheffish/rspec/chef_run_support.rb | 8 +++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/cheffish/chef_run.rb b/lib/cheffish/chef_run.rb index 9518248..b63c8e3 100644 --- a/lib/cheffish/chef_run.rb +++ b/lib/cheffish/chef_run.rb @@ -68,7 +68,13 @@ def resources end def converge - client.converge + begin + client.converge + @converged = true + rescue RuntimeError => e + @raised_exception = e + raise + end end def reset @@ -77,10 +83,15 @@ def reset @stdout = nil @stderr = nil @logs = nil + @raised_exception = nil end def converged? - @converged + !!@converged + end + + def converge_failed? + @raised_exception.nil? ? false : true end def updated? diff --git a/lib/cheffish/rspec/chef_run_support.rb b/lib/cheffish/rspec/chef_run_support.rb index 8a95434..b74fc63 100644 --- a/lib/cheffish/rspec/chef_run_support.rb +++ b/lib/cheffish/rspec/chef_run_support.rb @@ -30,7 +30,7 @@ def with_recipe(&block) end after :each do - if !@converged + if !chef_client.converge_failed? && !chef_client.converged? raise "Never tried to converge!" end end @@ -72,7 +72,7 @@ def chef_client end def chef_run - chef_client.converge if !@converged + converge if !chef_client.converged? event_sink.events end @@ -96,14 +96,12 @@ def run_recipe(&recipe) def reset_chef_client @event_sink = nil @basic_chef_client = nil - @converged = false end def converge - if @converged + if chef_client.converged? raise "Already converged! Cannot converge twice, that's bad mojo." end - @converged = true chef_client.converge end end