diff options
author | Nick Lewis <nick@puppetlabs.com> | 2010-12-02 10:49:13 -0800 |
---|---|---|
committer | Nick Lewis <nick@puppetlabs.com> | 2010-12-02 15:09:43 -0800 |
commit | 1131ad73078c3e3022b23ee8ab756b44c7de27c9 (patch) | |
tree | a0b8a00966ad0a8a6b38186bc6e8f787be1d29a5 | |
parent | af6e08c0a59db951502d0cf8c0ca24f5001e92f1 (diff) | |
download | puppet-1131ad73078c3e3022b23ee8ab756b44c7de27c9.tar.gz puppet-1131ad73078c3e3022b23ee8ab756b44c7de27c9.tar.xz puppet-1131ad73078c3e3022b23ee8ab756b44c7de27c9.zip |
(#4943) Add puppet inspect application
"puppet inspect" will load the locally stored YAML catalog and record
the current state of the audited properties in the catalog. It uses
settings specified in the [agent] configuration section, and will send
its inspect report to the specified server.
Paired-With: Jesse Wolfe
-rw-r--r-- | lib/puppet/application/inspect.rb | 80 | ||||
-rw-r--r-- | lib/puppet/transaction/report.rb | 5 | ||||
-rw-r--r-- | spec/unit/application/inspect_spec.rb | 79 | ||||
-rwxr-xr-x | spec/unit/transaction/report_spec.rb | 8 |
4 files changed, 170 insertions, 2 deletions
diff --git a/lib/puppet/application/inspect.rb b/lib/puppet/application/inspect.rb new file mode 100644 index 000000000..c28fef326 --- /dev/null +++ b/lib/puppet/application/inspect.rb @@ -0,0 +1,80 @@ +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 + + 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/transaction/report.rb b/lib/puppet/transaction/report.rb index e6d1e0528..75c08fc7a 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -10,7 +10,7 @@ class Puppet::Transaction::Report indirects :report, :terminus_class => :processor - attr_reader :resource_statuses, :logs, :metrics, :host, :time + 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) @@ -49,13 +49,14 @@ class Puppet::Transaction::Report calculate_event_metrics end - def initialize + def initialize(kind = "apply") @metrics = {} @logs = [] @resource_statuses = {} @external_times ||= {} @host = Puppet[:certname] @time = Time.now + @kind = kind end def name diff --git a/spec/unit/application/inspect_spec.rb b/spec/unit/application/inspect_spec.rb new file mode 100644 index 000000000..a3cc74d86 --- /dev/null +++ b/spec/unit/application/inspect_spec.rb @@ -0,0 +1,79 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/application/inspect' +require 'puppet/resource/catalog' +require 'puppet/indirector/catalog/yaml' +require 'puppet/indirector/report/rest' + +describe Puppet::Application::Inspect do + before :each do + @inspect = Puppet::Application[:inspect] + end + + describe "during setup" do + it "should print its configuration if asked" do + Puppet[:configprint] = "all" + + Puppet.settings.expects(:print_configs).returns(true) + lambda { @inspect.setup }.should raise_error(SystemExit) + end + + it "should fail if reporting is turned off" do + Puppet[:report] = false + lambda { @inspect.setup }.should raise_error(/report=true/) + end + end + + describe "when executing" do + before :each do + Puppet[:report] = true + Puppet::Util::Log.stubs(:newdestination) + Puppet::Transaction::Report::Rest.any_instance.stubs(:save) + @inspect.setup + end + + it "should retrieve the local catalog" do + Puppet::Resource::Catalog::Yaml.any_instance.expects(:find).with {|request| request.key == Puppet[:certname] }.returns(Puppet::Resource::Catalog.new) + + @inspect.run_command + end + + it "should save the report to REST" do + Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(Puppet::Resource::Catalog.new) + Puppet::Transaction::Report::Rest.any_instance.expects(:save).with {|request| request.instance.host == Puppet[:certname] } + + @inspect.run_command + end + + it "should audit the specified properties" do + catalog = Puppet::Resource::Catalog.new + file = Tempfile.new("foo") + file.puts("file contents") + file.flush + resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) + catalog.add_resource(resource) + Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) + + events = nil + + Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| + events = request.instance.resource_statuses.values.first.events + end + + @inspect.run_command + + properties = events.inject({}) do |property_values, event| + property_values.merge(event.property => event.previous_value) + end + properties["ensure"].should == :file + properties["content"].should == "{md5}#{Digest::MD5.hexdigest("file contents\n")}" + end + end + + after :all do + Puppet::Resource::Catalog.indirection.reset_terminus_class + Puppet::Transaction::Report.indirection.terminus_class = :processor + end +end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 7e0b0554b..77f82159b 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -24,6 +24,14 @@ describe Puppet::Transaction::Report do Puppet::Transaction::Report.new.time.should == "mytime" end + it "should have a default 'kind' of 'apply'" do + Puppet::Transaction::Report.new.kind.should == "apply" + end + + it "should take a 'kind' as an argument" do + Puppet::Transaction::Report.new("inspect").kind.should == "inspect" + end + describe "when accepting logs" do before do @report = Puppet::Transaction::Report.new |