diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 59a95d35a..33a70ce8a 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -1,173 +1,157 @@ require 'puppet/application' class Puppet::Application::Apply < Puppet::Application should_parse_config option("--debug","-d") option("--execute EXECUTE","-e") do |arg| options[:code] = arg end option("--loadclasses","-L") option("--verbose","-v") option("--use-nodes") option("--detailed-exitcodes") option("--apply catalog", "-a catalog") do |arg| options[:catalog] = arg end option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end def run_command if options[:catalog] apply elsif Puppet[:parseonly] parseonly else main end end def apply if options[:catalog] == "-" text = $stdin.read else text = File.read(options[:catalog]) end begin catalog = Puppet::Resource::Catalog.convert_from(Puppet::Resource::Catalog.default_format,text) catalog = Puppet::Resource::Catalog.pson_create(catalog) unless catalog.is_a?(Puppet::Resource::Catalog) rescue => detail raise Puppet::Error, "Could not deserialize catalog from pson: #{detail}" end catalog = catalog.to_ral require 'puppet/configurer' configurer = Puppet::Configurer.new configurer.run :catalog => catalog end def parseonly # Set our code or file to use. if options[:code] or command_line.args.length == 0 Puppet[:code] = options[:code] || STDIN.read else Puppet[:manifest] = command_line.args.shift end begin Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types rescue => detail Puppet.err detail exit 1 end exit 0 end def main # Set our code or file to use. if options[:code] or command_line.args.length == 0 Puppet[:code] = options[:code] || STDIN.read else manifest = command_line.args.shift raise "Could not find file #{manifest}" unless File.exist?(manifest) Puppet.warning("Only one file can be applied per run. Skipping #{command_line.args.join(', ')}") if command_line.args.size > 0 Puppet[:manifest] = manifest end # Collect our facts. unless facts = Puppet::Node::Facts.find(Puppet[:certname]) raise "Could not find facts for #{Puppet[:certname]}" end # Find our Node unless node = Puppet::Node.find(Puppet[:certname]) raise "Could not find node #{Puppet[:certname]}" end # Merge in the facts. node.merge(facts.values) # Allow users to load the classes that puppet agent creates. if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) $stderr.puts "#{file} is not readable" exit(63) end node.classes = File.read(file).split(/[\s\n]+/) end end begin # Compile our catalog starttime = Time.now catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral catalog.finalize catalog.retrieval_duration = Time.now - starttime require 'puppet/configurer' configurer = Puppet::Configurer.new - configurer.execute_prerun_command + report = configurer.run(:skip_plugin_download => true, :catalog => catalog) - # And apply it - if Puppet[:report] - report = configurer.initialize_report - Puppet::Util::Log.newdestination(report) - end - transaction = catalog.apply - - configurer.execute_postrun_command - - if Puppet[:report] - Puppet::Util::Log.close(report) - configurer.send_report(report, transaction) - else - transaction.generate_report - end - - exit( Puppet[:noop] ? 0 : options[:detailed_exitcodes] ? transaction.report.exit_status : 0 ) + exit( Puppet[:noop] ? 0 : options[:detailed_exitcodes] ? report.exit_status : 0 ) rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts detail.message exit(1) end end def setup exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? # If noop is set, then also enable diffs Puppet[:show_diff] = true if Puppet[:noop] Puppet::Util::Log.newdestination(:console) unless options[:logset] client = nil server = nil trap(:INT) do $stderr.puts "Exiting" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/application/inspect.rb b/lib/puppet/application/inspect.rb index c28fef326..caa32a7c2 100644 --- a/lib/puppet/application/inspect.rb +++ b/lib/puppet/application/inspect.rb @@ -1,80 +1,82 @@ require 'puppet/application' class Puppet::Application::Inspect < Puppet::Application should_parse_config run_mode :agent option("--debug","-d") option("--verbose","-v") option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end def setup exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? raise "Inspect requires reporting to be enabled. Set report=true in puppet.conf to enable reporting." unless Puppet[:report] @report = Puppet::Transaction::Report.new("inspect") Puppet::Util::Log.newdestination(@report) Puppet::Util::Log.newdestination(:console) unless options[:logset] trap(:INT) do $stderr.puts "Exiting" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end Puppet::Transaction::Report.terminus_class = :rest Puppet::Resource::Catalog.terminus_class = :yaml end def run_command retrieval_starttime = Time.now unless catalog = Puppet::Resource::Catalog.find(Puppet[:certname]) raise "Could not find catalog for #{Puppet[:certname]}" end + @report.configuration_version = catalog.version + retrieval_time = Time.now - retrieval_starttime @report.add_times("config_retrieval", retrieval_time) starttime = Time.now catalog.to_ral.resources.each do |ral_resource| audited_attributes = ral_resource[:audit] next unless audited_attributes audited_resource = ral_resource.to_resource status = Puppet::Resource::Status.new(ral_resource) audited_attributes.each do |name| event = ral_resource.event(:previous_value => audited_resource[name], :property => name, :status => "audit", :message => "inspected value is #{audited_resource[name].inspect}") status.add_event(event) end @report.add_resource_status(status) end @report.add_metric(:time, {"config_retrieval" => retrieval_time, "inspect" => Time.now - starttime}) begin @report.save rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not send report: #{detail}" end end end diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index 31d31c2d2..070176554 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -1,239 +1,237 @@ # The client for interacting with the puppetmaster config server. require 'sync' require 'timeout' require 'puppet/network/http_pool' require 'puppet/util' class Puppet::Configurer class CommandHookError < RuntimeError; end require 'puppet/configurer/fact_handler' require 'puppet/configurer/plugin_handler' include Puppet::Configurer::FactHandler include Puppet::Configurer::PluginHandler # For benchmarking include Puppet::Util attr_reader :compile_time # Provide more helpful strings to the logging that the Agent does def self.to_s "Puppet configuration client" end class << self # Puppetd should only have one instance running, and we need a way # to retrieve it. attr_accessor :instance include Puppet::Util end # How to lock instances of this class. def self.lockfile_path Puppet[:puppetdlockfile] end def clear @catalog.clear(true) if @catalog @catalog = nil end def execute_postrun_command execute_from_setting(:postrun_command) end def execute_prerun_command execute_from_setting(:prerun_command) end # Initialize and load storage def dostorage Puppet::Util::Storage.load @compile_time ||= Puppet::Util::Storage.cache(:configuration)[:compile_time] rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Corrupt state file #{Puppet[:statefile]}: #{detail}" begin ::File.unlink(Puppet[:statefile]) retry rescue => detail raise Puppet::Error.new("Cannot remove #{Puppet[:statefile]}: #{detail}") end end # Just so we can specify that we are "the" instance. def initialize Puppet.settings.use(:main, :ssl, :agent) self.class.instance = self @running = false @splayed = false end - def initialize_report - Puppet::Transaction::Report.new - end - # Prepare for catalog retrieval. Downloads everything necessary, etc. - def prepare + def prepare(options) dostorage - download_plugins + download_plugins unless options[:skip_plugin_download] - download_fact_plugins + download_fact_plugins unless options[:skip_plugin_download] execute_prerun_command end # Get the remote catalog, yo. Returns nil if no catalog can be found. def retrieve_catalog if Puppet::Resource::Catalog.indirection.terminus_class == :rest # This is a bit complicated. We need the serialized and escaped facts, # and we need to know which format they're encoded in. Thus, we # get a hash with both of these pieces of information. fact_options = facts_for_uploading else fact_options = {} end # First try it with no cache, then with the cache. unless (Puppet[:use_cached_catalog] and result = retrieve_catalog_from_cache(fact_options)) or result = retrieve_new_catalog(fact_options) if ! Puppet[:usecacheonfailure] Puppet.warning "Not using cache on failed catalog" return nil end result = retrieve_catalog_from_cache(fact_options) end return nil unless result convert_catalog(result, @duration) end # Convert a plain resource catalog into our full host catalog. def convert_catalog(result, duration) catalog = result.to_ral catalog.finalize catalog.retrieval_duration = duration catalog.write_class_file catalog end # The code that actually runs the catalog. # This just passes any options on to the catalog, # which accepts :tags and :ignoreschedules. def run(options = {}) begin - prepare + prepare(options) rescue SystemExit,NoMemoryError raise rescue Exception => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Failed to prepare catalog: #{detail}" end - options[:report] ||= initialize_report + options[:report] ||= Puppet::Transaction::Report.new("apply") report = options[:report] Puppet::Util::Log.newdestination(report) if catalog = options[:catalog] options.delete(:catalog) elsif ! catalog = retrieve_catalog Puppet.err "Could not retrieve catalog; skipping run" return end + report.configuration_version = catalog.version + transaction = nil begin benchmark(:notice, "Finished catalog run") do transaction = catalog.apply(options) end report rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Failed to apply catalog: #{detail}" return end ensure # Make sure we forget the retained module_directories of any autoload # we might have used. Thread.current[:env_module_directories] = nil # Now close all of our existing http connections, since there's no # reason to leave them lying open. Puppet::Network::HttpPool.clear_http_instances execute_postrun_command Puppet::Util::Log.close(report) send_report(report, transaction) end def send_report(report, trans = nil) trans.generate_report if trans puts report.summary if Puppet[:summarize] report.save if Puppet[:report] rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not send report: #{detail}" end private def self.timeout timeout = Puppet[:configtimeout] case timeout when String if timeout =~ /^\d+$/ timeout = Integer(timeout) else raise ArgumentError, "Configuration timeout must be an integer" end when Integer # nothing else raise ArgumentError, "Configuration timeout must be an integer" end timeout end def execute_from_setting(setting) return if (command = Puppet[setting]) == "" begin Puppet::Util.execute([command]) rescue => detail raise CommandHookError, "Could not run command from #{setting}: #{detail}" end end def retrieve_catalog_from_cache(fact_options) result = nil @duration = thinmark do result = Puppet::Resource::Catalog.find(Puppet[:certname], fact_options.merge(:ignore_terminus => true)) end Puppet.notice "Using cached catalog" result rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not retrieve catalog from cache: #{detail}" return nil end def retrieve_new_catalog(fact_options) result = nil @duration = thinmark do result = Puppet::Resource::Catalog.find(Puppet[:certname], fact_options.merge(:ignore_cache => true)) end result rescue SystemExit,NoMemoryError raise rescue Exception => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not retrieve catalog from remote server: #{detail}" return nil end end diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index dcd9aad0a..2d49062dd 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -1,361 +1,361 @@ # the class that actually walks our resource/property tree, collects the changes, # and performs them require 'puppet' require 'puppet/util/tagging' require 'puppet/application' class Puppet::Transaction require 'puppet/transaction/change' require 'puppet/transaction/event' require 'puppet/transaction/event_manager' require 'puppet/transaction/resource_harness' require 'puppet/resource/status' attr_accessor :component, :catalog, :ignoreschedules attr_accessor :sorted_resources, :configurator # The report, once generated. attr_accessor :report # Routes and stores any events and subscriptions. attr_reader :event_manager # Handles most of the actual interacting with resources attr_reader :resource_harness include Puppet::Util include Puppet::Util::Tagging # Wraps application run state check to flag need to interrupt processing def stop_processing? Puppet::Application.stop_requested? end # Add some additional times for reporting def add_times(hash) hash.each do |name, num| report.add_times(name, num) end end # Are there any failed resources in this transaction? def any_failed? report.resource_statuses.values.detect { |status| status.failed? } end # Apply all changes for a resource def apply(resource, ancestor = nil) status = resource_harness.evaluate(resource) add_resource_status(status) event_manager.queue_events(ancestor || resource, status.events) rescue => detail resource.err "Could not evaluate: #{detail}" end # Find all of the changed resources. def changed? report.resource_statuses.values.find_all { |status| status.changed }.collect { |status| catalog.resource(status.resource) } end # Copy an important relationships from the parent to the newly-generated # child resource. def make_parent_child_relationship(resource, children) depthfirst = resource.depthfirst? children.each do |gen_child| if depthfirst edge = [gen_child, resource] else edge = [resource, gen_child] end relationship_graph.add_vertex(gen_child) unless relationship_graph.edge?(edge[1], edge[0]) relationship_graph.add_edge(*edge) else resource.debug "Skipping automatic relationship to #{gen_child}" end end end # See if the resource generates new resources at evaluation time. def eval_generate(resource) generate_additional_resources(resource, :eval_generate) end # Evaluate a single resource. def eval_resource(resource, ancestor = nil) if skip?(resource) resource_status(resource).skipped = true else eval_children_and_apply_resource(resource, ancestor) end # Check to see if there are any events queued for this resource event_manager.process_events(resource) end def eval_children_and_apply_resource(resource, ancestor = nil) resource_status(resource).scheduled = true # We need to generate first regardless, because the recursive # actions sometimes change how the top resource is applied. children = eval_generate(resource) if ! children.empty? and resource.depthfirst? children.each do |child| # The child will never be skipped when the parent isn't eval_resource(child, ancestor || resource) end end # Perform the actual changes apply(resource, ancestor) if ! children.empty? and ! resource.depthfirst? children.each do |child| eval_resource(child, ancestor || resource) end end end # This method does all the actual work of running a transaction. It # collects all of the changes, executes them, and responds to any # necessary events. def evaluate # Start logging. Puppet::Util::Log.newdestination(@report) prepare Puppet.info "Applying configuration version '#{catalog.version}'" if catalog.version begin @sorted_resources.each do |resource| next if stop_processing? if resource.is_a?(Puppet::Type::Component) Puppet.warning "Somehow left a component in the relationship graph" next end ret = nil seconds = thinmark do ret = eval_resource(resource) end resource.info "Evaluated in %0.2f seconds" % seconds if Puppet[:evaltrace] and @catalog.host_config? ret end ensure # And then close the transaction log. Puppet::Util::Log.close(@report) end Puppet.debug "Finishing transaction #{object_id}" end def events event_manager.events end def failed?(resource) s = resource_status(resource) and s.failed? end # Does this resource have any failed dependencies? def failed_dependencies?(resource) # First make sure there are no failed dependencies. To do this, # we check for failures in any of the vertexes above us. It's not # enough to check the immediate dependencies, which is why we use # a tree from the reversed graph. found_failed = false relationship_graph.dependencies(resource).each do |dep| next unless failed?(dep) resource.notice "Dependency #{dep} has failures: #{resource_status(dep).failed}" found_failed = true end found_failed end # A general method for recursively generating new resources from a # resource. def generate_additional_resources(resource, method) return [] unless resource.respond_to?(method) begin made = resource.send(method) rescue => detail puts detail.backtrace if Puppet[:trace] resource.err "Failed to generate additional resources using '#{method}': #{detail}" end return [] unless made made = [made] unless made.is_a?(Array) made.uniq.find_all do |res| begin res.tag(*resource.tags) @catalog.add_resource(res) do |r| r.finish make_parent_child_relationship(resource, [r]) # Call 'generate' recursively generate_additional_resources(r, method) end true rescue Puppet::Resource::Catalog::DuplicateResourceError res.info "Duplicate generated resource; skipping" false end end end # Collect any dynamically generated resources. This method is called # before the transaction starts. def generate list = @catalog.vertices newlist = [] while ! list.empty? list.each do |resource| newlist += generate_additional_resources(resource, :generate) end list = newlist newlist = [] end end # Generate a transaction report. def generate_report @report.calculate_metrics @report end # Should we ignore tags? def ignore_tags? ! (@catalog.host_config? or Puppet[:name] == "puppet") end # this should only be called by a Puppet::Type::Component resource now # and it should only receive an array def initialize(catalog) @catalog = catalog - @report = Report.new + @report = Report.new("apply", catalog.version) @event_manager = Puppet::Transaction::EventManager.new(self) @resource_harness = Puppet::Transaction::ResourceHarness.new(self) end # Prefetch any providers that support it. We don't support prefetching # types, just providers. def prefetch prefetchers = {} @catalog.vertices.each do |resource| if provider = resource.provider and provider.class.respond_to?(:prefetch) prefetchers[provider.class] ||= {} prefetchers[provider.class][resource.name] = resource end end # Now call prefetch, passing in the resources so that the provider instances can be replaced. prefetchers.each do |provider, resources| Puppet.debug "Prefetching #{provider.name} resources for #{provider.resource_type.name}" begin provider.prefetch(resources) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not prefetch #{provider.resource_type.name} provider '#{provider.name}': #{detail}" end end end # Prepare to evaluate the resources in a transaction. def prepare # Now add any dynamically generated resources generate # Then prefetch. It's important that we generate and then prefetch, # so that any generated resources also get prefetched. prefetch # This will throw an error if there are cycles in the graph. @sorted_resources = relationship_graph.topsort end def relationship_graph catalog.relationship_graph end # Send off the transaction report. def send_report begin report = generate_report rescue => detail Puppet.err "Could not generate report: #{detail}" return end puts report.summary if Puppet[:summarize] if Puppet[:report] begin report.save rescue => detail Puppet.err "Reporting failed: #{detail}" end end end def add_resource_status(status) report.add_resource_status status end def resource_status(resource) report.resource_statuses[resource.to_s] || add_resource_status(Puppet::Resource::Status.new(resource)) end # Is the resource currently scheduled? def scheduled?(resource) self.ignoreschedules or resource_harness.scheduled?(resource_status(resource), resource) end # Should this resource be skipped? def skip?(resource) if missing_tags?(resource) resource.debug "Not tagged with #{tags.join(", ")}" elsif ! scheduled?(resource) resource.debug "Not scheduled" elsif failed_dependencies?(resource) resource.warning "Skipping because of failed dependencies" elsif resource.virtual? resource.debug "Skipping because virtual" else return false end true end # The tags we should be checking. def tags self.tags = Puppet[:tags] unless defined?(@tags) super end def handle_qualified_tags( qualified ) # The default behavior of Puppet::Util::Tagging is # to split qualified tags into parts. That would cause # qualified tags to match too broadly here. return end # Is this resource tagged appropriately? def missing_tags?(resource) return false if ignore_tags? return false if tags.empty? not resource.tagged?(*tags) end end require 'puppet/transaction/report' diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 75c08fc7a..8a928454f 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -1,150 +1,154 @@ require 'puppet' require 'puppet/indirector' # A class for reporting what happens on each client. Reports consist of # two types of data: Logs and Metrics. Logs are the output that each # change produces, and Metrics are all of the numerical data involved # in the transaction. class Puppet::Transaction::Report extend Puppet::Indirector indirects :report, :terminus_class => :processor + attr_accessor :configuration_version attr_reader :resource_statuses, :logs, :metrics, :host, :time, :kind # This is necessary since Marshall doesn't know how to # dump hash with default proc (see below @records) def self.default_format :yaml end def <<(msg) @logs << msg self end def add_times(name, value) @external_times[name] = value end def add_metric(name, hash) metric = Puppet::Util::Metric.new(name) hash.each do |name, value| metric.newvalue(name, value) end @metrics[metric.name] = metric metric end def add_resource_status(status) @resource_statuses[status.resource] = status end def calculate_metrics calculate_resource_metrics calculate_time_metrics calculate_change_metrics calculate_event_metrics end - def initialize(kind = "apply") + def initialize(kind, configuration_version=nil) @metrics = {} @logs = [] @resource_statuses = {} @external_times ||= {} @host = Puppet[:certname] @time = Time.now @kind = kind + @report_format = 2 + @puppet_version = Puppet.version + @configuration_version = configuration_version end def name host end # Provide a summary of this report. def summary ret = "" @metrics.sort { |a,b| a[1].label <=> b[1].label }.each do |name, metric| ret += "#{metric.label}:\n" metric.values.sort { |a,b| # sort by label if a[0] == :total 1 elsif b[0] == :total -1 else a[1] <=> b[1] end }.each do |name, label, value| next if value == 0 value = "%0.2f" % value if value.is_a?(Float) ret += " %15s %s\n" % [label + ":", value] end end ret end # Based on the contents of this report's metrics, compute a single number # that represents the report. The resulting number is a bitmask where # individual bits represent the presence of different metrics. def exit_status status = 0 status |= 2 if @metrics["changes"][:total] > 0 status |= 4 if @metrics["resources"][:failed] > 0 status end private def calculate_change_metrics metrics = Hash.new(0) resource_statuses.each do |name, status| metrics[:total] += status.change_count if status.change_count end add_metric(:changes, metrics) end def calculate_event_metrics metrics = Hash.new(0) resource_statuses.each do |name, status| metrics[:total] += status.events.length status.events.each do |event| metrics[event.status] += 1 end end add_metric(:events, metrics) end def calculate_resource_metrics metrics = Hash.new(0) metrics[:total] = resource_statuses.length resource_statuses.each do |name, status| Puppet::Resource::Status::STATES.each do |state| metrics[state] += 1 if status.send(state) end end add_metric(:resources, metrics) end def calculate_time_metrics metrics = Hash.new(0) resource_statuses.each do |name, status| type = Puppet::Resource.new(name).type metrics[type.to_s.downcase] += status.evaluation_time if status.evaluation_time end @external_times.each do |name, value| metrics[name.to_s.downcase] = value end add_metric(:time, metrics) end end diff --git a/spec/integration/indirector/report/rest_spec.rb b/spec/integration/indirector/report/rest_spec.rb index fdc218975..7fa026b73 100644 --- a/spec/integration/indirector/report/rest_spec.rb +++ b/spec/integration/indirector/report/rest_spec.rb @@ -1,97 +1,97 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } require 'puppet/transaction/report' require 'puppet/network/server' require 'puppet/network/http/webrick/rest' describe "Report REST Terminus" do before do Puppet[:masterport] = 34343 Puppet[:server] = "localhost" # Get a safe temporary file @tmpfile = Tempfile.new("webrick_integration_testing") @dir = @tmpfile.path + "_dir" Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet.settings[:server] = "127.0.0.1" Puppet.settings[:masterport] = "34343" Puppet::Util::Cacher.expire Puppet[:servertype] = 'webrick' Puppet[:server] = '127.0.0.1' Puppet[:certname] = '127.0.0.1' # Generate the certificate with a local CA Puppet::SSL::Host.ca_location = :local ca = Puppet::SSL::CertificateAuthority.new ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) @host = Puppet::SSL::Host.new(Puppet[:certname]) @params = { :port => 34343, :handlers => [ :report ] } @server = Puppet::Network::Server.new(@params) @server.listen # Let's use REST for our reports :-) @old_terminus = Puppet::Transaction::Report.indirection.terminus_class Puppet::Transaction::Report.terminus_class = :rest # LAK:NOTE We need to have a fake model here so that our indirected methods get # passed through REST; otherwise we'd be stubbing 'save', which would cause an immediate # return. @report = stub_everything 'report' @mock_model = stub_everything 'faked model', :name => "report", :convert_from => @report Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization) end after do Puppet::Network::HttpPool.expire Puppet::SSL::Host.ca_location = :none Puppet.settings.clear @server.unlisten Puppet::Transaction::Report.terminus_class = @old_terminus end it "should be able to send a report to the server" do @report.expects(:save) - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") resourcemetrics = { :total => 12, :out_of_sync => 20, :applied => 45, :skipped => 1, :restarted => 23, :failed_restarts => 1, :scheduled => 10 } report.add_metric(:resources, resourcemetrics) timemetrics = { :resource1 => 10, :resource2 => 50, :resource3 => 40, :resource4 => 20, } report.add_metric(:times, timemetrics) report.add_metric( :changes, :total => 20 ) report.save end end diff --git a/spec/integration/transaction/report_spec.rb b/spec/integration/transaction/report_spec.rb index eed7acaa9..e7d952eb2 100755 --- a/spec/integration/transaction/report_spec.rb +++ b/spec/integration/transaction/report_spec.rb @@ -1,29 +1,29 @@ #!/usr/bin/env ruby # # Created by Luke Kanies on 2008-4-8. # Copyright (c) 2008. All rights reserved. require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Transaction::Report do describe "when using the indirector" do after do Puppet::Util::Cacher.expire Puppet.settings.stubs(:use) end it "should be able to delegate to the :processor terminus" do Puppet::Transaction::Report.indirection.stubs(:terminus_class).returns :processor terminus = Puppet::Transaction::Report.indirection.terminus(:processor) Facter.stubs(:value).returns "host.domain.com" - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") terminus.expects(:process).with(report) report.save end end end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 877c47bcc..4e1744206 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -1,405 +1,405 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/apply' require 'puppet/file_bucket/dipper' +require 'puppet/configurer' describe Puppet::Application::Apply do before :each do @apply = Puppet::Application[:apply] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @apply.options.expects(:[]=).with(option, 'arg') @apply.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @apply.options.expects(:[]=).with(:code, 'arg') @apply.send("handle_execute".to_sym, 'arg') end it "should ask Puppet::Application to parse Puppet configuration file" do @apply.should_parse_config?.should be_true end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @apply.handle_logdest("console") end it "should put the logset options to true" do @apply.options.expects(:[]=).with(:logset,true) @apply.handle_logdest("console") end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:trap) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) @apply.options.stubs(:[]).with(any_parameters) end it "should set show_diff on --noop" do Puppet.stubs(:[]=) Puppet.stubs(:[]).with(:config) Puppet.stubs(:[]).with(:noop).returns(true) Puppet.expects(:[]=).with(:show_diff, true) @apply.setup end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @apply.setup end it "should set INT trap" do @apply.expects(:trap).with(:INT) @apply.setup end it "should set log level to debug if --debug was passed" do @apply.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @apply.setup end it "should set log level to info if --verbose was passed" do @apply.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @apply.setup end it "should print puppet config if asked to in Puppet config" do @apply.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @apply.setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @apply.setup }.should raise_error(SystemExit) end end describe "when executing" do it "should dispatch to parseonly if parseonly is set" do @apply.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(true) @apply.expects(:parseonly) @apply.run_command end it "should dispatch to 'apply' if it was called with 'apply'" do @apply.options[:catalog] = "foo" @apply.expects(:apply) @apply.run_command end it "should dispatch to main if parseonly is not set" do @apply.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(false) @apply.expects(:main) @apply.run_command end describe "the parseonly command" do before :each do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @apply.stubs(:exit) @apply.options.stubs(:[]).with(:code).returns "some code" @collection = stub_everything Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) end it "should use a Puppet Resource Type Collection to parse the file" do @collection.expects(:perform_initial_import) @apply.parseonly end it "should exit with exit code 0 if no error" do @apply.expects(:exit).with(0) @apply.parseonly end it "should exit with exit code 1 if error" do @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) @apply.expects(:exit).with(1) @apply.parseonly end end describe "the main command" do before :each do Puppet.stubs(:[]) Puppet.settings.stubs(:use) Puppet.stubs(:[]).with(:prerun_command).returns "" Puppet.stubs(:[]).with(:postrun_command).returns "" Puppet.stubs(:[]).with(:trace).returns(true) @apply.options.stubs(:[]) @facts = stub_everything 'facts' Puppet::Node::Facts.stubs(:find).returns(@facts) @node = stub_everything 'node' Puppet::Node.stubs(:find).returns(@node) @catalog = stub_everything 'catalog' @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = stub_everything 'transaction' @catalog.stubs(:apply).returns(@transaction) @apply.stubs(:exit) + + Puppet::Util::Storage.stubs(:load) + Puppet::Configurer.any_instance.stubs(:save_last_run_summary) # to prevent it from trying to write files end it "should set the code to run from --code" do @apply.options.stubs(:[]).with(:code).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @apply.main end it "should set the code to run from STDIN if no arguments" do @apply.command_line.stubs(:args).returns([]) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @apply.main end it "should set the manifest if a file is passed on command line and the file exists" do File.stubs(:exist?).with('site.pp').returns true @apply.command_line.stubs(:args).returns(['site.pp']) Puppet.expects(:[]=).with(:manifest,"site.pp") @apply.main end it "should raise an error if a file is passed on command line and the file does not exist" do File.stubs(:exist?).with('noexist.pp').returns false @apply.command_line.stubs(:args).returns(['noexist.pp']) lambda { @apply.main }.should raise_error(RuntimeError, 'Could not find file noexist.pp') end it "should set the manifest to the first file and warn other files will be skipped" do File.stubs(:exist?).with('starwarsIV').returns true File.expects(:exist?).with('starwarsI').never @apply.command_line.stubs(:args).returns(['starwarsIV', 'starwarsI', 'starwarsII']) Puppet.expects(:[]=).with(:manifest,"starwarsIV") Puppet.expects(:warning).with('Only one file can be applied per run. Skipping starwarsI, starwarsII') @apply.main end it "should collect the node facts" do Puppet::Node::Facts.expects(:find).returns(@facts) @apply.main end it "should raise an error if we can't find the node" do Puppet::Node::Facts.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should look for the node" do Puppet::Node.expects(:find).returns(@node) @apply.main end it "should raise an error if we can't find the node" do Puppet::Node.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should merge in our node the loaded facts" do @facts.stubs(:values).returns("values") @node.expects(:merge).with("values") @apply.main end it "should load custom classes if loadclasses" do @apply.options.stubs(:[]).with(:loadclasses).returns(true) Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") @node.expects(:classes=) @apply.main end it "should compile the catalog" do Puppet::Resource::Catalog.expects(:find).returns(@catalog) @apply.main end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) @apply.main end it "should finalize the catalog" do @catalog.expects(:finalize) @apply.main end it "should call the prerun and postrun commands on a Configurer instance" do - configurer = stub 'configurer' - - Puppet::Configurer.expects(:new).returns configurer - configurer.expects(:execute_prerun_command) - configurer.expects(:execute_postrun_command) + Puppet::Configurer.any_instance.expects(:execute_prerun_command) + Puppet::Configurer.any_instance.expects(:execute_postrun_command) @apply.main end it "should apply the catalog" do @catalog.expects(:apply).returns(stub_everything('transaction')) @apply.main end describe "with detailed_exitcodes" do it "should exit with report's computed exit status" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) - report = stub 'report', :exit_status => 666 - @transaction.stubs(:report).returns(report) + Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) @apply.expects(:exit).with(666) @apply.main end it "should always exit with 0 if option is disabled" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(false) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(0) @apply.main end it "should always exit with 0 if --noop" do Puppet.stubs(:[]).with(:noop).returns(true) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(0) @apply.main end end end describe "the 'apply' command" do it "should read the catalog in from disk if a file name is provided" do @apply.options[:catalog] = "/my/catalog.pson" File.expects(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should read the catalog in from stdin if '-' is provided" do @apply.options[:catalog] = "-" $stdin.expects(:read).returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should deserialize the catalog from the default format" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should fail helpfully if deserializing fails" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" lambda { @apply.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) @apply.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog catalog.expects(:to_ral).returns "mycatalog" configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:run).with(:catalog => "mycatalog") @apply.apply end end end end diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb index ebc5768ea..3b2a44f0f 100755 --- a/spec/unit/configurer_spec.rb +++ b/spec/unit/configurer_spec.rb @@ -1,494 +1,486 @@ #!/usr/bin/env ruby # # Created by Luke Kanies on 2007-11-12. # Copyright (c) 2007. All rights reserved. require File.dirname(__FILE__) + '/../spec_helper' require 'puppet/configurer' describe Puppet::Configurer do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new end it "should include the Plugin Handler module" do Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::PluginHandler) end it "should include the Fact Handler module" do Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::FactHandler) end it "should use the puppetdlockfile as its lockfile path" do Puppet.settings.expects(:value).with(:puppetdlockfile).returns("/my/lock") Puppet::Configurer.lockfile_path.should == "/my/lock" end describe "when executing a pre-run hook" do it "should do nothing if the hook is set to an empty string" do Puppet.settings[:prerun_command] = "" Puppet::Util.expects(:exec).never @agent.execute_prerun_command end it "should execute any pre-run command provided via the 'prerun_command' setting" do Puppet.settings[:prerun_command] = "/my/command" Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } @agent.execute_prerun_command end it "should fail if the command fails" do Puppet.settings[:prerun_command] = "/my/command" Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure lambda { @agent.execute_prerun_command }.should raise_error(Puppet::Configurer::CommandHookError) end end describe "when executing a post-run hook" do it "should do nothing if the hook is set to an empty string" do Puppet.settings[:postrun_command] = "" Puppet::Util.expects(:exec).never @agent.execute_postrun_command end it "should execute any post-run command provided via the 'postrun_command' setting" do Puppet.settings[:postrun_command] = "/my/command" Puppet::Util.expects(:execute).with { |args| args[0] == "/my/command" } @agent.execute_postrun_command end it "should fail if the command fails" do Puppet.settings[:postrun_command] = "/my/command" Puppet::Util.expects(:execute).raises Puppet::ExecutionFailure lambda { @agent.execute_postrun_command }.should raise_error(Puppet::Configurer::CommandHookError) end end end -describe Puppet::Configurer, "when initializing a report" do - it "should return an instance of a transaction report" do - Puppet.settings.stubs(:use).returns(true) - @agent = Puppet::Configurer.new - @agent.initialize_report.should be_instance_of(Puppet::Transaction::Report) - end -end - describe Puppet::Configurer, "when executing a catalog run" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:prepare) @agent.stubs(:facts_for_uploading).returns({}) @catalog = Puppet::Resource::Catalog.new @catalog.stubs(:apply) @agent.stubs(:retrieve_catalog).returns @catalog Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:close) end it "should prepare for the run" do @agent.expects(:prepare) @agent.run end it "should initialize a transaction report if one is not provided" do - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns report @agent.run end it "should pass the new report to the catalog" do - report = stub 'report' - @agent.stubs(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.stubs(:new).returns report @catalog.expects(:apply).with{|options| options[:report] == report} @agent.run end it "should use the provided report if it was passed one" do - report = stub 'report' - @agent.expects(:initialize_report).never + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).never @catalog.expects(:apply).with{|options| options[:report] == report} @agent.run(:report => report) end it "should set the report as a log destination" do - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns report Puppet::Util::Log.expects(:newdestination).with(report) @agent.run end it "should retrieve the catalog" do @agent.expects(:retrieve_catalog) @agent.run end it "should log a failure and do nothing if no catalog can be retrieved" do @agent.expects(:retrieve_catalog).returns nil Puppet.expects(:err).with "Could not retrieve catalog; skipping run" @agent.run end it "should apply the catalog with all options to :run" do @agent.expects(:retrieve_catalog).returns @catalog @catalog.expects(:apply).with { |args| args[:one] == true } @agent.run :one => true end it "should accept a catalog and use it instead of retrieving a different one" do @agent.expects(:retrieve_catalog).never @catalog.expects(:apply) @agent.run :one => true, :catalog => @catalog end it "should benchmark how long it takes to apply the catalog" do @agent.expects(:benchmark).with(:notice, "Finished catalog run") @agent.expects(:retrieve_catalog).returns @catalog @catalog.expects(:apply).never # because we're not yielding @agent.run end it "should execute post-run hooks after the run" do @agent.expects(:execute_postrun_command) @agent.run end it "should send the report" do - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report).with { |r, trans| r == report } @agent.run end it "should send the transaction report with a reference to the transaction if a run was actually made" do - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns(report) trans = stub 'transaction' @catalog.expects(:apply).returns trans @agent.expects(:send_report).with { |r, t| t == trans } @agent.run :catalog => @catalog end it "should send the transaction report even if the catalog could not be retrieved" do @agent.expects(:retrieve_catalog).returns nil - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report) @agent.run end it "should send the transaction report even if there is a failure" do @agent.expects(:retrieve_catalog).raises "whatever" - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report) lambda { @agent.run }.should raise_error end it "should remove the report as a log destination when the run is finished" do - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns(report) Puppet::Util::Log.expects(:close).with(report) @agent.run end it "should return the report as the result of the run" do - report = stub 'report' - @agent.expects(:initialize_report).returns report + report = Puppet::Transaction::Report.new("apply") + Puppet::Transaction::Report.expects(:new).returns(report) @agent.run.should equal(report) end end describe Puppet::Configurer, "when sending a report" do before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new - @report = stub 'report' + @report = Puppet::Transaction::Report.new("apply") @trans = stub 'transaction' end it "should require a report" do lambda { @configurer.send_report }.should raise_error(ArgumentError) end it "should allow specification of a transaction" do lambda { @configurer.send_report(@report, @trans) }.should_not raise_error(ArgumentError) end it "should use any provided transaction to add metrics to the report" do @trans.expects(:generate_report) @configurer.send_report(@report, @trans) end it "should print a report summary if configured to do so" do Puppet.settings[:summarize] = true @report.expects(:summary).returns "stuff" @configurer.expects(:puts).with("stuff") @configurer.send_report(@report) end it "should not print a report summary if not configured to do so" do Puppet.settings[:summarize] = false @configurer.expects(:puts).never @configurer.send_report(@report) end it "should save the report if reporting is enabled" do Puppet.settings[:report] = true @report.expects(:save) @configurer.send_report(@report) end it "should not save the report if reporting is disabled" do Puppet.settings[:report] = false @report.expects(:save).never @configurer.send_report(@report) end it "should log but not fail if saving the report fails" do Puppet.settings[:report] = true @report.expects(:save).raises "whatever" Puppet.expects(:err) lambda { @configurer.send_report(@report) }.should_not raise_error end end describe Puppet::Configurer, "when retrieving a catalog" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:facts_for_uploading).returns({}) @catalog = Puppet::Resource::Catalog.new # this is the default when using a Configurer instance Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :rest @agent.stubs(:convert_catalog).returns @catalog end describe "and configured to only retrieve a catalog from the cache" do before do Puppet.settings[:use_cached_catalog] = true end it "should first look in the cache for a catalog" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.never @agent.retrieve_catalog.should == @catalog end it "should compile a new catalog if none is found in the cache" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog @agent.retrieve_catalog.should == @catalog end end describe "when not using a REST terminus for catalogs" do it "should not pass any facts when retrieving the catalog" do @agent.expects(:facts_for_uploading).never Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:facts].nil? }.returns @catalog @agent.retrieve_catalog end end describe "when using a REST terminus for catalogs" do it "should pass the prepared facts and the facts format as arguments when retrieving the catalog" do @agent.expects(:facts_for_uploading).returns(:facts => "myfacts", :facts_format => :foo) Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:facts] == "myfacts" and options[:facts_format] == :foo }.returns @catalog @agent.retrieve_catalog end end it "should use the Catalog class to get its catalog" do Puppet::Resource::Catalog.expects(:find).returns @catalog @agent.retrieve_catalog end it "should use its certname to retrieve the catalog" do Facter.stubs(:value).returns "eh" Puppet.settings[:certname] = "myhost.domain.com" Puppet::Resource::Catalog.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog @agent.retrieve_catalog end it "should default to returning a catalog retrieved directly from the server, skipping the cache" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog @agent.retrieve_catalog.should == @catalog end it "should log and return the cached catalog when no catalog can be retrieved from the server" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog Puppet.expects(:notice) @agent.retrieve_catalog.should == @catalog end it "should not look in the cache for a catalog if one is returned from the server" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never @agent.retrieve_catalog.should == @catalog end it "should return the cached catalog when retrieving the remote catalog throws an exception" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh" Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog @agent.retrieve_catalog.should == @catalog end it "should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled" do Puppet.stubs(:[]) Puppet.expects(:[]).with(:usecacheonfailure).returns false Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet.expects(:warning) @agent.retrieve_catalog.should be_nil end it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil @agent.retrieve_catalog.should be_nil end it "should convert the catalog before returning" do Puppet::Resource::Catalog.stubs(:find).returns @catalog @agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog" @agent.retrieve_catalog.should == "converted catalog" end it "should return nil if there is an error while retrieving the catalog" do Puppet::Resource::Catalog.expects(:find).at_least_once.raises "eh" @agent.retrieve_catalog.should be_nil end end describe Puppet::Configurer, "when converting the catalog" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @catalog = Puppet::Resource::Catalog.new @oldcatalog = stub 'old_catalog', :to_ral => @catalog end it "should convert the catalog to a RAL-formed catalog" do @oldcatalog.expects(:to_ral).returns @catalog @agent.convert_catalog(@oldcatalog, 10).should equal(@catalog) end it "should finalize the catalog" do @catalog.expects(:finalize) @agent.convert_catalog(@oldcatalog, 10) end it "should record the passed retrieval time with the RAL catalog" do @catalog.expects(:retrieval_duration=).with 10 @agent.convert_catalog(@oldcatalog, 10) end it "should write the RAL catalog's class file" do @catalog.expects(:write_class_file) @agent.convert_catalog(@oldcatalog, 10) end end describe Puppet::Configurer, "when preparing for a run" do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:dostorage) @agent.stubs(:download_fact_plugins) @agent.stubs(:download_plugins) @agent.stubs(:execute_prerun_command) @facts = {"one" => "two", "three" => "four"} end it "should initialize the metadata store" do @agent.class.stubs(:facts).returns(@facts) @agent.expects(:dostorage) - @agent.prepare + @agent.prepare({}) end it "should download fact plugins" do @agent.expects(:download_fact_plugins) - @agent.prepare + @agent.prepare({}) end it "should download plugins" do @agent.expects(:download_plugins) - @agent.prepare + @agent.prepare({}) end it "should perform the pre-run commands" do @agent.expects(:execute_prerun_command) - @agent.prepare + @agent.prepare({}) end end diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb index c814975df..70742f7dc 100644 --- a/spec/unit/reports/http_spec.rb +++ b/spec/unit/reports/http_spec.rb @@ -1,56 +1,56 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } require 'puppet/reports' # FakeHTTP fakes the behavior of Net::HTTP#request and acts as a sensor for an # otherwise difficult to trace method call. # class FakeHTTP REQUESTS = {} def self.request(req) REQUESTS[req.path] = req end end processor = Puppet::Reports.report(:http) describe processor do before { Net::HTTP.any_instance.stubs(:start).yields(FakeHTTP) } - subject { Puppet::Transaction::Report.new.extend(processor) } + subject { Puppet::Transaction::Report.new("apply").extend(processor) } it { should respond_to(:process) } it "should use the reporturl setting's host and port" do uri = URI.parse(Puppet[:reporturl]) Net::HTTP.expects(:new).with(uri.host, uri.port).returns(stub_everything('http')) subject.process end describe "request" do before { subject.process } describe "path" do it "should use the path specified by the 'reporturl' setting" do reports_request.path.should == URI.parse(Puppet[:reporturl]).path end end describe "body" do it "should be the report as YAML" do reports_request.body.should == subject.to_yaml end end describe "content type" do it "should be 'application/x-yaml'" do reports_request.content_type.should == "application/x-yaml" end end end private def reports_request; FakeHTTP::REQUESTS[URI.parse(Puppet[:reporturl]).path] end end diff --git a/spec/unit/reports/tagmail_spec.rb b/spec/unit/reports/tagmail_spec.rb index bdb16600e..1dadfc7cd 100755 --- a/spec/unit/reports/tagmail_spec.rb +++ b/spec/unit/reports/tagmail_spec.rb @@ -1,95 +1,95 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } require 'puppet/reports' require 'puppettest/support/utils' tagmail = Puppet::Reports.report(:tagmail) describe tagmail do extend PuppetTest::Support::Utils before do - @processor = Puppet::Transaction::Report.new + @processor = Puppet::Transaction::Report.new("apply") @processor.extend(Puppet::Reports.report(:tagmail)) end passers = File.join(datadir, "reports", "tagmail_passers.conf") File.readlines(passers).each do |line| it "should be able to parse '#{line.inspect}'" do @processor.parse(line) end end failers = File.join(datadir, "reports", "tagmail_failers.conf") File.readlines(failers).each do |line| it "should not be able to parse '#{line.inspect}'" do lambda { @processor.parse(line) }.should raise_error(ArgumentError) end end { "tag: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, []], "tag.localhost: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag.localhost}, []], "tag, other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag other}, []], "tag-other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag-other}, []], "tag, !other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, %w{other}], "tag, !other, one, !two: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag one}, %w{other two}], "tag: abuse@domain.com, other@domain.com" => [%w{abuse@domain.com other@domain.com}, %w{tag}, []] }.each do |line, results| it "should parse '#{line}' as #{results.inspect}" do @processor.parse(line).shift.should == results end end describe "when matching logs" do before do @processor << Puppet::Util::Log.new(:level => :notice, :message => "first", :tags => %w{one}) @processor << Puppet::Util::Log.new(:level => :notice, :message => "second", :tags => %w{one two}) @processor << Puppet::Util::Log.new(:level => :notice, :message => "third", :tags => %w{one two three}) end def match(pos = [], neg = []) pos = Array(pos) neg = Array(neg) result = @processor.match([[%w{abuse@domain.com}, pos, neg]]) actual_result = result.shift if actual_result actual_result[1] else nil end end it "should match all messages when provided the 'all' tag as a positive matcher" do results = match("all") %w{first second third}.each do |str| results.should be_include(str) end end it "should remove messages that match a negated tag" do match("all", "three").should_not be_include("third") end it "should find any messages tagged with a provided tag" do results = match("two") results.should be_include("second") results.should be_include("third") results.should_not be_include("first") end it "should allow negation of specific tags from a specific tag list" do results = match("two", "three") results.should be_include("second") results.should_not be_include("third") end it "should allow a tag to negate all matches" do results = match([], "one") results.should be_nil end end end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 77f82159b..604c2f54d 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -1,242 +1,248 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/transaction/report' describe Puppet::Transaction::Report do before do Puppet::Util::Storage.stubs(:store) end it "should set its host name to the certname" do Puppet.settings.expects(:value).with(:certname).returns "myhost" - Puppet::Transaction::Report.new.host.should == "myhost" + Puppet::Transaction::Report.new("apply").host.should == "myhost" end it "should return its host name as its name" do - r = Puppet::Transaction::Report.new + r = Puppet::Transaction::Report.new("apply") r.name.should == r.host end it "should create an initialization timestamp" do Time.expects(:now).returns "mytime" - Puppet::Transaction::Report.new.time.should == "mytime" - end - - it "should have a default 'kind' of 'apply'" do - Puppet::Transaction::Report.new.kind.should == "apply" + Puppet::Transaction::Report.new("apply").time.should == "mytime" end it "should take a 'kind' as an argument" do Puppet::Transaction::Report.new("inspect").kind.should == "inspect" end + it "should take a 'configuration_version' as an argument" do + Puppet::Transaction::Report.new("inspect", "some configuration version").configuration_version.should == "some configuration version" + end + + it "should be able to set configuration_version" do + report = Puppet::Transaction::Report.new("inspect") + report.configuration_version = "some version" + report.configuration_version.should == "some version" + end + describe "when accepting logs" do before do - @report = Puppet::Transaction::Report.new + @report = Puppet::Transaction::Report.new("apply") end it "should add new logs to the log list" do @report << "log" @report.logs[-1].should == "log" end it "should return self" do r = @report << "log" r.should equal(@report) end end describe "when accepting resource statuses" do before do - @report = Puppet::Transaction::Report.new + @report = Puppet::Transaction::Report.new("apply") end it "should add each status to its status list" do status = stub 'status', :resource => "foo" @report.add_resource_status status @report.resource_statuses["foo"].should equal(status) end end describe "when using the indirector" do it "should redirect :find to the indirection" do @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) @indirection.expects(:find) Puppet::Transaction::Report.find(:report) end it "should redirect :save to the indirection" do Facter.stubs(:value).returns("eh") @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") @indirection.expects(:save) report.save end it "should default to the 'processor' terminus" do Puppet::Transaction::Report.indirection.terminus_class.should == :processor end it "should delegate its name attribute to its host method" do - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") report.expects(:host).returns "me" report.name.should == "me" end after do Puppet::Util::Cacher.expire end end describe "when computing exit status" do it "should produce 2 if changes are present" do - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {:total => 1}) report.add_metric("resources", {:failed => 0}) report.exit_status.should == 2 end it "should produce 4 if failures are present" do - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {:total => 0}) report.add_metric("resources", {:failed => 1}) report.exit_status.should == 4 end it "should produce 6 if both changes and failures are present" do - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {:total => 1}) report.add_metric("resources", {:failed => 1}) report.exit_status.should == 6 end end describe "when calculating metrics" do before do - @report = Puppet::Transaction::Report.new + @report = Puppet::Transaction::Report.new("apply") end def metric(name, value) if metric = @report.metrics[name.to_s] metric[value] else nil end end def add_statuses(count, type = :file) 3.times do |i| status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => "/my/path#{i}")) yield status if block_given? @report.add_resource_status status end end [:time, :resources, :changes, :events].each do |type| it "should add #{type} metrics" do @report.calculate_metrics @report.metrics[type.to_s].should be_instance_of(Puppet::Transaction::Metric) end end describe "for resources" do it "should provide the total number of resources" do add_statuses(3) @report.calculate_metrics metric(:resources, :total).should == 3 end Puppet::Resource::Status::STATES.each do |state| it "should provide the number of #{state} resources as determined by the status objects" do add_statuses(3) { |status| status.send(state.to_s + "=", true) } @report.calculate_metrics metric(:resources, state).should == 3 end end end describe "for changes" do it "should provide the number of changes from the resource statuses" do add_statuses(3) { |status| status.change_count = 3 } @report.calculate_metrics metric(:changes, :total).should == 9 end end describe "for times" do it "should provide the total amount of time for each resource type" do add_statuses(3, :file) do |status| status.evaluation_time = 1 end add_statuses(3, :exec) do |status| status.evaluation_time = 2 end add_statuses(3, :mount) do |status| status.evaluation_time = 3 end @report.calculate_metrics metric(:time, "file").should == 3 metric(:time, "exec").should == 6 metric(:time, "mount").should == 9 end it "should add any provided times from external sources" do @report.add_times :foobar, 50 @report.calculate_metrics metric(:time, "foobar").should == 50 end end describe "for events" do it "should provide the total number of events" do add_statuses(3) do |status| 3.times { |i| status.add_event(Puppet::Transaction::Event.new) } end @report.calculate_metrics metric(:events, :total).should == 9 end Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name| it "should provide the number of #{status_name} events" do add_statuses(3) do |status| 3.times do |i| event = Puppet::Transaction::Event.new event.status = status_name status.add_event(event) end end @report.calculate_metrics metric(:events, status_name).should == 9 end end end end describe "when producing a summary" do before do resource = Puppet::Type.type(:notify).new(:name => "testing") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource trans = catalog.apply @report = trans.report @report.calculate_metrics end %w{Changes Total Resources}.each do |main| it "should include information on #{main} in the summary" do @report.summary.should be_include(main) end end end end diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index 2df4404be..566c90438 100755 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -1,452 +1,452 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../spec_helper' require 'puppet/transaction' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Transaction do before do @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) end it "should delegate its event list to the event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.expects(:events).returns %w{my events} @transaction.events.should == %w{my events} end it "should delegate adding times to its report" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.report.expects(:add_times).with(:foo, 10) @transaction.report.expects(:add_times).with(:bar, 20) @transaction.add_times :foo => 10, :bar => 20 end it "should be able to accept resource status instances" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource).should equal(status) end it "should be able to look resource status up by resource reference" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource.to_s).should equal(status) end # This will basically only ever be used during testing. it "should automatically create resource statuses if asked for a non-existent status" do resource = Puppet::Type.type(:notify).new :title => "foobar" @transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status) end it "should add provided resource statuses to its report" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.report.resource_statuses[resource.to_s].should equal(status) end it "should calculate metrics on and report the report when asked to generate a report" do @transaction.report.expects(:calculate_metrics) @transaction.generate_report.should equal(@transaction.report) end it "should consider a resource to be failed if a status instance exists for that resource and indicates it is failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_failed(resource) end it "should not consider a resource to be failed if a status instance exists for that resource but indicates it is not failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_failed(resource) end it "should consider there to be failed resources if any statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_any_failed end it "should not consider there to be failed resources if no statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_any_failed end it "should be possible to replace the report object" do - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") @transaction.report = report @transaction.report.should == report end it "should consider a resource to have failed dependencies if any of its dependencies are failed" describe "when initializing" do it "should create an event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager) @transaction.event_manager.transaction.should equal(@transaction) end it "should create a resource harness" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness) @transaction.resource_harness.transaction.should equal(@transaction) end end describe "when evaluating a resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.stubs(:eval_children_and_apply_resource) @transaction.stubs(:skip?).returns false @resource = Puppet::Type.type(:file).new :path => @basepath end it "should check whether the resource should be skipped" do @transaction.expects(:skip?).with(@resource).returns false @transaction.eval_resource(@resource) end it "should eval and apply children" do @transaction.expects(:eval_children_and_apply_resource).with(@resource, nil) @transaction.eval_resource(@resource) end it "should process events" do @transaction.event_manager.expects(:process_events).with(@resource) @transaction.eval_resource(@resource) end describe "and the resource should be skipped" do before do @transaction.expects(:skip?).with(@resource).returns true end it "should mark the resource's status as skipped" do @transaction.eval_resource(@resource) @transaction.resource_status(@resource).should be_skipped end end end describe "when applying a resource" do before do @resource = Puppet::Type.type(:file).new :path => @basepath @status = Puppet::Resource::Status.new(@resource) @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.stubs(:queue_events) @transaction.resource_harness.stubs(:evaluate).returns(@status) end it "should use its resource harness to apply the resource" do @transaction.resource_harness.expects(:evaluate).with(@resource) @transaction.apply(@resource) end it "should add the resulting resource status to its status list" do @transaction.apply(@resource) @transaction.resource_status(@resource).should be_instance_of(Puppet::Resource::Status) end it "should queue any events added to the resource status" do @status.expects(:events).returns %w{a b} @transaction.event_manager.expects(:queue_events).with(@resource, ["a", "b"]) @transaction.apply(@resource) end it "should log and skip any resources that cannot be applied" do @transaction.resource_harness.expects(:evaluate).raises ArgumentError @resource.expects(:err) @transaction.apply(@resource) @transaction.report.resource_statuses[@resource.to_s].should be_nil end end describe "when generating resources" do it "should call 'generate' on all created resources" do first = Puppet::Type.type(:notify).new(:name => "first") second = Puppet::Type.type(:notify).new(:name => "second") third = Puppet::Type.type(:notify).new(:name => "third") @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) first.expects(:generate).returns [second] second.expects(:generate).returns [third] third.expects(:generate) @transaction.generate_additional_resources(first, :generate) end it "should finish all resources" do generator = stub 'generator', :depthfirst? => true, :tags => [] resource = stub 'resource', :tag => nil @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.expects(:generate).returns [resource] @catalog.expects(:add_resource).yields(resource) resource.expects(:finish) @transaction.generate_additional_resources(generator, :generate) end it "should skip generated resources that conflict with existing resources" do generator = mock 'generator', :tags => [] resource = stub 'resource', :tag => nil @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.expects(:generate).returns [resource] @catalog.expects(:add_resource).raises(Puppet::Resource::Catalog::DuplicateResourceError.new("foo")) resource.expects(:finish).never resource.expects(:info) # log that it's skipped @transaction.generate_additional_resources(generator, :generate).should be_empty end it "should copy all tags to the newly generated resources" do child = stub 'child' generator = stub 'resource', :tags => ["one", "two"] @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.stubs(:generate).returns [child] @catalog.stubs(:add_resource) child.expects(:tag).with("one", "two") @transaction.generate_additional_resources(generator, :generate) end end describe "when skipping a resource" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should skip resource with missing tags" do @transaction.stubs(:missing_tags?).returns(true) @transaction.should be_skip(@resource) end it "should skip unscheduled resources" do @transaction.stubs(:scheduled?).returns(false) @transaction.should be_skip(@resource) end it "should skip resources with failed dependencies" do @transaction.stubs(:failed_dependencies?).returns(true) @transaction.should be_skip(@resource) end it "should skip virtual resource" do @resource.stubs(:virtual?).returns true @transaction.should be_skip(@resource) end end describe "when determining if tags are missing" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) @transaction.stubs(:ignore_tags?).returns false end it "should not be missing tags if tags are being ignored" do @transaction.expects(:ignore_tags?).returns true @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should not be missing tags if the transaction tags are empty" do @transaction.tags = [] @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should otherwise let the resource determine if it is missing tags" do tags = ['one', 'two'] @transaction.tags = tags @resource.expects(:tagged?).with(*tags).returns(false) @transaction.should be_missing_tags(@resource) end end describe "when determining if a resource should be scheduled" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should always schedule resources if 'ignoreschedules' is set" do @transaction.ignoreschedules = true @transaction.resource_harness.expects(:scheduled?).never @transaction.should be_scheduled(@resource) end it "should let the resource harness determine whether the resource should be scheduled" do @transaction.resource_harness.expects(:scheduled?).with(@transaction.resource_status(@resource), @resource).returns "feh" @transaction.scheduled?(@resource).should == "feh" end end describe "when prefetching" do it "should match resources by name, not title" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) # Have both a title and name resource = Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh" @catalog.add_resource resource resource.provider.class.expects(:prefetch).with("bar" => resource) @transaction.prefetch end end it "should return all resources for which the resource status indicates the resource has changed when determinig changed resources" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) names = [] 2.times do |i| name = File.join(@basepath, "file#{i}") resource = Puppet::Type.type(:file).new :path => name names << resource.to_s @catalog.add_resource resource @transaction.add_resource_status Puppet::Resource::Status.new(resource) end @transaction.resource_status(names[0]).changed = true @transaction.changed?.should == [@catalog.resource(names[0])] end describe 'when checking application run state' do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should return true for :stop_processing? if Puppet::Application.stop_requested? is true' do Puppet::Application.stubs(:stop_requested?).returns(true) @transaction.stop_processing?.should be_true end it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do Puppet::Application.stubs(:stop_requested?).returns(false) @transaction.stop_processing?.should be_false end describe 'within an evaluate call' do before do @resource = stub 'resource', :ref => 'some_ref' @catalog.add_resource @resource @transaction.stubs(:prepare) @transaction.sorted_resources = [@resource] end it 'should stop processing if :stop_processing? is true' do @transaction.expects(:stop_processing?).returns(true) @transaction.expects(:eval_resource).never @transaction.evaluate end it 'should continue processing if :stop_processing? is false' do @transaction.expects(:stop_processing?).returns(false) @transaction.expects(:eval_resource).returns(nil) @transaction.evaluate end end end end describe Puppet::Transaction, " when determining tags" do before do @config = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@config) end it "should default to the tags specified in the :tags setting" do Puppet.expects(:[]).with(:tags).returns("one") @transaction.tags.should == %w{one} end it "should split tags based on ','" do Puppet.expects(:[]).with(:tags).returns("one,two") @transaction.tags.should == %w{one two} end it "should use any tags set after creation" do Puppet.expects(:[]).with(:tags).never @transaction.tags = %w{one two} @transaction.tags.should == %w{one two} end it "should always convert assigned tags to an array" do @transaction.tags = "one::two" @transaction.tags.should == %w{one::two} end it "should accept a comma-delimited string" do @transaction.tags = "one, two" @transaction.tags.should == %w{one two} end it "should accept an empty string" do @transaction.tags = "" @transaction.tags.should == [] end end diff --git a/test/lib/puppettest/reporttesting.rb b/test/lib/puppettest/reporttesting.rb index 448a6a9d8..54a2f6799 100644 --- a/test/lib/puppettest/reporttesting.rb +++ b/test/lib/puppettest/reporttesting.rb @@ -1,16 +1,16 @@ module PuppetTest::Reporttesting def fakereport # Create a bunch of log messages in an array. - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") 3.times { |i| # We have to use warning so that the logs always happen log = Puppet.warning("Report test message #{i}") report << log } report end end diff --git a/test/other/report.rb b/test/other/report.rb index 8a909b41c..eacf1632b 100755 --- a/test/other/report.rb +++ b/test/other/report.rb @@ -1,135 +1,135 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../lib/puppettest' require 'puppet' require 'puppet/reports' require 'puppet/transaction/report' require 'puppettest' require 'puppettest/reporttesting' class TestReports < Test::Unit::TestCase include PuppetTest include PuppetTest::Reporttesting def mkreport # First do some work objects = [] 6.times do |i| file = tempfile # Make every third file File.open(file, "w") { |f| f.puts "" } if i % 3 == 0 objects << Puppet::Type.type(:file).new( :path => file, :ensure => "file" ) end config = mk_catalog(*objects) # So the report works out. config.retrieval_duration = 0.001 trans = config.apply trans.generate_report end # Make sure we can use reports as log destinations. def test_reports_as_log_destinations report = fakereport assert_nothing_raised { Puppet::Util::Log.newdestination(report) } # Now make a file for testing logging file = Puppet::Type.type(:file).new(:path => tempfile, :ensure => "file") file.finish log = nil assert_nothing_raised { log = file.log "This is a message, yo" } assert(report.logs.include?(log), "Report did not get log message") assert_nothing_raised { Puppet::Util::Log.close(report) } log = file.log "This is another message, yo" assert(! report.logs.include?(log), "Report got log message after close") end def test_store_report # Create a bunch of log messages in an array. - report = Puppet::Transaction::Report.new + report = Puppet::Transaction::Report.new("apply") # We have to reuse reporting here because of something going on in the # server/report.rb file Puppet.settings.use(:main, :master) 3.times { |i| log = Puppet.warning("Report test message #{i}") report << log } assert_nothing_raised do report.extend(Puppet::Reports.report(:store)) end yaml = YAML.dump(report) file = report.process assert(FileTest.exists?(file), "report file did not get created") assert_equal(yaml, File.read(file), "File did not get written") end if Puppet.features.rrd? || Puppet.features.rrd_legacy? def test_rrdgraph_report Puppet.settings.use(:main, :metrics) report = mkreport assert(! report.metrics.empty?, "Did not receive any metrics") assert_nothing_raised do report.extend(Puppet::Reports.report(:rrdgraph)) end assert_nothing_raised { report.process } hostdir = nil assert_nothing_raised do hostdir = report.hostdir end assert(hostdir, "Did not get hostdir back") assert(FileTest.directory?(hostdir), "Host rrd dir did not get created") index = File.join(hostdir, "index.html") assert(FileTest.exists?(index), "index file was not created") # Now make sure it creaets each of the rrd files %w{changes resources time}.each do |type| file = File.join(hostdir, "#{type}.rrd") assert(FileTest.exists?(file), "Did not create rrd file for #{type}") daily = file.sub ".rrd", "-daily.png" assert(FileTest.exists?(daily), "Did not make daily graph for #{type}") end end else $stderr.puts "Install RRD for metric reporting tests" end end