diff options
| author | Dominic Cleal <dcleal@redhat.com> | 2011-02-19 21:21:13 +0000 |
|---|---|---|
| committer | Dominic Cleal <dcleal@redhat.com> | 2011-02-19 21:21:13 +0000 |
| commit | c87ec2598700c4e5236452a016f0497ec848cb90 (patch) | |
| tree | 47a2435ef019bfcac2ec2aa388935173bc5c6b52 /lib | |
| parent | 3eace859f20d9ac7366382826028af44c3ab62d6 (diff) | |
| parent | ea348761df0b5297dbac50c7f1c48d22746524fa (diff) | |
| download | puppet-c87ec2598700c4e5236452a016f0497ec848cb90.tar.gz puppet-c87ec2598700c4e5236452a016f0497ec848cb90.tar.xz puppet-c87ec2598700c4e5236452a016f0497ec848cb90.zip | |
Merge branch 'master' into tickets/master/4258-dev
Diffstat (limited to 'lib')
111 files changed, 962 insertions, 885 deletions
diff --git a/lib/puppet.rb b/lib/puppet.rb index 78fb5138b..a58d3c801 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -24,7 +24,7 @@ require 'puppet/util/run_mode' # it's also a place to find top-level commands like 'debug' module Puppet - PUPPETVERSION = '2.6.3' + PUPPETVERSION = '2.6.4' def Puppet.version PUPPETVERSION diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index f0159a65d..17ad69cee 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -212,10 +212,17 @@ class Application end def find(name) - self.const_get(name.to_s.capitalize) - rescue + klass = name.to_s.capitalize + + # const_defined? is used before const_get since const_defined? will only + # check within our namespace, whereas const_get will check ancestor + # trees as well, resulting in unexpected behaviour. + if !self.const_defined?(klass) puts "Unable to find application '#{name.to_s}'." Kernel::exit(1) + end + + self.const_get(klass) end def [](name) diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb index c5ad72068..96f33296f 100644 --- a/lib/puppet/application/agent.rb +++ b/lib/puppet/application/agent.rb @@ -228,9 +228,9 @@ class Puppet::Application::Agent < Puppet::Application # access to the local files and we don't need a ca. Puppet::SSL::Host.ca_location = options[:fingerprint] ? :none : :remote - Puppet::Transaction::Report.terminus_class = :rest + Puppet::Transaction::Report.indirection.terminus_class = :rest # we want the last report to be persisted locally - Puppet::Transaction::Report.cache_class = :yaml + Puppet::Transaction::Report.indirection.cache_class = :yaml # Override the default; puppetd needs this, usually. # You can still override this on the command-line with, e.g., :compiler. @@ -239,8 +239,7 @@ class Puppet::Application::Agent < Puppet::Application # Override the default. Puppet[:facts_terminus] = :facter - Puppet::Resource::Catalog.cache_class = :yaml - + Puppet::Resource::Catalog.indirection.cache_class = :yaml # We need tomake the client either way, we just don't start it # if --no-client is set. diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 8ec3fab6b..e5b4bb5b7 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -85,12 +85,12 @@ class Puppet::Application::Apply < Puppet::Application end # Collect our facts. - unless facts = Puppet::Node::Facts.find(Puppet[:certname]) + unless facts = Puppet::Node::Facts.indirection.find(Puppet[:certname]) raise "Could not find facts for #{Puppet[:certname]}" end # Find our Node - unless node = Puppet::Node.find(Puppet[:certname]) + unless node = Puppet::Node.indirection.find(Puppet[:certname]) raise "Could not find node #{Puppet[:certname]}" end @@ -112,7 +112,7 @@ class Puppet::Application::Apply < Puppet::Application begin # Compile our catalog starttime = Time.now - catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) + catalog = Puppet::Resource::Catalog.indirection.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral @@ -149,7 +149,7 @@ class Puppet::Application::Apply < Puppet::Application end # we want the last report to be persisted locally - Puppet::Transaction::Report.cache_class = :yaml + Puppet::Transaction::Report.indirection.cache_class = :yaml if options[:debug] Puppet::Util::Log.level = :debug diff --git a/lib/puppet/application/doc.rb b/lib/puppet/application/doc.rb index 1f6c63286..aaefd6e75 100644 --- a/lib/puppet/application/doc.rb +++ b/lib/puppet/application/doc.rb @@ -8,7 +8,7 @@ class Puppet::Application::Doc < Puppet::Application attr_accessor :unknown_args, :manifest def preinit - {:references => [], :mode => :text, :format => :to_rest }.each do |name,value| + {:references => [], :mode => :text, :format => :to_markdown }.each do |name,value| options[name] = value end @unknown_args = [] @@ -113,9 +113,6 @@ class Puppet::Application::Doc < Puppet::Application text += Puppet::Util::Reference.footer unless with_contents # We've only got one reference - # Replace the trac links, since they're invalid everywhere else - text.gsub!(/`\w+\s+([^`]+)`:trac:/) { |m| $1 } - if options[:mode] == :pdf Puppet::Util::Reference.pdf(text) else diff --git a/lib/puppet/application/inspect.rb b/lib/puppet/application/inspect.rb new file mode 100644 index 000000000..19324e285 --- /dev/null +++ b/lib/puppet/application/inspect.rb @@ -0,0 +1,123 @@ +require 'puppet' +require 'puppet/application' +require 'puppet/file_bucket/dipper' + +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.indirection.terminus_class = :rest + Puppet::Resource::Catalog.indirection.terminus_class = :yaml + end + + def run_command + retrieval_starttime = Time.now + + unless catalog = Puppet::Resource::Catalog.indirection.find(Puppet[:certname]) + raise "Could not find catalog for #{Puppet[:certname]}" + end + + @report.configuration_version = catalog.version + + inspect_starttime = Time.now + @report.add_times("config_retrieval", inspect_starttime - retrieval_starttime) + + if Puppet[:archive_files] + dipper = Puppet::FileBucket::Dipper.new(:Server => Puppet[:archive_file_server]) + end + + catalog.to_ral.resources.each do |ral_resource| + audited_attributes = ral_resource[:audit] + next unless audited_attributes + + status = Puppet::Resource::Status.new(ral_resource) + + begin + audited_resource = ral_resource.to_resource + rescue StandardError => detail + puts detail.backtrace if Puppet[:trace] + ral_resource.err "Could not inspect #{ral_resource}; skipping: #{detail}" + audited_attributes.each do |name| + event = ral_resource.event( + :property => name, + :status => "failure", + :audited => true, + :message => "failed to inspect #{name}" + ) + status.add_event(event) + end + else + audited_attributes.each do |name| + next if audited_resource[name].nil? + # Skip :absent properties of :absent resources. Really, it would be nicer if the RAL returned nil for those, but it doesn't. ~JW + if name == :ensure or audited_resource[:ensure] != :absent or audited_resource[name] != :absent + event = ral_resource.event( + :previous_value => audited_resource[name], + :property => name, + :status => "audit", + :audited => true, + :message => "inspected value is #{audited_resource[name].inspect}" + ) + status.add_event(event) + end + end + end + if Puppet[:archive_files] and ral_resource.type == :file and audited_attributes.include?(:content) + path = ral_resource[:path] + if File.readable?(path) + begin + dipper.backup(path) + rescue StandardError => detail + Puppet.warning detail + end + end + end + @report.add_resource_status(status) + end + + finishtime = Time.now + @report.add_times("inspect", finishtime - inspect_starttime) + @report.finalize_report + + begin + Puppet::Transaction::Report.indirection.save(@report) + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err "Could not send report: #{detail}" + end + end +end diff --git a/lib/puppet/application/kick.rb b/lib/puppet/application/kick.rb index 37aeb1ef2..12dad653a 100644 --- a/lib/puppet/application/kick.rb +++ b/lib/puppet/application/kick.rb @@ -120,7 +120,7 @@ class Puppet::Application::Kick < Puppet::Application :background => ! options[:foreground], :ignoreschedules => options[:ignoreschedules] } - run = Puppet::Run.new( run_options ).save( url ) + run = Puppet::Run.indirection.save(Puppet::Run.new( run_options ), url) puts "Getting status" result = run.status puts "status is #{result}" @@ -175,12 +175,12 @@ class Puppet::Application::Kick < Puppet::Application if Puppet[:node_terminus] == "ldap" and (options[:all] or @classes) if options[:all] - @hosts = Puppet::Node.search("whatever", :fqdn => options[:fqdn]).collect { |node| node.name } + @hosts = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn]).collect { |node| node.name } puts "all: #{@hosts.join(", ")}" else @hosts = [] @classes.each do |klass| - list = Puppet::Node.search("whatever", :fqdn => options[:fqdn], :class => klass).collect { |node| node.name } + list = Puppet::Node.indirection.search("whatever", :fqdn => options[:fqdn], :class => klass).collect { |node| node.name } puts "#{klass}: #{list.join(", ")}" @hosts += list diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb index fde474907..879b66c67 100644 --- a/lib/puppet/application/master.rb +++ b/lib/puppet/application/master.rb @@ -51,7 +51,7 @@ class Puppet::Application::Master < Puppet::Application Puppet::Util::Log.newdestination :console raise ArgumentError, "Cannot render compiled catalogs without pson support" unless Puppet.features.pson? begin - unless catalog = Puppet::Resource::Catalog.find(options[:node]) + unless catalog = Puppet::Resource::Catalog.indirection.find(options[:node]) raise "Could not compile catalog for #{options[:node]}" end @@ -139,7 +139,7 @@ class Puppet::Application::Master < Puppet::Application Puppet.settings.use :main, :master, :ssl # Cache our nodes in yaml. Currently not configurable. - Puppet::Node.cache_class = :yaml + Puppet::Node.indirection.cache_class = :yaml # Configure all of the SSL stuff. if Puppet::SSL::CertificateAuthority.ca? diff --git a/lib/puppet/application/queue.rb b/lib/puppet/application/queue.rb index 239f6b2ad..b9e8ca4ca 100644 --- a/lib/puppet/application/queue.rb +++ b/lib/puppet/application/queue.rb @@ -41,12 +41,12 @@ class Puppet::Application::Queue < Puppet::Application require 'puppet/indirector/catalog/queue' # provides Puppet::Indirector::Queue.subscribe Puppet.notice "Starting puppetqd #{Puppet.version}" Puppet::Resource::Catalog::Queue.subscribe do |catalog| - # Once you have a Puppet::Resource::Catalog instance, calling save on it should suffice + # Once you have a Puppet::Resource::Catalog instance, passing it to save should suffice # to put it through to the database via its active_record indirector (which is determined # by the terminus_class = :active_record setting above) Puppet::Util.benchmark(:notice, "Processing queued catalog for #{catalog.name}") do begin - catalog.save + Puppet::Resource::Catalog.indirection.save(catalog) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not save queued catalog for #{catalog.name}: #{detail}" @@ -79,7 +79,7 @@ class Puppet::Application::Queue < Puppet::Application exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? require 'puppet/resource/catalog' - Puppet::Resource::Catalog.terminus_class = :active_record + Puppet::Resource::Catalog.indirection.terminus_class = :active_record daemon.daemonize if Puppet[:daemonize] @@ -87,6 +87,6 @@ class Puppet::Application::Queue < Puppet::Application # class set up, because if storeconfigs is enabled, # we'll get a loop of continually caching the catalog # for storage again. - Puppet::Resource::Catalog.cache_class = nil + Puppet::Resource::Catalog.indirection.cache_class = nil end end diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb index f55caa58a..c7c1c28be 100644 --- a/lib/puppet/application/resource.rb +++ b/lib/puppet/application/resource.rb @@ -76,12 +76,12 @@ class Puppet::Application::Resource < Puppet::Application text = if name if params.empty? - [ Puppet::Resource.find( key ) ] + [ Puppet::Resource.indirection.find( key ) ] else - [ Puppet::Resource.new( type, name, :parameters => params ).save( key ) ] + [ Puppet::Resource.indirection.save(Puppet::Resource.new( type, name, :parameters => params ), key) ] end else - Puppet::Resource.search( key, {} ) + Puppet::Resource.indirection.search( key, {} ) end.map(&format).join("\n") if options[:edit] diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index 1c0639029..72e387c64 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -72,10 +72,6 @@ class Puppet::Configurer @splayed = false end - def initialize_report - Puppet::Transaction::Report.new - end - # Prepare for catalog retrieval. Downloads everything necessary, etc. def prepare(options) dostorage @@ -134,7 +130,7 @@ class Puppet::Configurer 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) @@ -145,6 +141,8 @@ class Puppet::Configurer return end + report.configuration_version = catalog.version + transaction = nil begin @@ -171,12 +169,12 @@ class Puppet::Configurer send_report(report, transaction) end - def send_report(report, trans = nil) - trans.generate_report if trans + def send_report(report, trans) + report.finalize_report if trans puts report.summary if Puppet[:summarize] save_last_run_summary(report) if Puppet[:report] - report.save + Puppet::Transaction::Report.indirection.save(report) end rescue => detail puts detail.backtrace if Puppet[:trace] @@ -224,7 +222,7 @@ class Puppet::Configurer 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)) + result = Puppet::Resource::Catalog.indirection.find(Puppet[:certname], fact_options.merge(:ignore_terminus => true)) end Puppet.notice "Using cached catalog" result @@ -237,7 +235,7 @@ class Puppet::Configurer def retrieve_new_catalog(fact_options) result = nil @duration = thinmark do - result = Puppet::Resource::Catalog.find(Puppet[:certname], fact_options.merge(:ignore_cache => true)) + result = Puppet::Resource::Catalog.indirection.find(Puppet[:certname], fact_options.merge(:ignore_cache => true)) end result rescue SystemExit,NoMemoryError diff --git a/lib/puppet/configurer/fact_handler.rb b/lib/puppet/configurer/fact_handler.rb index 075a59458..abe032010 100644 --- a/lib/puppet/configurer/fact_handler.rb +++ b/lib/puppet/configurer/fact_handler.rb @@ -16,7 +16,7 @@ module Puppet::Configurer::FactHandler # compile them and then "cache" them on the server. begin reload_facter - Puppet::Node::Facts.find(Puppet[:certname]) + Puppet::Node::Facts.indirection.find(Puppet[:certname]) rescue SystemExit,NoMemoryError raise rescue Exception => detail diff --git a/lib/puppet/configurer/plugin_handler.rb b/lib/puppet/configurer/plugin_handler.rb index 539441e75..cfc6b5a0b 100644 --- a/lib/puppet/configurer/plugin_handler.rb +++ b/lib/puppet/configurer/plugin_handler.rb @@ -19,8 +19,6 @@ module Puppet::Configurer::PluginHandler begin Puppet.info "Loading downloaded plugin #{file}" load file - rescue SystemExit,NoMemoryError - raise rescue Exception => detail Puppet.err "Could not load downloaded file #{file}: #{detail}" end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 8bf1cd84d..e3b86bca4 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -2,8 +2,8 @@ module Puppet setdefaults(:main, :confdir => [Puppet.run_mode.conf_dir, "The main Puppet configuration directory. The default for this parameter is calculated based on the user. If the process - is running as root or the user that `puppet master` is supposed to run as, it defaults to a system directory, but if it's running as any other user, - it defaults to being in `~`."], + is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it's running as any other user, + it defaults to being in the user's home directory."], :vardir => [Puppet.run_mode.var_dir, "Where Puppet stores dynamic and growing data. The default for this parameter is calculated specially, like `confdir`_."], :name => [Puppet.application_name.to_s, "The name of the application, if we are running as one. The default is essentially $0 without the path or `.rb`."], @@ -121,7 +121,7 @@ module Puppet :hook => proc do |value| require 'puppet/node/facts' if value.to_s == "rest" - Puppet::Node::Facts.cache_class = :yaml + Puppet::Node::Facts.indirection.cache_class = :yaml end end }, @@ -152,7 +152,7 @@ module Puppet Puppet.settings[:storeconfigs] = true # But then we modify the configuration - Puppet::Resource::Catalog.cache_class = :queue + Puppet::Resource::Catalog.indirection.cache_class = :queue else raise "Cannot disable asynchronous storeconfigs in a running process" end @@ -622,6 +622,11 @@ module Puppet compression, but if it supports it, this setting might reduce performance on high-speed LANs."] ) + setdefaults(:inspect, + :archive_files => [false, "During an inspect run, whether to archive files whose contents are audited to a file bucket."], + :archive_file_server => ["$server", "During an inspect run, the file bucket server to archive files to if archive_files is set."] + ) + # Plugin information. setdefaults( @@ -794,9 +799,9 @@ module Puppet if value require 'puppet/rails' raise "StoreConfigs not supported without ActiveRecord 2.1 or higher" unless Puppet.features.rails? - Puppet::Resource::Catalog.cache_class = :active_record unless Puppet.settings[:async_storeconfigs] - Puppet::Node::Facts.cache_class = :active_record - Puppet::Node.cache_class = :active_record + Puppet::Resource::Catalog.indirection.cache_class = :active_record unless Puppet.settings[:async_storeconfigs] + Puppet::Node::Facts.indirection.cache_class = :active_record + Puppet::Node.indirection.cache_class = :active_record end end } diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb index dbfcdcd43..be487d83f 100644 --- a/lib/puppet/file_bucket/dipper.rb +++ b/lib/puppet/file_bucket/dipper.rb @@ -33,10 +33,15 @@ class Puppet::FileBucket::Dipper raise(ArgumentError, "File #{file} does not exist") unless ::File.exist?(file) contents = ::File.read(file) begin - file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path, :path => absolutize_path(file) ) + file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path) dest_path = "#{@rest_path}#{file_bucket_file.name}" - file_bucket_file.save(dest_path) + # Make a HEAD request for the file so that we don't waste time + # uploading it if it already exists in the bucket. + unless Puppet::FileBucket::File.indirection.head("#{@rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}") + Puppet::FileBucket::File.indirection.save(file_bucket_file, dest_path) + end + return file_bucket_file.checksum_data rescue => detail puts detail.backtrace if Puppet[:trace] @@ -47,7 +52,7 @@ class Puppet::FileBucket::Dipper # Retrieve a file by sum. def getfile(sum) source_path = "#{@rest_path}md5/#{sum}" - file_bucket_file = Puppet::FileBucket::File.find(source_path, :bucket_path => @local_path) + file_bucket_file = Puppet::FileBucket::File.indirection.find(source_path, :bucket_path => @local_path) raise Puppet::Error, "File not found" unless file_bucket_file file_bucket_file.to_s diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb index 96fd8e225..08c0329f1 100644 --- a/lib/puppet/file_bucket/file.rb +++ b/lib/puppet/file_bucket/file.rb @@ -1,10 +1,9 @@ require 'puppet/file_bucket' require 'puppet/indirector' require 'puppet/util/checksums' +require 'digest/md5' class Puppet::FileBucket::File - include Puppet::Util::Checksums - # This class handles the abstract notion of a file in a filebucket. # There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/* # There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper @@ -12,71 +11,27 @@ class Puppet::FileBucket::File require 'puppet/file_bucket/file/indirection_hooks' indirects :file_bucket_file, :terminus_class => :file, :extend => Puppet::FileBucket::File::IndirectionHooks - attr :path, true - attr :paths, true - attr :contents, true - attr :checksum_type - attr :bucket_path, true - - def self.default_checksum_type - "md5" - end + attr :contents + attr :bucket_path def initialize( contents, options = {} ) - @bucket_path = options[:bucket_path] - @path = options[:path] - @paths = options[:paths] || [] - - @checksum = options[:checksum] - @checksum_type = options[:checksum_type] - - self.contents = contents - - yield(self) if block_given? - - validate! - end + raise ArgumentError if !contents.is_a?(String) + @contents = contents - def validate! - validate_checksum_type!(checksum_type) - validate_checksum!(checksum) if checksum + @bucket_path = options.delete(:bucket_path) + raise ArgumentError if options != {} end - def contents=(str) - raise "You may not change the contents of a FileBucket File" if @contents - validate_content!(str) - @contents = str + def checksum_type + 'md5' end def checksum - return @checksum if @checksum - @checksum = calculate_checksum if contents - @checksum - end - - def checksum=(checksum) - validate_checksum!(checksum) - @checksum = checksum - end - - def checksum_type=( new_checksum_type ) - @checksum = nil - @checksum_type = new_checksum_type - end - - def checksum_type - unless @checksum_type - if @checksum - @checksum_type = sumtype(checksum) - else - @checksum_type = self.class.default_checksum_type - end - end - @checksum_type + "{#{checksum_type}}#{checksum_data}" end def checksum_data - sumdata(checksum) + @checksum_data ||= Digest::MD5.hexdigest(contents) end def to_s @@ -84,18 +39,7 @@ class Puppet::FileBucket::File end def name - [checksum_type, checksum_data, path].compact.join('/') - end - - def name=(name) - data = name.split('/',3) - self.path = data.pop - @checksum_type = nil - self.checksum = "{#{data[0]}}#{data[1]}" - end - - def conflict_check? - true + "#{checksum_type}/#{checksum_data}" end def self.from_s( contents ) @@ -103,34 +47,10 @@ class Puppet::FileBucket::File end def to_pson - hash = { "contents" => contents } - hash["path"] = @path if @path - hash.to_pson + { "contents" => contents }.to_pson end def self.from_pson( pson ) - self.new( pson["contents"], :path => pson["path"] ) - end - - private - - def calculate_checksum - "{#{checksum_type}}" + send(checksum_type, contents) - end - - def validate_content!(content) - raise ArgumentError, "Contents must be a string" if content and ! content.is_a?(String) - end - - def validate_checksum!(new_checksum) - newtype = sumtype(new_checksum) - - unless sumdata(new_checksum) == (calc_sum = send(newtype, contents)) - raise Puppet::Error, "Checksum #{new_checksum} does not match contents #{calc_sum}" - end - end - - def validate_checksum_type!(type) - raise ArgumentError, "Invalid checksum type #{type}" unless respond_to?(type) + self.new( pson["contents"] ) end end diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index 5b737578b..9effc5cdd 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -21,7 +21,6 @@ module Puppet::Indirector raise(ArgumentError, "Already handling indirection for #{@indirection.name}; cannot also handle #{indirection}") if @indirection # populate this class with the various new methods extend ClassMethods - include InstanceMethods include Puppet::Indirector::Envelope extend Puppet::Network::FormatHandler @@ -32,36 +31,5 @@ module Puppet::Indirector module ClassMethods attr_reader :indirection - - def cache_class=(klass) - indirection.cache_class = klass - end - - def terminus_class=(klass) - indirection.terminus_class = klass - end - - # Expire any cached instance. - def expire(*args) - indirection.expire(*args) - end - - def find(*args) - indirection.find(*args) - end - - def destroy(*args) - indirection.destroy(*args) - end - - def search(*args) - indirection.search(*args) - end - end - - module InstanceMethods - def save(key = nil) - self.class.indirection.save key, self - end end end diff --git a/lib/puppet/indirector/catalog/active_record.rb b/lib/puppet/indirector/catalog/active_record.rb index fabb08eb9..365cdfefe 100644 --- a/lib/puppet/indirector/catalog/active_record.rb +++ b/lib/puppet/indirector/catalog/active_record.rb @@ -30,9 +30,9 @@ class Puppet::Resource::Catalog::ActiveRecord < Puppet::Indirector::ActiveRecord host.merge_resources(catalog.vertices) host.last_compile = Time.now - if node = Puppet::Node.find(catalog.name) + if node = Puppet::Node.indirection.find(catalog.name) host.ip = node.parameters["ipaddress"] - host.environment = node.environment + host.environment = node.environment.to_s end host.save diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index 78be4caf7..f4c40812d 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -23,7 +23,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code facts = Puppet::Node::Facts.convert_from(format, text_facts) end facts.add_timestamp - facts.save + Puppet::Node::Facts.indirection.save(facts) end # Compile a node's catalog. @@ -88,7 +88,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code # Turn our host name into a node object. def find_node(name) begin - return nil unless node = Puppet::Node.find(name) + return nil unless node = Puppet::Node.indirection.find(name) rescue => detail puts detail.backtrace if Puppet[:trace] raise Puppet::Error, "Failed when searching for node #{name}: #{detail}" diff --git a/lib/puppet/indirector/file_bucket_file/file.rb b/lib/puppet/indirector/file_bucket_file/file.rb index 318858aaf..8bea2d767 100644 --- a/lib/puppet/indirector/file_bucket_file/file.rb +++ b/lib/puppet/indirector/file_bucket_file/file.rb @@ -14,16 +14,31 @@ module Puppet::FileBucketFile end def find( request ) - checksum, path = request_to_checksum_and_path( request ) - find_by_checksum( checksum, request.options ) + checksum = request_to_checksum( request ) + file_path = path_for(request.options[:bucket_path], checksum, 'contents') + + return nil unless ::File.exists?(file_path) + + if request.options[:diff_with] + hash_protocol = sumtype(checksum) + file2_path = path_for(request.options[:bucket_path], request.options[:diff_with], 'contents') + raise "could not find diff_with #{request.options[:diff_with]}" unless ::File.exists?(file2_path) + return `diff #{file_path.inspect} #{file2_path.inspect}` + else + contents = ::File.read file_path + Puppet.info "FileBucket read #{checksum}" + model.new(contents) + end end - def save( request ) - checksum, path = request_to_checksum_and_path( request ) + def head(request) + checksum = request_to_checksum(request) + file_path = path_for(request.options[:bucket_path], checksum, 'contents') + ::File.exists?(file_path) + end + def save( request ) instance = request.instance - instance.checksum = checksum if checksum - instance.path = path if path save_to_disk(instance) instance.to_s @@ -31,66 +46,41 @@ module Puppet::FileBucketFile private - def find_by_checksum( checksum, options ) - model.new( nil, :checksum => checksum ) do |bucket_file| - bucket_file.bucket_path = options[:bucket_path] - filename = contents_path_for( bucket_file ) - - return nil if ! ::File.exist? filename - - begin - contents = ::File.read filename - Puppet.info "FileBucket read #{bucket_file.checksum}" - rescue RuntimeError => e - raise Puppet::Error, "file could not be read: #{e.message}" - end - - if ::File.exist?(paths_path_for( bucket_file) ) - ::File.open(paths_path_for( bucket_file) ) do |f| - bucket_file.paths = f.readlines.map { |l| l.chomp } - end - end - - bucket_file.contents = contents - end - end - def save_to_disk( bucket_file ) - # If the file already exists, just return the md5 sum. - if ::File.exist?(contents_path_for( bucket_file) ) + filename = path_for(bucket_file.bucket_path, bucket_file.checksum_data, 'contents') + dirname = path_for(bucket_file.bucket_path, bucket_file.checksum_data) + + # If the file already exists, do nothing. + if ::File.exist?(filename) verify_identical_file!(bucket_file) else # Make the directories if necessary. - unless ::File.directory?( path_for( bucket_file) ) + unless ::File.directory?(dirname) Puppet::Util.withumask(0007) do - ::FileUtils.mkdir_p( path_for( bucket_file) ) + ::FileUtils.mkdir_p(dirname) end end - Puppet.info "FileBucket adding #{bucket_file.path} as #{bucket_file.checksum}" + Puppet.info "FileBucket adding #{bucket_file.checksum}" # Write the file to disk. Puppet::Util.withumask(0007) do - ::File.open(contents_path_for(bucket_file), ::File::WRONLY|::File::CREAT, 0440) do |of| + ::File.open(filename, ::File::WRONLY|::File::CREAT, 0440) do |of| of.print bucket_file.contents end end end - - save_path_to_paths_file(bucket_file) - bucket_file.checksum_data end - def request_to_checksum_and_path( request ) - return [request.key, nil] if checksum?(request.key) - - checksum_type, checksum, path = request.key.split(/\//, 3) - return(checksum_type.to_s == "" ? nil : [ "{#{checksum_type}}#{checksum}", path ]) + def request_to_checksum( request ) + checksum_type, checksum, path = request.key.split(/\//, 3) # Note: we ignore path if present. + raise "Unsupported checksum type #{checksum_type.inspect}" if checksum_type != 'md5' + raise "Invalid checksum #{checksum.inspect}" if checksum !~ /^[0-9a-f]{32}$/ + checksum end - def path_for(bucket_file, subfile = nil) - bucket_path = bucket_file.bucket_path || Puppet[:bucketdir] - digest = bucket_file.checksum_data + def path_for(bucket_path, digest, subfile = nil) + bucket_path ||= Puppet[:bucketdir] dir = ::File.join(digest[0..7].split("")) basedir = ::File.join(bucket_path, dir, digest) @@ -99,48 +89,18 @@ module Puppet::FileBucketFile ::File.join(basedir, subfile) end - def contents_path_for(bucket_file) - path_for(bucket_file, "contents") - end - - def paths_path_for(bucket_file) - path_for(bucket_file, "paths") - end - - def content_check? - true - end - # If conflict_check is enabled, verify that the passed text is # the same as the text in our file. def verify_identical_file!(bucket_file) - return unless content_check? - disk_contents = ::File.read(contents_path_for(bucket_file)) + disk_contents = ::File.read(path_for(bucket_file.bucket_path, bucket_file.checksum_data, 'contents')) # If the contents don't match, then we've found a conflict. # Unlikely, but quite bad. if disk_contents != bucket_file.contents - raise Puppet::FileBucket::BucketError, "Got passed new contents for sum #{bucket_file.checksum}", caller + raise Puppet::FileBucket::BucketError, "Got passed new contents for sum #{bucket_file.checksum}" else - Puppet.info "FileBucket got a duplicate file #{bucket_file.path} (#{bucket_file.checksum})" + Puppet.info "FileBucket got a duplicate file #{bucket_file.checksum}" end end - - def save_path_to_paths_file(bucket_file) - return unless bucket_file.path - - # check for dupes - if ::File.exist?(paths_path_for( bucket_file) ) - ::File.open(paths_path_for( bucket_file) ) do |f| - return if f.readlines.collect { |l| l.chomp }.include?(bucket_file.path) - end - end - - # if it's a new file, or if our path isn't in the file yet, add it - ::File.open(paths_path_for(bucket_file), ::File::WRONLY|::File::CREAT|::File::APPEND) do |of| - of.puts bucket_file.path - end - end - end end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 9095e48f8..d958a82ac 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -180,18 +180,13 @@ class Puppet::Indirector::Indirection request = request(:find, key, *args) terminus = prepare(request) - begin - if result = find_in_cache(request) - return result - end - rescue => detail - puts detail.backtrace if Puppet[:trace] - Puppet.err "Cached #{self.name} for #{request.key} failed: #{detail}" + if result = find_in_cache(request) + return result end # Otherwise, return the result from the terminus, caching if appropriate. if ! request.ignore_terminus? and result = terminus.find(request) - result.expiration ||= self.expiration + result.expiration ||= self.expiration if result.respond_to?(:expiration) if cache? and request.use_cache? Puppet.info "Caching #{self.name} for #{request.key}" cache.save request(:save, result, *args) @@ -203,6 +198,17 @@ class Puppet::Indirector::Indirection nil end + # Search for an instance in the appropriate terminus, and return a + # boolean indicating whether the instance was found. + def head(key, *args) + request = request(:head, key, *args) + terminus = prepare(request) + + # Look in the cache first, then in the terminus. Force the result + # to be a boolean. + !!(find_in_cache(request) || terminus.head(request)) + end + def find_in_cache(request) # See if our instance is in the cache and up to date. return nil unless cache? and ! request.ignore_cache? and cached = cache.find(request) @@ -213,6 +219,10 @@ class Puppet::Indirector::Indirection Puppet.debug "Using cached #{self.name} for #{request.key}" cached + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.err "Cached #{self.name} for #{request.key} failed: #{detail}" + nil end # Remove something via the terminus. @@ -247,7 +257,7 @@ class Puppet::Indirector::Indirection # Save the instance in the appropriate terminus. This method is # normally an instance method on the indirected class. - def save(key, instance = nil) + def save(instance, key = nil) request = request(:save, key, instance) terminus = prepare(request) diff --git a/lib/puppet/indirector/resource/ral.rb b/lib/puppet/indirector/resource/ral.rb index 1c2ab14ae..bc41d14ae 100644 --- a/lib/puppet/indirector/resource/ral.rb +++ b/lib/puppet/indirector/resource/ral.rb @@ -34,12 +34,17 @@ class Puppet::Resource::Ral < Puppet::Indirector::Code private + # {type,resource}_name: the resource name may contain slashes: + # File["/etc/hosts"]. To handle, assume the type name does + # _not_ have any slashes in it, and split only on the first. + def type_name( request ) - request.key.split('/')[0] + request.key.split('/', 2)[0] end def resource_name( request ) - request.key.split('/')[1] + name = request.key.split('/', 2)[1] + name unless name == "" end def type( request ) diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index eb41ff3b1..e50dc68ae 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -53,11 +53,15 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end else # Raise the http error if we didn't get a 'success' of some kind. - message = "Error #{response.code} on SERVER: #{(response.body||'').empty? ? response.message : uncompress_body(response)}" - raise Net::HTTPError.new(message, response) + raise convert_to_http_error(response) end end + def convert_to_http_error(response) + message = "Error #{response.code} on SERVER: #{(response.body||'').empty? ? response.message : uncompress_body(response)}" + Net::HTTPError.new(message, response) + end + # Provide appropriate headers. def headers add_accept_encoding({"Accept" => model.supported_formats.join(", ")}) @@ -73,6 +77,19 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus result end + def head(request) + response = network(request).head(indirection2uri(request), headers) + case response.code + when "404" + return false + when /^2/ + return true + else + # Raise the http error if we didn't get a 'success' of some kind. + raise convert_to_http_error(response) + end + end + def search(request) unless result = deserialize(network(request).get(indirection2uri(request), headers), true) return [] diff --git a/lib/puppet/network/handler/filebucket.rb b/lib/puppet/network/handler/filebucket.rb index 6aaa2df1c..55028ee64 100755 --- a/lib/puppet/network/handler/filebucket.rb +++ b/lib/puppet/network/handler/filebucket.rb @@ -28,12 +28,12 @@ class Puppet::Network::Handler # :nodoc: def addfile(contents, path, client = nil, clientip = nil) contents = Base64.decode64(contents) if client bucket = Puppet::FileBucket::File.new(contents) - bucket.save + Puppet::FileBucket::File.indirection.save(bucket) end # Return the contents associated with a given md5 sum. def getfile(md5, client = nil, clientip = nil) - bucket = Puppet::FileBucket::File.find("md5:#{md5}") + bucket = Puppet::FileBucket::File.indirection.find("md5:#{md5}") contents = bucket.contents if client diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb index 9abc7ee1a..5b4b17a32 100755 --- a/lib/puppet/network/handler/fileserver.rb +++ b/lib/puppet/network/handler/fileserver.rb @@ -236,7 +236,7 @@ class Puppet::Network::Handler unless hostname = (client || Facter.value("hostname")) raise ArgumentError, "Could not find hostname" end - env = (node = Puppet::Node.find(hostname)) ? node.environment : nil + env = (node = Puppet::Node.indirection.find(hostname)) ? node.environment : nil # And use the environment to look up the module. (mod = Puppet::Node::Environment.new(env).module(module_name) and mod.files?) ? @mounts[MODULES].copy(mod.name, mod.file_directory) : nil diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index c21aafafc..62aab539e 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -47,9 +47,9 @@ class Puppet::Network::Handler client ||= facts["hostname"] # Pass the facts to the fact handler - Puppet::Node::Facts.new(client, facts).save unless local? + Puppet::Node::Facts.indirection.save(Puppet::Node::Facts.new(client, facts)) unless local? - catalog = Puppet::Resource::Catalog.find(client) + catalog = Puppet::Resource::Catalog.indirection.find(client) case format when "yaml" diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index abbb2dfa9..4b7c15a36 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -13,6 +13,9 @@ module Puppet::Network::HTTP::API::V1 }, "DELETE" => { :singular => :destroy + }, + "HEAD" => { + :singular => :head } } diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 82238aa0a..2b9e81b61 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -103,7 +103,7 @@ module Puppet::Network::HTTP::Handler # Execute our find. def do_find(indirection_name, key, params, request, response) - unless result = model(indirection_name).find(key, params) + unless result = model(indirection_name).indirection.find(key, params) Puppet.info("Could not find #{indirection_name} for '#{key}'") return do_exception(response, "Could not find #{indirection_name} #{key}", 404) end @@ -114,13 +114,28 @@ module Puppet::Network::HTTP::Handler format = format_to_use(request) set_content_type(response, format) - set_response(response, result.render(format)) + if result.respond_to?(:render) + set_response(response, result.render(format)) + else + set_response(response, result) + end + end + + # Execute our head. + def do_head(indirection_request, request, response) + unless indirection_request.model.head(indirection_request.key, indirection_request.to_hash) + Puppet.info("Could not find #{indirection_request.indirection_name} for '#{indirection_request.key}'") + return do_exception(response, "Could not find #{indirection_request.indirection_name} #{indirection_request.key}", 404) + end + + # No need to set a response because no response is expected from a + # HEAD request. All we need to do is not die. end # Execute our search. def do_search(indirection_name, key, params, request, response) model = self.model(indirection_name) - result = model.search(key, params) + result = model.indirection.search(key, params) if result.nil? return do_exception(response, "Could not find instances in #{indirection_name} with '#{key}'", 404) @@ -134,7 +149,7 @@ module Puppet::Network::HTTP::Handler # Execute our destroy. def do_destroy(indirection_name, key, params, request, response) - result = model(indirection_name).destroy(key, params) + result = model(indirection_name).indirection.destroy(key, params) return_yaml_response(response, result) end @@ -146,7 +161,7 @@ module Puppet::Network::HTTP::Handler format = request_format(request) obj = model(indirection_name).convert_from(format, data) - result = obj.save(key) + result = model(indirection_name).indirection.save(obj, key) return_yaml_response(response, result) end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 8ed0b28ca..54bcf30c2 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -105,7 +105,7 @@ class Puppet::Network::HTTP::WEBrick results[:SSLStartImmediately] = true results[:SSLEnable] = true - raise Puppet::Error, "Could not find CA certificate" unless Puppet::SSL::Certificate.find(Puppet::SSL::CA_NAME) + raise Puppet::Error, "Could not find CA certificate" unless Puppet::SSL::Certificate.indirection.find(Puppet::SSL::CA_NAME) results[:SSLCACertificateFile] = Puppet[:localcacert] results[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb index b6a163316..9e3632499 100644 --- a/lib/puppet/network/rest_authconfig.rb +++ b/lib/puppet/network/rest_authconfig.rb @@ -17,7 +17,6 @@ module Puppet { :acl => "/certificate/", :method => :find, :authenticated => false }, { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, { :acl => "/status", :method => [:find], :authenticated => true }, - { :acl => "/resource", :method => [:find, :save, :search], :authenticated => true }, ] def self.main @@ -39,14 +38,10 @@ module Puppet # fail_on_deny could as well be called in the XMLRPC context # with a ClientRequest. - @rights.fail_on_deny( - build_uri(indirection, key), - :node => params[:node], - :ip => params[:ip], - :method => method, - :environment => params[:environment], - :authenticated => params[:authenticated] - ) + if authorization_failure_exception = @rights.is_request_forbidden_and_why?(indirection, method, key, params) + Puppet.warning("Denying access: #{authorization_failure_exception}") + raise authorization_failure_exception + end end def initialize(file = nil, parsenow = true) @@ -89,9 +84,5 @@ module Puppet end @rights.restrict_authenticated(acl[:acl], acl[:authenticated]) unless acl[:authenticated].nil? end - - def build_uri(indirection_name, key) - "/#{indirection_name}/#{key}" - end end end diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb index e3cd3179a..56af53983 100755 --- a/lib/puppet/network/rights.rb +++ b/lib/puppet/network/rights.rb @@ -26,19 +26,29 @@ class Rights # Check that name is allowed or not def allowed?(name, *args) - begin - fail_on_deny(name, :node => args[0], :ip => args[1]) - rescue AuthorizationError - return false - rescue ArgumentError - # the namespace contract says we should raise this error - # if we didn't find the right acl - raise + !is_forbidden_and_why?(name, :node => args[0], :ip => args[1]) + end + + def is_request_forbidden_and_why?(indirection, method, key, params) + methods_to_check = if method == :head + # :head is ok if either :find or :save is ok. + [:find, :save] + else + [method] + end + authorization_failure_exceptions = methods_to_check.map do |method| + is_forbidden_and_why?("/#{indirection}/#{key}", params.merge({:method => method})) + end + if authorization_failure_exceptions.include? nil + # One of the methods we checked is ok, therefore this request is ok. + nil + else + # Just need to return any of the failure exceptions. + authorization_failure_exceptions.first end - true end - def fail_on_deny(name, args = {}) + def is_forbidden_and_why?(name, args = {}) res = :nomatch right = @rights.find do |acl| found = false @@ -49,7 +59,7 @@ class Rights args[:match] = match if (res = acl.allowed?(args[:node], args[:ip], args)) != :dunno # return early if we're allowed - return if res + return nil if res # we matched, select this acl found = true end @@ -70,13 +80,12 @@ class Rights error.file = right.file error.line = right.line end - Puppet.warning("Denying access: #{error}") else # there were no rights allowing/denying name # if name is not a path, let's throw - error = ArgumentError.new "Unknown namespace right '#{name}'" + raise ArgumentError.new "Unknown namespace right '#{name}'" end - raise error + error end def initialize diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb index e8d58e6be..5b0a98615 100644 --- a/lib/puppet/node.rb +++ b/lib/puppet/node.rb @@ -57,7 +57,7 @@ class Puppet::Node # Merge the node facts with parameters from the node source. def fact_merge - if facts = Puppet::Node::Facts.find(name) + if facts = Puppet::Node::Facts.indirection.find(name) merge(facts.values) end rescue => detail diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb index d84d54113..451813f7d 100755 --- a/lib/puppet/node/facts.rb +++ b/lib/puppet/node/facts.rb @@ -15,8 +15,8 @@ class Puppet::Node::Facts # We want to expire any cached nodes if the facts are saved. module NodeExpirer - def save(key, instance) - Puppet::Node.expire(instance.name) + def save(instance, key = nil) + Puppet::Node.indirection.expire(instance.name) super end end diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 03891160b..122b4dd7a 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -19,6 +19,10 @@ class Puppet::Parser::AST attr_accessor :parent, :scope + def inspect + "( #{self.class} #{self.to_s} #{@children.inspect} )" + end + # don't fetch lexer comment by default def use_docs self.class.use_docs diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index c60e1d4fb..fdabd05c9 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -139,12 +139,23 @@ class Puppet::Parser::Compiler def evaluate_classes(classes, scope, lazy_evaluate = true) raise Puppet::DevError, "No source for scope passed to evaluate_classes" unless scope.source found = [] + param_classes = nil + # if we are a param class, save the classes hash + # and transform classes to be the keys + if classes.class == Hash + param_classes = classes + classes = classes.keys + end classes.each do |name| # If we can find the class, then make a resource that will evaluate it. if klass = scope.find_hostclass(name) - found << name and next if scope.class_scope(klass) - resource = klass.ensure_in_catalog(scope) + if param_classes + resource = klass.ensure_in_catalog(scope, param_classes[name] || {}) + else + found << name and next if scope.class_scope(klass) + resource = klass.ensure_in_catalog(scope) + end # If they've disabled lazy evaluation (which the :include function does), # then evaluate our resource immediately. @@ -432,7 +443,11 @@ class Puppet::Parser::Compiler @resources = [] # Make sure any external node classes are in our class list - @catalog.add_class(*@node.classes) + if @node.classes.class == Hash + @catalog.add_class(*@node.classes.keys) + else + @catalog.add_class(*@node.classes) + end end # Set the node's parameters into the top-scope as variables. diff --git a/lib/puppet/parser/functions/defined.rb b/lib/puppet/parser/functions/defined.rb index 90632af2f..2aeaa9ba0 100644 --- a/lib/puppet/parser/functions/defined.rb +++ b/lib/puppet/parser/functions/defined.rb @@ -1,10 +1,32 @@ # Test whether a given class or definition is defined -Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :doc => "Determine whether a given - type is defined, either as a native type or a defined type, or whether a class is defined. - This is useful for checking whether a class is defined and only including it if it is. - This function can also test whether a resource has been defined, using resource references - (e.g., `if defined(File['/tmp/myfile']) { ... }`). This function is unfortunately - dependent on the parse order of the configuration when testing whether a resource is defined.") do |vals| +Puppet::Parser::Functions::newfunction(:defined, :type => :rvalue, :doc => "Determine whether + a given class or resource type is defined. This function can also determine whether a + specific resource has been declared. Returns true or false. Accepts class names, + type names, and resource references. + + The `defined` function checks both native and defined types, including types + provided as plugins via modules. Types and classes are both checked using their names: + + defined(\"file\") + defined(\"customtype\") + defined(\"foo\") + defined(\"foo::bar\") + + Resource declarations are checked using resource references, e.g. + `defined( File['/tmp/myfile'] )`. Checking whether a given resource + has been declared is, unfortunately, dependent on the parse order of + the configuration, and the following code will not work: + + if defined(File['/tmp/foo']) { + notify(\"This configuration includes the /tmp/foo file.\") + } + file {\"/tmp/foo\": + ensure => present, + } + + However, this order requirement refers to parse order only, and ordering of + resources in the configuration graph (e.g. with `before` or `require`) does not + affect the behavior of `defined`.") do |vals| result = false vals = [vals] unless vals.is_a?(Array) vals.each do |val| diff --git a/lib/puppet/parser/functions/fqdn_rand.rb b/lib/puppet/parser/functions/fqdn_rand.rb index 3e7018ac4..52946f2c1 100644 --- a/lib/puppet/parser/functions/fqdn_rand.rb +++ b/lib/puppet/parser/functions/fqdn_rand.rb @@ -1,7 +1,10 @@ Puppet::Parser::Functions::newfunction(:fqdn_rand, :type => :rvalue, :doc => - "Generates random numbers based on the node's fqdn. The first argument - sets the range. Additional (optional) arguments may be used to further - distinguish the seed.") do |args| + "Generates random numbers based on the node's fqdn. Generated random values + will be a range from 0 up to and excluding n, where n is the first parameter. + The second argument specifies a number to add to the seed and is optional, for example: + + $random_number = fqdn_rand(30) + $random_number_seed = fqdn_rand(30,30)") do |args| require 'md5' max = args.shift srand MD5.new([lookupvar('fqdn'),args].join(':')).to_s.hex diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 84e1a0360..12f496a6e 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -152,9 +152,24 @@ class Puppet::Property < Puppet::Parameter # since we cannot fix it. Otherwise, we expect our should value # to be an array, and if @is matches any of those values, then # we consider it to be in-sync. - def insync?(is) + # + # Don't override this method. + def safe_insync?(is) + # If there is no @should value, consider the property to be in sync. return true unless @should + # Otherwise delegate to the (possibly derived) insync? method. + insync?(is) + end + + def self.method_added(sym) + raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync? + end + + # This method should be overridden by derived classes if necessary + # to provide extra logic to determine whether the property is in + # sync. + def insync?(is) self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array) # an empty array is analogous to no should values diff --git a/lib/puppet/property/keyvalue.rb b/lib/puppet/property/keyvalue.rb index 0181708f9..57d0ea2d9 100644 --- a/lib/puppet/property/keyvalue.rb +++ b/lib/puppet/property/keyvalue.rb @@ -77,8 +77,6 @@ module Puppet end def insync?(is) - return true unless @should - return true unless is (is == self.should) diff --git a/lib/puppet/property/list.rb b/lib/puppet/property/list.rb index dcee85db7..b86dc87f2 100644 --- a/lib/puppet/property/list.rb +++ b/lib/puppet/property/list.rb @@ -66,8 +66,6 @@ module Puppet end def insync?(is) - return true unless @should - return true unless is (prepare_is_for_comparison(is) == self.should) diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb index 7dbd06240..461968245 100644 --- a/lib/puppet/provider/augeas/augeas.rb +++ b/lib/puppet/provider/augeas/augeas.rb @@ -213,7 +213,12 @@ Puppet::Type.type(:augeas).provide(:augeas) do fail("Invalid command: #{cmd_array.join(" ")}") if clause_array.length != 2 comparator = clause_array.shift arg = clause_array.shift - return_value = (result.size.send(comparator, arg)) + case comparator + when "!=" + return_value = !(result.size.send(:==, arg)) + else + return_value = (result.size.send(comparator, arg)) + end when "include" arg = clause_array.shift return_value = result.include?(arg) diff --git a/lib/puppet/provider/file/posix.rb b/lib/puppet/provider/file/posix.rb index 6cbf98e9a..f7b8c9797 100644 --- a/lib/puppet/provider/file/posix.rb +++ b/lib/puppet/provider/file/posix.rb @@ -27,9 +27,7 @@ Puppet::Type.type(:file).provide :posix do end end - def insync?(current, should) - return true unless should - + def is_owner_insync?(current, should) should.each do |value| if value =~ /^\d+$/ uid = Integer(value) diff --git a/lib/puppet/provider/file/win32.rb b/lib/puppet/provider/file/win32.rb index 8ead69a89..21e7ca974 100644 --- a/lib/puppet/provider/file/win32.rb +++ b/lib/puppet/provider/file/win32.rb @@ -14,9 +14,7 @@ Puppet::Type.type(:file).provide :microsoft_windows do id end - def insync?(current, should) - return true unless should - + def is_owner_insync?(current, should) should.each do |value| if value =~ /^\d+$/ uid = Integer(value) diff --git a/lib/puppet/provider/host/parsed.rb b/lib/puppet/provider/host/parsed.rb index a303c4bcf..2ba01a41c 100644 --- a/lib/puppet/provider/host/parsed.rb +++ b/lib/puppet/provider/host/parsed.rb @@ -22,9 +22,7 @@ Puppet::Type.type(:host).provide(:parsed,:parent => Puppet::Provider::ParsedFile # An absent comment should match "comment => ''" hash[:comment] = '' if hash[:comment].nil? or hash[:comment] == :absent unless hash[:host_aliases].nil? or hash[:host_aliases] == :absent - hash[:host_aliases] = hash[:host_aliases].split(/\s+/) - else - hash[:host_aliases] = [] + hash[:host_aliases].gsub!(/\s+/,' ') # Change delimiter end }, :to_line => proc { |hash| @@ -32,8 +30,8 @@ Puppet::Type.type(:host).provide(:parsed,:parent => Puppet::Provider::ParsedFile raise ArgumentError, "#{n} is a required attribute for hosts" unless hash[n] and hash[n] != :absent end str = "#{hash[:ip]}\t#{hash[:name]}" - if hash.include? :host_aliases and !hash[:host_aliases].empty? - str += "\t#{hash[:host_aliases].join("\t")}" + if hash.include? :host_aliases and !hash[:host_aliases].nil? and hash[:host_aliases] != :absent + str += "\t#{hash[:host_aliases]}" end if hash.include? :comment and !hash[:comment].empty? str += "\t# #{hash[:comment]}" diff --git a/lib/puppet/provider/maillist/mailman.rb b/lib/puppet/provider/maillist/mailman.rb index 633642af7..e070a25dd 100755 --- a/lib/puppet/provider/maillist/mailman.rb +++ b/lib/puppet/provider/maillist/mailman.rb @@ -2,11 +2,11 @@ require 'puppet/provider/parsedfile' Puppet::Type.type(:maillist).provide(:mailman) do if [ "CentOS", "RedHat", "Fedora" ].any? { |os| Facter.value(:operatingsystem) == os } - commands :list_lists => "/usr/lib/mailman/bin/list_lists --bare", :rmlist => "/usr/lib/mailman/bin/rmlist", :newlist => "/usr/lib/mailman/bin/newlist" + commands :list_lists => "/usr/lib/mailman/bin/list_lists", :rmlist => "/usr/lib/mailman/bin/rmlist", :newlist => "/usr/lib/mailman/bin/newlist" commands :mailman => "/usr/lib/mailman/mail/mailman" else # This probably won't work for non-Debian installs, but this path is sure not to be in the PATH. - commands :list_lists => "list_lists --bare", :rmlist => "rmlist", :newlist => "newlist" + commands :list_lists => "list_lists", :rmlist => "rmlist", :newlist => "newlist" commands :mailman => "/var/lib/mailman/mail/mailman" end @@ -14,10 +14,9 @@ Puppet::Type.type(:maillist).provide(:mailman) do # Return a list of existing mailman instances. def self.instances - list_lists.split("\n").collect do |line| - name = line.strip - new(:ensure => :present, :name => name) - end + list_lists('--bare'). + split("\n"). + collect { |line| new(:ensure => :present, :name => line.strip) } end # Prefetch our list list, yo. diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index 8c7b24bd4..c979f742f 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -43,6 +43,8 @@ module Puppet::Provider::Mount line =~ / on #{name} / or line =~ %r{ on /private/var/automount#{name}} when "Solaris", "HP-UX" line =~ /^#{name} on / + when "AIX" + line.split(/\s+/)[1] == name else line =~ / on #{name} / end diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb index 106d99eb3..2e3480985 100644 --- a/lib/puppet/provider/nameservice/directoryservice.rb +++ b/lib/puppet/provider/nameservice/directoryservice.rb @@ -474,7 +474,7 @@ class DirectoryService < Puppet::Provider::NameService def remove_unwanted_members(current_members, new_members) current_members.each do |member| - if not new_members.include?(member) + if not new_members.flatten.include?(member) cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]] begin execute(cmd) @@ -486,7 +486,7 @@ class DirectoryService < Puppet::Provider::NameService end def add_members(current_members, new_members) - new_members.each do |new_member| + new_members.flatten.each do |new_member| if current_members.nil? or not current_members.include?(new_member) cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", new_member, @resource[:name]] begin diff --git a/lib/puppet/provider/package/freebsd.rb b/lib/puppet/provider/package/freebsd.rb index 2f012a4ed..e10a20b04 100755 --- a/lib/puppet/provider/package/freebsd.rb +++ b/lib/puppet/provider/package/freebsd.rb @@ -20,11 +20,11 @@ Puppet::Type.type(:package).provide :freebsd, :parent => :openbsd do if @resource[:source] =~ /\/$/ if @resource[:source] =~ /^(ftp|https?):/ - withenv :PACKAGESITE => @resource[:source] do + Puppet::Util::Execution::withenv :PACKAGESITE => @resource[:source] do pkgadd "-r", @resource[:name] end else - withenv :PKG_PATH => @resource[:source] do + Puppet::Util::Execution::withenv :PKG_PATH => @resource[:source] do pkgadd @resource[:name] end end diff --git a/lib/puppet/provider/zfs/solaris.rb b/lib/puppet/provider/zfs/solaris.rb index 85d054f86..9aec9d801 100644 --- a/lib/puppet/provider/zfs/solaris.rb +++ b/lib/puppet/provider/zfs/solaris.rb @@ -31,7 +31,7 @@ Puppet::Type.type(:zfs).provide(:solaris) do end end - [:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |field| + [:mountpoint, :recordsize, :aclmode, :aclinherit, :primarycache, :secondarycache, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |field| define_method(field) do zfs(:get, "-H", "-o", "value", field, @resource[:name]).strip end diff --git a/lib/puppet/provider/zone/solaris.rb b/lib/puppet/provider/zone/solaris.rb index c11444993..194af5049 100644 --- a/lib/puppet/provider/zone/solaris.rb +++ b/lib/puppet/provider/zone/solaris.rb @@ -40,7 +40,7 @@ Puppet::Type.type(:zone).provide(:solaris) do # Then perform all of our configuration steps. It's annoying # that we need this much internal info on the resource. @resource.send(:properties).each do |property| - str += property.configtext + "\n" if property.is_a? ZoneConfigProperty and ! property.insync?(properties[property.name]) + str += property.configtext + "\n" if property.is_a? ZoneConfigProperty and ! property.safe_insync?(properties[property.name]) end str += "commit\n" @@ -221,6 +221,9 @@ Puppet::Type.type(:zone).provide(:solaris) do if dir = config["inherit-pkg-dir"] result[:inherit] = dir.collect { |dirs| dirs[:dir] } end + if datasets = config["dataset"] + result[:dataset] = datasets.collect { |dataset| dataset[:name] } + end result[:iptype] = config[:"ip-type"] if net = config["net"] result[:ip] = net.collect do |params| diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb index e6a8dc20f..c8ff145ba 100644 --- a/lib/puppet/reference/configuration.rb +++ b/lib/puppet/reference/configuration.rb @@ -94,11 +94,11 @@ The file follows INI-style formatting. Here is an example of a very simple Note that boolean parameters must be explicitly specified as `true` or `false` as seen above. -If you need to change file parameters (e.g., reset the mode or owner), do +If you need to change file or directory parameters (e.g., reset the mode or owner), do so within curly braces on the same line: [main] - myfile = /tmp/whatever {owner = root, mode = 644} + vardir = /new/vardir {owner = root, mode = 644} If you're starting out with a fresh configuration, you may wish to let the executable generate a template configuration file for you by invoking diff --git a/lib/puppet/reference/function.rb b/lib/puppet/reference/function.rb index 1333e0d26..7d39bebd5 100644 --- a/lib/puppet/reference/function.rb +++ b/lib/puppet/reference/function.rb @@ -8,6 +8,10 @@ performing stand-alone work like importing. Rvalues return values and can only be used in a statement requiring a value, such as an assignment or a case statement. +Functions execute on the Puppet master. They do not execute on the Puppet agent. +Hence they only have access to the commands and data available on the Puppet master +host. + Here are the functions available in Puppet: " diff --git a/lib/puppet/relationship.rb b/lib/puppet/relationship.rb index 7079fb44b..08d7d042b 100644 --- a/lib/puppet/relationship.rb +++ b/lib/puppet/relationship.rb @@ -71,6 +71,10 @@ class Puppet::Relationship "#{source} => #{target}" end + def inspect + "{ #{source} => #{target} }" + end + def to_pson_data_hash data = { 'source' => source.to_s, diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb index 30f24591c..99a9fc177 100644 --- a/lib/puppet/reports/store.rb +++ b/lib/puppet/reports/store.rb @@ -8,24 +8,6 @@ Puppet::Reports.register_report(:store) do to perform some maintenance on them if you use this report (it's the only default report)." - def mkclientdir(client, dir) - config = Puppet::Util::Settings.new - - config.setdefaults( - "reportclient-#{client}".to_sym, - "client-#{client}-dir" => { :default => dir, - :mode => 0750, - :desc => "Client dir for #{client}", - :owner => 'service', - :group => 'service' - }, - - :noop => [false, "Used by settings internally."] - ) - - config.use("reportclient-#{client}".to_sym) - end - def process # We don't want any tracking back in the fs. Unlikely, but there # you go. @@ -33,7 +15,7 @@ Puppet::Reports.register_report(:store) do dir = File.join(Puppet[:reportdir], client) - mkclientdir(client, dir) unless FileTest.exists?(dir) + Dir.mkdir(dir, 0750) unless FileTest.exists?(dir) # Now store the report. now = Time.now.gmtime diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb index 4f0d50750..e832804f5 100644 --- a/lib/puppet/resource.rb +++ b/lib/puppet/resource.rb @@ -46,6 +46,10 @@ class Puppet::Resource resource end + def inspect + "#{@type}[#{@title}]#{to_hash.inspect}" + end + def to_pson_data_hash data = ([:type, :title, :tags] + ATTRIBUTES).inject({}) do |hash, param| next hash unless value = self.send(param) @@ -201,8 +205,13 @@ class Puppet::Resource tag(self.title) if valid_tag?(self.title) @reference = Reference.new(@type,@title) # for serialization compatibility with 0.25.x - - raise ArgumentError, "Invalid resource type #{type}" if strict? and ! resource_type + if strict? and ! resource_type + if @type == 'Class' + raise ArgumentError, "Could not find declared class #{title}" + else + raise ArgumentError, "Invalid resource type #{type}" + end + end end def ref diff --git a/lib/puppet/resource/status.rb b/lib/puppet/resource/status.rb index 2bdfbbfef..dea8c105d 100644 --- a/lib/puppet/resource/status.rb +++ b/lib/puppet/resource/status.rb @@ -4,13 +4,15 @@ module Puppet include Puppet::Util::Tagging include Puppet::Util::Logging - ATTRIBUTES = [:resource, :node, :version, :file, :line, :current_values, :skipped_reason, :status, :evaluation_time, :change_count] - attr_accessor *ATTRIBUTES + attr_accessor :resource, :node, :file, :line, :current_values, :status, :evaluation_time STATES = [:skipped, :failed, :failed_to_restart, :restarted, :changed, :out_of_sync, :scheduled] attr_accessor *STATES attr_reader :source_description, :default_log_level, :time, :resource + attr_reader :change_count, :out_of_sync_count, :resource_type, :title + + YAML_ATTRIBUTES = %w{@resource @file @line @evaluation_time @change_count @out_of_sync_count @tags @time @events @out_of_sync @changed @resource_type @title @skipped @failed} # Provide a boolean method for each of the states. STATES.each do |attr| @@ -28,6 +30,13 @@ module Puppet @events << event if event.status == 'failure' self.failed = true + elsif event.status == 'success' + @change_count += 1 + @changed = true + end + if event.status != 'audit' + @out_of_sync_count += 1 + @out_of_sync = true end end @@ -38,14 +47,26 @@ module Puppet def initialize(resource) @source_description = resource.path @resource = resource.to_s + @change_count = 0 + @out_of_sync_count = 0 + @changed = false + @out_of_sync = false + @skipped = false + @failed = false - [:file, :line, :version].each do |attr| + [:file, :line].each do |attr| send(attr.to_s + "=", resource.send(attr)) end tag(*resource.tags) @time = Time.now @events = [] + @resource_type = resource.type.to_s.capitalize + @title = resource.title + end + + def to_yaml_properties + (YAML_ATTRIBUTES & instance_variables).sort end private diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb index 77824845d..b9cf6991a 100644 --- a/lib/puppet/resource/type.rb +++ b/lib/puppet/resource/type.rb @@ -145,18 +145,26 @@ class Puppet::Resource::Type # classes and nodes. No parameters are be supplied--if this is a # parameterized class, then all parameters take on their default # values. - def ensure_in_catalog(scope) + def ensure_in_catalog(scope, parameters=nil) type == :definition and raise ArgumentError, "Cannot create resources for defined resource types" resource_type = type == :hostclass ? :class : :node # Do nothing if the resource already exists; this makes sure we don't # get multiple copies of the class resource, which helps provide the # singleton nature of classes. - if resource = scope.catalog.resource(resource_type, name) + # we should not do this for classes with parameters + # if parameters are passed, we should still try to create the resource + # even if it exists so that we can fail + # this prevents us from being able to combine param classes with include + if resource = scope.catalog.resource(resource_type, name) and !parameters return resource end - resource = Puppet::Parser::Resource.new(resource_type, name, :scope => scope, :source => self) + if parameters + parameters.each do |k,v| + resource.set_parameter(k,v) + end + end instantiate_resource(scope, resource) scope.compiler.add_resource(scope, resource) resource @@ -224,6 +232,19 @@ class Puppet::Resource::Type set[param] = true end + if @type == :hostclass + scope.setvar("title", resource.title.to_s.downcase) unless set.include? :title + scope.setvar("name", resource.name.to_s.downcase ) unless set.include? :name + else + scope.setvar("title", resource.title ) unless set.include? :title + scope.setvar("name", resource.name ) unless set.include? :name + end + scope.setvar("module_name", module_name) if module_name and ! set.include? :module_name + + if caller_name = scope.parent_module_name and ! set.include?(:caller_module_name) + scope.setvar("caller_module_name", caller_name) + end + scope.class_set(self.name,scope) if hostclass? or node? # Verify that all required arguments are either present or # have been provided with defaults. arguments.each do |param, default| @@ -240,19 +261,6 @@ class Puppet::Resource::Type resource[param] = value end - if @type == :hostclass - scope.setvar("title", resource.title.to_s.downcase) unless set.include? :title - scope.setvar("name", resource.name.to_s.downcase ) unless set.include? :name - else - scope.setvar("title", resource.title ) unless set.include? :title - scope.setvar("name", resource.name ) unless set.include? :name - end - scope.setvar("module_name", module_name) if module_name and ! set.include? :module_name - - if caller_name = scope.parent_module_name and ! set.include?(:caller_module_name) - scope.setvar("caller_module_name", caller_name) - end - scope.class_set(self.name,scope) if hostclass? or node? end # Create a new subscope in which to evaluate our code. diff --git a/lib/puppet/resource/type_collection.rb b/lib/puppet/resource/type_collection.rb index a96927613..6ab978f7c 100644 --- a/lib/puppet/resource/type_collection.rb +++ b/lib/puppet/resource/type_collection.rb @@ -25,6 +25,10 @@ class Puppet::Resource::TypeCollection end end + def inspect + "TypeCollection" + { :hostclasses => @hostclasses.keys, :definitions => @definitions.keys, :nodes => @nodes.keys }.inspect + end + def <<(thing) add(thing) self diff --git a/lib/puppet/simple_graph.rb b/lib/puppet/simple_graph.rb index 6d153b46d..9d7f218a6 100644 --- a/lib/puppet/simple_graph.rb +++ b/lib/puppet/simple_graph.rb @@ -395,6 +395,10 @@ class Puppet::SimpleGraph @vertex = vertex @adjacencies = adjacencies end + + def inspect + { :@adjacencies => @adjacencies, :@vertex => @vertex.to_s }.inspect + end end # instance_variable_get is used by Object.to_zaml to get instance diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index 0c226ca6a..d65067c70 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -63,7 +63,7 @@ class Puppet::SSL::CertificateAuthority store = nil store = autosign_store(auto) if auto != true - Puppet::SSL::CertificateRequest.search("*").each do |csr| + Puppet::SSL::CertificateRequest.indirection.search("*").each do |csr| sign(csr.name) if auto == true or store.allowed?(csr.name, "127.1.1.1") end end @@ -93,10 +93,10 @@ class Puppet::SSL::CertificateAuthority # Retrieve (or create, if necessary) the certificate revocation list. def crl unless defined?(@crl) - unless @crl = Puppet::SSL::CertificateRevocationList.find(Puppet::SSL::CA_NAME) + unless @crl = Puppet::SSL::CertificateRevocationList.indirection.find(Puppet::SSL::CA_NAME) @crl = Puppet::SSL::CertificateRevocationList.new(Puppet::SSL::CA_NAME) @crl.generate(host.certificate.content, host.key.content) - @crl.save + Puppet::SSL::CertificateRevocationList.indirection.save(@crl) end end @crl @@ -109,7 +109,7 @@ class Puppet::SSL::CertificateAuthority # Generate a new certificate. def generate(name) - raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.find(name) + raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.indirection.find(name) host = Puppet::SSL::Host.new(name) host.generate_certificate_request @@ -169,7 +169,7 @@ class Puppet::SSL::CertificateAuthority # List all signed certificates. def list - Puppet::SSL::Certificate.search("*").collect { |c| c.name } + Puppet::SSL::Certificate.indirection.search("*").collect { |c| c.name } end # Read the next serial from the serial file, and increment the @@ -199,14 +199,14 @@ class Puppet::SSL::CertificateAuthority # Print a given host's certificate as text. def print(name) - (cert = Puppet::SSL::Certificate.find(name)) ? cert.to_text : nil + (cert = Puppet::SSL::Certificate.indirection.find(name)) ? cert.to_text : nil end # Revoke a given certificate. def revoke(name) raise ArgumentError, "Cannot revoke certificates when the CRL is disabled" unless crl - if cert = Puppet::SSL::Certificate.find(name) + if cert = Puppet::SSL::Certificate.indirection.find(name) serial = cert.content.serial elsif ! serial = inventory.serial(name) raise ArgumentError, "Could not find a serial number for #{name}" @@ -229,7 +229,7 @@ class Puppet::SSL::CertificateAuthority csr = self_signing_csr issuer = csr.content else - unless csr = Puppet::SSL::CertificateRequest.find(hostname) + unless csr = Puppet::SSL::CertificateRequest.indirection.find(hostname) raise ArgumentError, "Could not find certificate request for #{hostname}" end issuer = host.certificate.content @@ -248,17 +248,17 @@ class Puppet::SSL::CertificateAuthority # Save the now-signed cert. This should get routed correctly depending # on the certificate type. - cert.save + Puppet::SSL::Certificate.indirection.save(cert) # And remove the CSR if this wasn't self signed. - Puppet::SSL::CertificateRequest.destroy(csr.name) unless self_signing_csr + Puppet::SSL::CertificateRequest.indirection.destroy(csr.name) unless self_signing_csr cert end # Verify a given host's certificate. def verify(name) - unless cert = Puppet::SSL::Certificate.find(name) + unless cert = Puppet::SSL::Certificate.indirection.find(name) raise ArgumentError, "Could not find a certificate for #{name}" end store = OpenSSL::X509::Store.new @@ -271,7 +271,7 @@ class Puppet::SSL::CertificateAuthority end def fingerprint(name, md = :MD5) - unless cert = Puppet::SSL::Certificate.find(name) || Puppet::SSL::CertificateRequest.find(name) + unless cert = Puppet::SSL::Certificate.indirection.find(name) || Puppet::SSL::CertificateRequest.indirection.find(name) raise ArgumentError, "Could not find a certificate or csr for #{name}" end cert.fingerprint(md) @@ -279,6 +279,6 @@ class Puppet::SSL::CertificateAuthority # List the waiting certificate requests. def waiting? - Puppet::SSL::CertificateRequest.search("*").collect { |r| r.name } + Puppet::SSL::CertificateRequest.indirection.search("*").collect { |r| r.name } end end diff --git a/lib/puppet/ssl/certificate_request.rb b/lib/puppet/ssl/certificate_request.rb index 2f6cae3f5..8c83339a1 100644 --- a/lib/puppet/ssl/certificate_request.rb +++ b/lib/puppet/ssl/certificate_request.rb @@ -5,7 +5,20 @@ class Puppet::SSL::CertificateRequest < Puppet::SSL::Base wraps OpenSSL::X509::Request extend Puppet::Indirector - indirects :certificate_request, :terminus_class => :file + + # If auto-signing is on, sign any certificate requests as they are saved. + module AutoSigner + def save(instance, key = nil) + super + + # Try to autosign the CSR. + if ca = Puppet::SSL::CertificateAuthority.instance + ca.autosign + end + end + end + + indirects :certificate_request, :terminus_class => :file, :extend => AutoSigner # Convert a string into an instance. def self.from_s(string) @@ -46,13 +59,4 @@ class Puppet::SSL::CertificateRequest < Puppet::SSL::Base Puppet.info "Certificate Request fingerprint (md5): #{fingerprint}" @content end - - def save(args = {}) - super() - - # Try to autosign the CSR. - if ca = Puppet::SSL::CertificateAuthority.instance - ca.autosign - end - end end diff --git a/lib/puppet/ssl/certificate_revocation_list.rb b/lib/puppet/ssl/certificate_revocation_list.rb index 44e0a9e22..293f4b8c0 100644 --- a/lib/puppet/ssl/certificate_revocation_list.rb +++ b/lib/puppet/ssl/certificate_revocation_list.rb @@ -79,6 +79,6 @@ class Puppet::SSL::CertificateRevocationList < Puppet::SSL::Base @content.sign(cakey, OpenSSL::Digest::SHA1.new) - save + Puppet::SSL::CertificateRevocationList.indirection.save(self) end end diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb index 8a6f0aa6d..7f71ced99 100644 --- a/lib/puppet/ssl/host.rb +++ b/lib/puppet/ssl/host.rb @@ -43,31 +43,31 @@ class Puppet::SSL::Host # Configure how our various classes interact with their various terminuses. def self.configure_indirection(terminus, cache = nil) - Certificate.terminus_class = terminus - CertificateRequest.terminus_class = terminus - CertificateRevocationList.terminus_class = terminus + Certificate.indirection.terminus_class = terminus + CertificateRequest.indirection.terminus_class = terminus + CertificateRevocationList.indirection.terminus_class = terminus if cache # This is weird; we don't actually cache our keys, we # use what would otherwise be the cache as our normal # terminus. - Key.terminus_class = cache + Key.indirection.terminus_class = cache else - Key.terminus_class = terminus + Key.indirection.terminus_class = terminus end if cache - Certificate.cache_class = cache - CertificateRequest.cache_class = cache - CertificateRevocationList.cache_class = cache + Certificate.indirection.cache_class = cache + CertificateRequest.indirection.cache_class = cache + CertificateRevocationList.indirection.cache_class = cache else # Make sure we have no cache configured. puppet master # switches the configurations around a bit, so it's important # that we specify the configs for absolutely everything, every # time. - Certificate.cache_class = nil - CertificateRequest.cache_class = nil - CertificateRevocationList.cache_class = nil + Certificate.indirection.cache_class = nil + CertificateRequest.indirection.cache_class = nil + CertificateRevocationList.indirection.cache_class = nil end end @@ -94,7 +94,7 @@ class Puppet::SSL::Host # Remove all traces of a given host def self.destroy(name) - [Key, Certificate, CertificateRequest].collect { |part| part.destroy(name) }.any? { |x| x } + [Key, Certificate, CertificateRequest].collect { |part| part.indirection.destroy(name) }.any? { |x| x } end # Search for more than one host, optionally only specifying @@ -106,7 +106,7 @@ class Puppet::SSL::Host # Collect the results from each class, flatten them, collect all of the names, make the name list unique, # then create a Host instance for each one. - classlist.collect { |klass| klass.search }.flatten.collect { |r| r.name }.uniq.collect do |name| + classlist.collect { |klass| klass.indirection.search }.flatten.collect { |r| r.name }.uniq.collect do |name| new(name) end end @@ -117,7 +117,7 @@ class Puppet::SSL::Host end def key - @key ||= Key.find(name) + @key ||= Key.indirection.find(name) end # This is the private key; we can create it from scratch @@ -126,7 +126,7 @@ class Puppet::SSL::Host @key = Key.new(name) @key.generate begin - @key.save + Key.indirection.save(@key) rescue @key = nil raise @@ -135,7 +135,7 @@ class Puppet::SSL::Host end def certificate_request - @certificate_request ||= CertificateRequest.find(name) + @certificate_request ||= CertificateRequest.indirection.find(name) end # Our certificate request requires the key but that's all. @@ -144,7 +144,7 @@ class Puppet::SSL::Host @certificate_request = CertificateRequest.new(name) @certificate_request.generate(key.content) begin - @certificate_request.save + CertificateRequest.indirection.save(@certificate_request) rescue @certificate_request = nil raise @@ -159,8 +159,8 @@ class Puppet::SSL::Host # get the CA cert first, since it's required for the normal cert # to be of any use. - return nil unless Certificate.find("ca") unless ca? - return nil unless @certificate = Certificate.find(name) + return nil unless Certificate.indirection.find("ca") unless ca? + return nil unless @certificate = Certificate.indirection.find(name) unless certificate_matches_key? raise Puppet::Error, "Retrieved certificate does not match private key; please remove certificate from server and regenerate it with the current key" @@ -212,7 +212,7 @@ class Puppet::SSL::Host @ssl_store.add_file(Puppet[:localcacert]) # If there's a CRL, add it to our store. - if crl = Puppet::SSL::CertificateRevocationList.find(CA_NAME) + if crl = Puppet::SSL::CertificateRevocationList.indirection.find(CA_NAME) @ssl_store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation] @ssl_store.add_crl(crl.content) end diff --git a/lib/puppet/ssl/inventory.rb b/lib/puppet/ssl/inventory.rb index b2b402a53..e094da100 100644 --- a/lib/puppet/ssl/inventory.rb +++ b/lib/puppet/ssl/inventory.rb @@ -36,7 +36,7 @@ class Puppet::SSL::Inventory f.print "# Inventory of signed certificates\n# SERIAL NOT_BEFORE NOT_AFTER SUBJECT\n" end - Puppet::SSL::Certificate.search("*").each { |cert| add(cert) } + Puppet::SSL::Certificate.indirection.search("*").each { |cert| add(cert) } end # Find the serial number for a given certificate. diff --git a/lib/puppet/sslcertificates/monkey_patch.rb b/lib/puppet/sslcertificates/monkey_patch.rb deleted file mode 100644 index 663b944c1..000000000 --- a/lib/puppet/sslcertificates/monkey_patch.rb +++ /dev/null @@ -1,6 +0,0 @@ -# This is the file that we use to add indirection to all the SSL Certificate classes. - -require 'puppet/indirector' - -OpenSSL::PKey::RSA.extend Puppet::Indirector -OpenSSL::PKey::RSA.indirects :ssl_rsa, :terminus_class => :file diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index dcd9aad0a..eba601cfe 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -6,7 +6,6 @@ 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' @@ -222,12 +221,6 @@ class Puppet::Transaction 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") @@ -238,7 +231,7 @@ class Puppet::Transaction def initialize(catalog) @catalog = catalog - @report = Report.new + @report = Puppet::Transaction::Report.new("apply") @event_manager = Puppet::Transaction::EventManager.new(self) @@ -285,26 +278,6 @@ class Puppet::Transaction 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 diff --git a/lib/puppet/transaction/change.rb b/lib/puppet/transaction/change.rb deleted file mode 100644 index ecc3b5a5f..000000000 --- a/lib/puppet/transaction/change.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'puppet/transaction' -require 'puppet/transaction/event' - -# Handle all of the work around performing an actual change, -# including calling 'sync' on the properties and producing events. -class Puppet::Transaction::Change - attr_accessor :is, :should, :property, :proxy, :auditing - - def auditing? - auditing - end - - # Create our event object. - def event - result = property.event - result.previous_value = is - result.desired_value = should - result - end - - def initialize(property, currentvalue) - @property = property - @is = currentvalue - - @should = property.should - - @changed = false - end - - def apply - return audit_event if auditing? - return noop_event if noop? - - property.sync - - result = event - result.message = property.change_to_s(is, should) - result.status = "success" - result.send_log - result - rescue => detail - puts detail.backtrace if Puppet[:trace] - result = event - result.status = "failure" - - result.message = "change from #{property.is_to_s(is)} to #{property.should_to_s(should)} failed: #{detail}" - result.send_log - result - end - - # Is our property noop? This is used for generating special events. - def noop? - @property.noop - end - - # The resource that generated this change. This is used for handling events, - # and the proxy resource is used for generated resources, since we can't - # send an event to a resource we don't have a direct relationship with. If we - # have a proxy resource, then the events will be considered to be from - # that resource, rather than us, so the graph resolution will still work. - def resource - self.proxy || @property.resource - end - - def to_s - "change #{@property.change_to_s(@is, @should)}" - end - - private - - def audit_event - # This needs to store the appropriate value, and then produce a new event - result = event - result.message = "audit change: previously recorded value #{property.should_to_s(should)} has been changed to #{property.is_to_s(is)}" - result.status = "audit" - result.send_log - result - end - - def noop_event - result = event - result.message = "is #{property.is_to_s(is)}, should be #{property.should_to_s(should)} (noop)" - result.status = "noop" - result.send_log - result - end -end diff --git a/lib/puppet/transaction/event.rb b/lib/puppet/transaction/event.rb index e5e5793da..cd695cff8 100644 --- a/lib/puppet/transaction/event.rb +++ b/lib/puppet/transaction/event.rb @@ -7,7 +7,8 @@ class Puppet::Transaction::Event include Puppet::Util::Tagging include Puppet::Util::Logging - ATTRIBUTES = [:name, :resource, :property, :previous_value, :desired_value, :status, :message, :node, :version, :file, :line, :source_description] + ATTRIBUTES = [:name, :resource, :property, :previous_value, :desired_value, :historical_value, :status, :message, :file, :line, :source_description, :audited] + YAML_ATTRIBUTES = %w{@audited @property @previous_value @desired_value @historical_value @message @name @status @time} attr_accessor *ATTRIBUTES attr_writer :tags attr_accessor :time @@ -15,9 +16,9 @@ class Puppet::Transaction::Event EVENT_STATUSES = %w{noop success failure audit} - def initialize(*args) - options = args.last.is_a?(Hash) ? args.pop : ATTRIBUTES.inject({}) { |hash, attr| hash[attr] = args.pop; hash } - options.each { |attr, value| send(attr.to_s + "=", value) unless value.nil? } + def initialize(options = {}) + @audited = false + options.each { |attr, value| send(attr.to_s + "=", value) } @time = Time.now end @@ -46,6 +47,10 @@ class Puppet::Transaction::Event message end + def to_yaml_properties + (YAML_ATTRIBUTES & instance_variables).sort + end + private # If it's a failure, use 'err', else use either the resource's log level (if available) diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 1d3091428..16fee42ae 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -10,7 +10,8 @@ class Puppet::Transaction::Report indirects :report, :terminus_class => :processor - attr_reader :resource_statuses, :logs, :metrics, :host, :time + attr_accessor :configuration_version + attr_reader :resource_statuses, :logs, :metrics, :host, :time, :kind, :status # This is necessary since Marshall doesn't know how to # dump hash with default proc (see below @records) @@ -42,20 +43,37 @@ class Puppet::Transaction::Report @resource_statuses[status.resource] = status end - def calculate_metrics - calculate_resource_metrics - calculate_time_metrics - calculate_change_metrics - calculate_event_metrics + def compute_status(resource_metrics, change_metric) + if (resource_metrics["failed"] || 0) > 0 + 'failed' + elsif change_metric > 0 + 'changed' + else + 'unchanged' + end + end + + def finalize_report + resource_metrics = add_metric(:resources, calculate_resource_metrics) + add_metric(:time, calculate_time_metrics) + change_metric = calculate_change_metric + add_metric(:changes, {"total" => change_metric}) + add_metric(:events, calculate_event_metrics) + @status = compute_status(resource_metrics, change_metric) end - def initialize + 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 + @status = 'failed' # assume failed until the report is finalized end def name @@ -110,44 +128,45 @@ class Puppet::Transaction::Report # 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 |= 2 if @metrics["changes"]["total"] > 0 + status |= 4 if @metrics["resources"]["failed"] > 0 status end + def to_yaml_properties + (instance_variables - ["@external_times"]).sort + 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) + def calculate_change_metric + resource_statuses.map { |name, status| status.change_count || 0 }.inject(0) { |a,b| a+b } end def calculate_event_metrics metrics = Hash.new(0) + metrics["total"] = 0 resource_statuses.each do |name, status| - metrics[:total] += status.events.length + metrics["total"] += status.events.length status.events.each do |event| metrics[event.status] += 1 end end - add_metric(:events, metrics) + metrics end def calculate_resource_metrics metrics = Hash.new(0) - metrics[:total] = resource_statuses.length + 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) + metrics[state.to_s] += 1 if status.send(state) end end - add_metric(:resources, metrics) + metrics end def calculate_time_metrics @@ -161,6 +180,8 @@ class Puppet::Transaction::Report metrics[name.to_s.downcase] = value end - add_metric(:time, metrics) + metrics["total"] = metrics.values.inject(0) { |a,b| a+b } + + metrics end end diff --git a/lib/puppet/transaction/resource_harness.rb b/lib/puppet/transaction/resource_harness.rb index 29ec9a539..4a3d35e0d 100644 --- a/lib/puppet/transaction/resource_harness.rb +++ b/lib/puppet/transaction/resource_harness.rb @@ -7,92 +7,138 @@ class Puppet::Transaction::ResourceHarness attr_reader :transaction def allow_changes?(resource) - return true unless resource.purging? and resource.deleting? - return true unless deps = relationship_graph.dependents(resource) and ! deps.empty? and deps.detect { |d| ! d.deleting? } - - deplabel = deps.collect { |r| r.ref }.join(",") - plurality = deps.length > 1 ? "":"s" - resource.warning "#{deplabel} still depend#{plurality} on me -- not purging" - false - end - - def apply_changes(status, changes) - changes.each do |change| - status << change.apply - - cache(change.property.resource, change.property.name, change.is) if change.auditing? + if resource.purging? and resource.deleting? and deps = relationship_graph.dependents(resource) \ + and ! deps.empty? and deps.detect { |d| ! d.deleting? } + deplabel = deps.collect { |r| r.ref }.join(",") + plurality = deps.length > 1 ? "":"s" + resource.warning "#{deplabel} still depend#{plurality} on me -- not purging" + false + else + true end - status.changed = true end - # Used mostly for scheduling at this point. + # Used mostly for scheduling and auditing at this point. def cached(resource, name) Puppet::Util::Storage.cache(resource)[name] end - # Used mostly for scheduling at this point. + # Used mostly for scheduling and auditing at this point. def cache(resource, name, value) Puppet::Util::Storage.cache(resource)[name] = value end - def changes_to_perform(status, resource) + def perform_changes(resource) current = resource.retrieve_resource cache resource, :checked, Time.now return [] if ! allow_changes?(resource) - audited = copy_audited_parameters(resource, current) - - if param = resource.parameter(:ensure) - return [] if absent_and_not_being_created?(current, param) - return [Puppet::Transaction::Change.new(param, current[:ensure])] unless ensure_is_insync?(current, param) - return [] if ensure_should_be_absent?(current, param) + current_values = current.to_hash + historical_values = Puppet::Util::Storage.cache(resource).dup + desired_values = {} + resource.properties.each do |property| + desired_values[property.name] = property.should end + audited_params = (resource[:audit] || []).map { |p| p.to_sym } + synced_params = [] - resource.properties.reject { |p| p.name == :ensure }.reject do |param| - param.should.nil? - end.reject do |param| - param_is_insync?(current, param) - end.collect do |param| - change = Puppet::Transaction::Change.new(param, current[param.name]) - change.auditing = true if audited.include?(param.name) - change + # Record the current state in state.yml. + audited_params.each do |param| + cache(resource, param, current_values[param]) end - end - def copy_audited_parameters(resource, current) - return [] unless audit = resource[:audit] - audit = Array(audit).collect { |p| p.to_sym } - audited = [] - audit.find_all do |param| - next if resource[param] + # Update the machine state & create logs/events + events = [] + ensure_param = resource.parameter(:ensure) + if desired_values[:ensure] && !ensure_param.safe_insync?(current_values[:ensure]) + events << apply_parameter(ensure_param, current_values[:ensure], audited_params.include?(:ensure), historical_values[:ensure]) + synced_params << :ensure + elsif current_values[:ensure] != :absent + work_order = resource.properties # Note: only the resource knows what order to apply changes in + work_order.each do |param| + if desired_values[param.name] && !param.safe_insync?(current_values[param.name]) + events << apply_parameter(param, current_values[param.name], audited_params.include?(param.name), historical_values[param.name]) + synced_params << param.name + end + end + end - if value = cached(resource, param) - resource[param] = value - audited << param + # Add more events to capture audit results + audited_params.each do |param_name| + if historical_values.include?(param_name) + if historical_values[param_name] != current_values[param_name] && !synced_params.include?(param_name) + event = create_change_event(resource.parameter(param_name), current_values[param_name], true, historical_values[param_name]) + event.send_log + events << event + end else - resource.debug "Storing newly-audited value #{current[param]} for #{param}" - cache(resource, param, current[param]) + resource.property(param_name).notice "audit change: newly-recorded value #{current_values[param_name]}" + end + end + + events + end + + def create_change_event(property, current_value, do_audit, historical_value) + event = property.event + event.previous_value = current_value + event.desired_value = property.should + event.historical_value = historical_value + + if do_audit + event.audited = true + event.status = "audit" + if historical_value != current_value + event.message = "audit change: previously recorded value #{property.is_to_s(historical_value)} has been changed to #{property.is_to_s(current_value)}" end end - audited + event + end + + def apply_parameter(property, current_value, do_audit, historical_value) + event = create_change_event(property, current_value, do_audit, historical_value) + + if do_audit && historical_value && historical_value != current_value + brief_audit_message = " (previously recorded value was #{property.is_to_s(historical_value)})" + else + brief_audit_message = "" + end + + if property.noop + event.message = "current_value #{property.is_to_s(current_value)}, should be #{property.should_to_s(property.should)} (noop)#{brief_audit_message}" + event.status = "noop" + else + property.sync + event.message = [ property.change_to_s(current_value, property.should), brief_audit_message ].join + event.status = "success" + end + event + rescue => detail + puts detail.backtrace if Puppet[:trace] + event.status = "failure" + + event.message = "change from #{property.is_to_s(current_value)} to #{property.should_to_s(property.should)} failed: #{detail}" + event + ensure + event.send_log end def evaluate(resource) start = Time.now status = Puppet::Resource::Status.new(resource) - if changes = changes_to_perform(status, resource) and ! changes.empty? - status.out_of_sync = true - status.change_count = changes.length - apply_changes(status, changes) - if ! resource.noop? - cache(resource, :synced, Time.now) - resource.flush if resource.respond_to?(:flush) - end + perform_changes(resource).each do |event| + status << event + end + + if status.changed? && ! resource.noop? + cache(resource, :synced, Time.now) + resource.flush if resource.respond_to?(:flush) end + return status rescue => detail resource.fail "Could not create resource status: #{detail}" unless status @@ -129,22 +175,4 @@ class Puppet::Transaction::ResourceHarness return nil unless name = resource[:schedule] resource.catalog.resource(:schedule, name) || resource.fail("Could not find schedule #{name}") end - - private - - def absent_and_not_being_created?(current, param) - current[:ensure] == :absent and param.should.nil? - end - - def ensure_is_insync?(current, param) - param.insync?(current[:ensure]) - end - - def ensure_should_be_absent?(current, param) - param.should == :absent - end - - def param_is_insync?(current, param) - param.insync?(current[param.name]) - end end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 1b6e7dcd7..e03650b54 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -446,7 +446,7 @@ class Type # Create a transaction event. Called by Transaction or by # a property. def event(options = {}) - Puppet::Transaction::Event.new({:resource => self, :file => file, :line => line, :tags => tags, :version => version}.merge(options)) + Puppet::Transaction::Event.new({:resource => self, :file => file, :line => line, :tags => tags}.merge(options)) end # Let the catalog determine whether a given cached value is @@ -648,7 +648,7 @@ class Type "The is value is not in the is array for '#{property.name}'" end ensureis = is[property] - if property.insync?(ensureis) and property.should == :absent + if property.safe_insync?(ensureis) and property.should == :absent return true end end @@ -660,7 +660,7 @@ class Type end propis = is[property] - unless property.insync?(propis) + unless property.safe_insync?(propis) property.debug("Not in sync: #{propis.inspect} vs #{property.should.inspect}") insync = false #else diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb index 76399d693..4b18e71f9 100755 --- a/lib/puppet/type/cron.rb +++ b/lib/puppet/type/cron.rb @@ -54,11 +54,7 @@ Puppet::Type.newtype(:cron) do # We have to override the parent method, because we consume the entire # "should" array def insync?(is) - if @should - self.is_to_s(is) == self.should_to_s - else - true - end + self.is_to_s(is) == self.should_to_s end # A method used to do parameter input handling. Converts integers diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb index 606888c75..daa49e223 100755 --- a/lib/puppet/type/exec.rb +++ b/lib/puppet/type/exec.rb @@ -9,41 +9,11 @@ module Puppet commands is to use the checks like `creates` to avoid running the command unless some condition is met. - Note also that you can restrict an `exec` to only run when it receives + Note that you can restrict an `exec` to only run when it receives events by using the `refreshonly` parameter; this is a useful way to have your configuration respond to events with arbitrary commands. - It is worth noting that `exec` is special, in that it is not - currently considered an error to have multiple `exec` instances - with the same name. This was done purely because it had to be this - way in order to get certain functionality, but it complicates things. - In particular, you will not be able to use `exec` instances that - share their commands with other instances as a dependency, since - Puppet has no way of knowing which instance you mean. - - For example: - - # defined in the production class - exec { \"make\": - cwd => \"/prod/build/dir\", - path => \"/usr/bin:/usr/sbin:/bin\" - } - - . etc. . - - # defined in the test class - exec { \"make\": - cwd => \"/test/build/dir\", - path => \"/usr/bin:/usr/sbin:/bin\" - } - - Any other type would throw an error, complaining that you had - the same instance being managed in multiple places, but these are - obviously different images, so `exec` had to be treated specially. - - It is recommended to avoid duplicate names whenever possible. - - Note that if an `exec` receives an event from another resource, + Note also that if an `exec` receives an event from another resource, it will get executed again (or execute the command specified in `refresh`, if there is one). There is a strong tendency to use `exec` to do whatever work Puppet @@ -335,7 +305,7 @@ module Puppet # Pull down the main aliases file file { \"/etc/aliases\": source => \"puppet://server/module/aliases\" - } + } # Rebuild the database, but only when the file changes exec { newaliases: @@ -664,7 +634,7 @@ module Puppet def validatecmd(cmd) exe = extractexe(cmd) # if we're not fully qualified, require a path - self.fail "'#{cmd}' is both unqualifed and specified no search path" if File.expand_path(exe) != exe and self[:path].nil? + self.fail "'#{cmd}' is not qualified and no path was specified. Please qualify the command or specify a path." if File.expand_path(exe) != exe and self[:path].nil? end def extractexe(cmd) diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index f35a26408..963b9e5dd 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -34,7 +34,7 @@ Puppet::Type.newtype(:file) do validate do |value| # accept various path syntaxes: lone slash, posix, win32, unc - unless (Puppet.features.posix? and (value =~ /^\/$/ or value =~ /^\/[^\/]/)) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) + unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) fail Puppet::Error, "File paths must be fully qualified, not '#{value}'" end end @@ -271,17 +271,24 @@ Puppet::Type.newtype(:file) do end CREATORS = [:content, :source, :target] + SOURCE_ONLY_CHECKSUMS = [:none, :ctime, :mtime] validate do - count = 0 + creator_count = 0 CREATORS.each do |param| - count += 1 if self.should(param) + creator_count += 1 if self.should(param) end - count += 1 if @parameters.include?(:source) - self.fail "You cannot specify more than one of #{CREATORS.collect { |p| p.to_s}.join(", ")}" if count > 1 + creator_count += 1 if @parameters.include?(:source) + self.fail "You cannot specify more than one of #{CREATORS.collect { |p| p.to_s}.join(", ")}" if creator_count > 1 self.fail "You cannot specify a remote recursion without a source" if !self[:source] and self[:recurse] == :remote + self.fail "You cannot specify source when using checksum 'none'" if self[:checksum] == :none && !self[:source].nil? + + SOURCE_ONLY_CHECKSUMS.each do |checksum_type| + self.fail "You cannot specify content when using checksum '#{checksum_type}'" if self[:checksum] == checksum_type && !self[:content].nil? + end + self.warning "Possible error: recurselimit is set but not recurse, no recursion will happen" if !self[:recurse] and self[:recurselimit] end @@ -290,25 +297,8 @@ Puppet::Type.newtype(:file) do super(path.gsub(/\/+/, '/').sub(/\/$/, '')) end - # List files, but only one level deep. - def self.instances(base = "/") - return [] unless FileTest.directory?(base) - - files = [] - Dir.entries(base).reject { |e| - e == "." or e == ".." - }.each do |name| - path = File.join(base, name) - if obj = self[path] - obj[:audit] = :all - files << obj - else - files << self.new( - :name => path, :audit => :all - ) - end - end - files + def self.instances(base = '/') + return self.new(:name => base, :recurse => true, :recurselimit => 1, :audit => :all).recurse_local.values end @depthfirst = false @@ -587,7 +577,7 @@ Puppet::Type.newtype(:file) do def perform_recursion(path) - Puppet::FileServing::Metadata.search( + Puppet::FileServing::Metadata.indirection.search( path, :links => self[:links], @@ -718,8 +708,9 @@ Puppet::Type.newtype(:file) do mode = self.should(:mode) # might be nil umask = mode ? 000 : 022 + mode_int = mode ? mode.to_i(8) : nil - content_checksum = Puppet::Util.withumask(umask) { File.open(path, 'w', mode) { |f| write_content(f) } } + content_checksum = Puppet::Util.withumask(umask) { File.open(path, 'w', mode_int ) { |f| write_content(f) } } # And put our new file in place if use_temporary_file # This is only not true when our file is empty. @@ -778,7 +769,7 @@ Puppet::Type.newtype(:file) do # Make sure we get a new stat objct expire currentvalue = thing.retrieve - thing.sync unless thing.insync?(currentvalue) + thing.sync unless thing.safe_insync?(currentvalue) end end end @@ -796,3 +787,5 @@ require 'puppet/type/file/group' require 'puppet/type/file/mode' require 'puppet/type/file/type' require 'puppet/type/file/selcontext' # SELinux file context +require 'puppet/type/file/ctime' +require 'puppet/type/file/mtime' diff --git a/lib/puppet/type/file/checksum.rb b/lib/puppet/type/file/checksum.rb index 732460738..5586b1383 100755 --- a/lib/puppet/type/file/checksum.rb +++ b/lib/puppet/type/file/checksum.rb @@ -9,7 +9,7 @@ Puppet::Type.type(:file).newparam(:checksum) do The default checksum parameter, if checksums are enabled, is md5." - newvalues "md5", "md5lite", "timestamp", "mtime", "time", "none" + newvalues "md5", "md5lite", "mtime", "ctime", "none" defaultto :md5 diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb index b8f30a9c7..cf924f371 100755 --- a/lib/puppet/type/file/content.rb +++ b/lib/puppet/type/file/content.rb @@ -96,7 +96,6 @@ module Puppet end return true if ! @resource.replace? - return true unless self.should result = super @@ -168,6 +167,8 @@ module Puppet def each_chunk_from(source_or_content) if source_or_content.is_a?(String) yield source_or_content + elsif source_or_content.nil? && resource.parameter(:ensure) && [:present, :file].include?(resource.parameter(:ensure).value) + yield '' elsif source_or_content.nil? yield read_file_from_filebucket elsif self.class.standalone? diff --git a/lib/puppet/type/file/ctime.rb b/lib/puppet/type/file/ctime.rb new file mode 100644 index 000000000..24b098703 --- /dev/null +++ b/lib/puppet/type/file/ctime.rb @@ -0,0 +1,18 @@ +module Puppet + Puppet::Type.type(:file).newproperty(:ctime) do + desc "A read-only state to check the file ctime." + + def retrieve + current_value = :absent + if stat = @resource.stat(false) + current_value = stat.ctime + end + current_value + end + + validate do + fail "ctime is read-only" + end + end +end + diff --git a/lib/puppet/type/file/ensure.rb b/lib/puppet/type/file/ensure.rb index 967e06aee..4a68551ee 100755 --- a/lib/puppet/type/file/ensure.rb +++ b/lib/puppet/type/file/ensure.rb @@ -66,7 +66,7 @@ module Puppet end if mode Puppet::Util.withumask(000) do - Dir.mkdir(@resource[:path],mode) + Dir.mkdir(@resource[:path], mode.to_i(8)) end else Dir.mkdir(@resource[:path]) diff --git a/lib/puppet/type/file/mode.rb b/lib/puppet/type/file/mode.rb index 1ce56c843..2acd8b359 100755 --- a/lib/puppet/type/file/mode.rb +++ b/lib/puppet/type/file/mode.rb @@ -25,60 +25,26 @@ module Puppet @event = :file_changed - # Our modes are octal, so make sure they print correctly. Other - # valid values are symbols, basically - def is_to_s(currentvalue) - case currentvalue - when Integer - return "%o" % currentvalue - when Symbol - return currentvalue - else - raise Puppet::DevError, "Invalid current value for mode: #{currentvalue.inspect}" - end - end - - def should_to_s(newvalue = @should) - case newvalue - when Integer - return "%o" % newvalue - when Symbol - return newvalue - else - raise Puppet::DevError, "Invalid 'should' value for mode: #{newvalue.inspect}" - end - end - munge do |should| - # this is pretty hackish, but i need to make sure the number is in - # octal, yet the number can only be specified as a string right now - value = should - if value.is_a?(String) - unless value =~ /^\d+$/ - raise Puppet::Error, "File modes can only be numbers, not #{value.inspect}" - end - # Make sure our number looks like octal. - unless value =~ /^0/ - value = "0#{value}" - end - old = value - begin - value = Integer(value) - rescue ArgumentError => detail - raise Puppet::DevError, "Could not convert #{old.inspect} to integer" + if should.is_a?(String) + unless should =~ /^[0-7]+$/ + raise Puppet::Error, "File modes can only be octal numbers, not #{should.inspect}" end + should.to_i(8).to_s(8) + else + should.to_s(8) end - - return value end # If we're a directory, we need to be executable for all cases # that are readable. This should probably be selectable, but eh. def dirmask(value) if FileTest.directory?(@resource[:path]) + value = value.to_i(8) value |= 0100 if value & 0400 != 0 value |= 010 if value & 040 != 0 value |= 01 if value & 04 != 0 + value = value.to_s(8) end value @@ -101,7 +67,7 @@ module Puppet unless defined?(@fixed) @should &&= @should.collect { |s| self.dirmask(s) } end - return stat.mode & 007777 + return (stat.mode & 007777).to_s(8) else return :absent end @@ -111,7 +77,7 @@ module Puppet mode = self.should begin - File.chmod(mode, @resource[:path]) + File.chmod(mode.to_i(8), @resource[:path]) rescue => detail error = Puppet::Error.new("failed to chmod #{@resource[:path]}: #{detail.message}") error.set_backtrace detail.backtrace diff --git a/lib/puppet/type/file/mtime.rb b/lib/puppet/type/file/mtime.rb new file mode 100644 index 000000000..8ca7ed0d6 --- /dev/null +++ b/lib/puppet/type/file/mtime.rb @@ -0,0 +1,17 @@ +module Puppet + Puppet::Type.type(:file).newproperty(:mtime) do + desc "A read-only state to check the file mtime." + + def retrieve + current_value = :absent + if stat = @resource.stat(false) + current_value = stat.mtime + end + current_value + end + + validate do + fail "mtime is read-only" + end + end +end diff --git a/lib/puppet/type/file/owner.rb b/lib/puppet/type/file/owner.rb index d473da20e..483cc7fce 100755 --- a/lib/puppet/type/file/owner.rb +++ b/lib/puppet/type/file/owner.rb @@ -6,7 +6,7 @@ module Puppet @event = :file_changed def insync?(current) - provider.insync?(current, @should) + provider.is_owner_insync?(current, @should) end # We want to print names, not numbers diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index 7d03de2b0..ac06a26a1 100755 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -98,7 +98,7 @@ module Puppet cached_attr(:content) do raise Puppet::DevError, "No source for content was stored with the metadata" unless metadata.source - unless tmp = Puppet::FileServing::Content.find(metadata.source) + unless tmp = Puppet::FileServing::Content.indirection.find(metadata.source) fail "Could not find any content at %s" % metadata.source end tmp.content @@ -148,7 +148,7 @@ module Puppet result = nil value.each do |source| begin - if data = Puppet::FileServing::Metadata.find(source) + if data = Puppet::FileServing::Metadata.indirection.find(source) result = data result.source = source break @@ -169,7 +169,6 @@ module Puppet checks.delete(:checksum) resource[:audit] = checks - resource[:checksum] = :md5 unless resource.property(:checksum) end def local? diff --git a/lib/puppet/type/file/target.rb b/lib/puppet/type/file/target.rb index 9e7229dda..b9fe9213b 100644 --- a/lib/puppet/type/file/target.rb +++ b/lib/puppet/type/file/target.rb @@ -14,7 +14,7 @@ module Puppet # Only call mklink if ensure didn't call us in the first place. currentensure = @resource.property(:ensure).retrieve - mklink if @resource.property(:ensure).insync?(currentensure) + mklink if @resource.property(:ensure).safe_insync?(currentensure) end # Create our link. diff --git a/lib/puppet/type/file/type.rb b/lib/puppet/type/file/type.rb index eb50b81f9..4da54e2cb 100755 --- a/lib/puppet/type/file/type.rb +++ b/lib/puppet/type/file/type.rb @@ -3,23 +3,16 @@ module Puppet require 'etc' desc "A read-only state to check the file type." - #munge do |value| - # raise Puppet::Error, ":type is read-only" - #end - def retrieve - currentvalue = :absent + current_value = :absent if stat = @resource.stat(false) - currentvalue = stat.ftype + current_value = stat.ftype end - # so this state is never marked out of sync - @should = [currentvalue] - currentvalue + current_value end - - def sync - raise Puppet::Error, ":type is read-only" + validate do + fail "type is read-only" end end end diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb index 1af74d886..a770edab9 100755 --- a/lib/puppet/type/host.rb +++ b/lib/puppet/type/host.rb @@ -1,3 +1,5 @@ +require 'puppet/property/ordered_list' + module Puppet newtype(:host) do ensurable @@ -13,41 +15,24 @@ module Puppet end - newproperty(:host_aliases) do + # for now we use OrderedList to indicate that the order does matter. + newproperty(:host_aliases, :parent => Puppet::Property::OrderedList) do desc "Any aliases the host might have. Multiple values must be specified as an array." - def insync?(is) - is == @should + def delimiter + " " end - def is_to_s(currentvalue = @is) - currentvalue = [currentvalue] unless currentvalue.is_a? Array - currentvalue.join(" ") - end - - # We actually want to return the whole array here, not just the first - # value. - def should - if defined?(@should) - if @should == [:absent] - return :absent - else - return @should - end - else - return nil - end - end - - def should_to_s(newvalue = @should) - newvalue.join(" ") + def inclusive? + true end validate do |value| raise Puppet::Error, "Host aliases cannot include whitespace" if value =~ /\s/ raise Puppet::Error, "Host alias cannot be an empty string. Use an empty array to delete all host_aliases " if value =~ /^\s*$/ end + end newproperty(:comment) do @@ -56,7 +41,7 @@ module Puppet newproperty(:target) do desc "The file in which to store service information. Only used by - those providers that write to disk." + those providers that write to disk. On most systems this defaults to `/etc/hosts`." defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) @resource.class.defaultprovider.default_target diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb index e8c6b3290..915bb8e6c 100755 --- a/lib/puppet/type/mount.rb +++ b/lib/puppet/type/mount.rb @@ -89,7 +89,7 @@ module Puppet if prop.name == :ensure false else - ! prop.insync?(currentvalues[prop]) + ! prop.safe_insync?(currentvalues[prop]) end end.each { |prop| prop.sync }.length @resource.flush if oos > 0 @@ -200,7 +200,7 @@ module Puppet newvalues(:true, :false) defaultto do case Facter.value(:operatingsystem) - when "FreeBSD", "Darwin" + when "FreeBSD", "Darwin", "AIX" false else true diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index 51a866332..d73d90dff 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -109,8 +109,6 @@ module Puppet # Override the parent method, because we've got all kinds of # funky definitions of 'in sync'. def insync?(is) - @should ||= [] - @latest ||= nil @lateststamp ||= (Time.now.to_i - 1000) # Iterate across all of the should values, and see how they diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb index 786a50448..3ef044932 100644 --- a/lib/puppet/type/service.rb +++ b/lib/puppet/type/service.rb @@ -73,7 +73,7 @@ module Puppet if property = @resource.property(:enable) val = property.retrieve - property.sync unless property.insync?(val) + property.sync unless property.safe_insync?(val) end event @@ -146,10 +146,16 @@ module Puppet specified." end newparam(:status) do - desc "Specify a *status* command manually. If left - unspecified, the status method will be determined - automatically, usually by looking for the service in the - process table." + desc "Specify a *status* command manually. This command must + return 0 if the service is running and a nonzero value otherwise. + Ideally, these return codes should conform to + [the LSB's specification for init script status actions](http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html), + but puppet only considers the difference between 0 and nonzero + to be relevant. + + If left unspecified, the status method will be determined + automatically, usually by looking for the service in the process + table." end newparam(:stop) do diff --git a/lib/puppet/type/sshkey.rb b/lib/puppet/type/sshkey.rb index b7a1b8a8d..59a1a12f8 100755 --- a/lib/puppet/type/sshkey.rb +++ b/lib/puppet/type/sshkey.rb @@ -41,7 +41,7 @@ module Puppet raise Puppet::Error, "Aliases cannot include whitespace" end if value =~ /,/ - raise Puppet::Error, "Aliases cannot include whitespace" + raise Puppet::Error, "Aliases must be provided as an array, not a comma-separated list" end end end @@ -50,6 +50,11 @@ module Puppet desc "The host name that the key is associated with." isnamevar + + validate do |value| + raise Puppet::Error, "Resourcename cannot include whitespaces" if value =~ /\s/ + raise Puppet::Error, "No comma in resourcename allowed. If you want to specify aliases use the host_aliases property" if value.include?(',') + end end newproperty(:target) do diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb index c8110bb69..e7389a0d1 100755 --- a/lib/puppet/type/user.rb +++ b/lib/puppet/type/user.rb @@ -72,6 +72,11 @@ module Puppet end end + newproperty(:home) do + desc "The home directory of the user. The directory must be created + separately and is not currently checked for existence." + end + newproperty(:uid) do desc "The user ID. Must be specified numerically. For new users being created, if no user ID is specified then one will be @@ -107,8 +112,6 @@ module Puppet end def insync?(is) - return true unless self.should - # We know the 'is' is a number, so we need to convert the 'should' to a number, # too. @should.each do |value| @@ -138,11 +141,6 @@ module Puppet desc "A description of the user. Generally is a user's full name." end - newproperty(:home) do - desc "The home directory of the user. The directory must be created - separately and is not currently checked for existence." - end - newproperty(:shell) do desc "The user's login shell. The shell must exist and be executable." @@ -177,7 +175,7 @@ module Puppet end validate do |value| - if value.to_s !~ /^\d+$/ + if value.to_s !~ /^-?\d+$/ raise ArgumentError, "Password minimum age must be provided as a number" end end @@ -196,7 +194,7 @@ module Puppet end validate do |value| - if value.to_s !~ /^\d+$/ + if value.to_s !~ /^-?\d+$/ raise ArgumentError, "Password maximum age must be provided as a number" end end diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb index 160b2164d..9b4c79428 100644 --- a/lib/puppet/type/yumrepo.rb +++ b/lib/puppet/type/yumrepo.rb @@ -7,14 +7,14 @@ module Puppet class IniProperty < Puppet::Property def insync?(is) # A should property of :absent is the same as nil - if is.nil? && (should.nil? || should == :absent) + if is.nil? && should == :absent return true end super(is) end def sync - if insync?(retrieve) + if safe_insync?(retrieve) result = nil else result = set(self.should) diff --git a/lib/puppet/type/zfs.rb b/lib/puppet/type/zfs.rb index 1757931f8..be18ab5aa 100755 --- a/lib/puppet/type/zfs.rb +++ b/lib/puppet/type/zfs.rb @@ -12,6 +12,26 @@ module Puppet desc "The mountpoint property." end + newproperty(:recordsize) do + desc "The recordsize property." + end + + newproperty(:aclmode) do + desc "The aclmode property." + end + + newproperty(:aclinherit) do + desc "The aclinherit property." + end + + newproperty(:primarycache) do + desc "The primarycache property." + end + + newproperty(:secondarycache) do + desc "The secondarycache property." + end + newproperty(:compression) do desc "The compression property." end diff --git a/lib/puppet/type/zone.rb b/lib/puppet/type/zone.rb index 408d6f5dd..fc524a541 100644 --- a/lib/puppet/type/zone.rb +++ b/lib/puppet/type/zone.rb @@ -282,6 +282,33 @@ Puppet::Type.newtype(:zone) do end end + newproperty(:dataset, :parent => ZoneMultiConfigProperty) do + desc "The list of datasets delegated to the non global zone from the + global zone. All datasets must be zfs filesystem names which is + different than the mountpoint." + + validate do |value| + unless value !~ /^\// + raise ArgumentError, "Datasets must be the name of a zfs filesystem" + end + end + + # Add a zfs filesystem to our list of datasets. + def add(dataset) + "add dataset\nset name=#{dataset}\nend" + end + + # Remove a zfs filesystem from our list of datasets. + def rm(dataset) + "remove dataset name=#{dataset}" + end + + def should + @should + end + end + + newproperty(:inherit, :parent => ZoneMultiConfigProperty) do desc "The list of directories that the zone inherits from the global zone. All directories must be fully qualified." diff --git a/lib/puppet/type/zpool.rb b/lib/puppet/type/zpool.rb index 49cce552a..df06522e8 100755 --- a/lib/puppet/type/zpool.rb +++ b/lib/puppet/type/zpool.rb @@ -8,8 +8,6 @@ module Puppet end def insync?(is) - return true unless self.should - return @should == [:absent] if is == :absent flatten_and_sort(is) == flatten_and_sort(@should) @@ -18,8 +16,6 @@ module Puppet class MultiVDev < VDev def insync?(is) - return true unless self.should - return @should == [:absent] if is == :absent return false unless is.length == @should.length diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb index 5aebd8392..e129301e6 100644 --- a/lib/puppet/util/checksums.rb +++ b/lib/puppet/util/checksums.rb @@ -1,6 +1,12 @@ # A stand-alone module for calculating checksums # in a generic way. module Puppet::Util::Checksums + class FakeChecksum + def <<(*args) + self + end + end + # Is the provided string a checksum? def checksum?(string) string =~ /^\{(\w{3,5})\}\S+/ @@ -55,11 +61,16 @@ module Puppet::Util::Checksums end # by definition this doesn't exist + # but we still need to execute the block given def mtime_stream + noop_digest = FakeChecksum.new + yield noop_digest nil end - alias :ctime_stream :mtime_stream + def mtime(content) + "" + end # Calculate a checksum using Digest::SHA1. def sha1(content) @@ -99,12 +110,24 @@ module Puppet::Util::Checksums File.stat(filename).send(:ctime) end + alias :ctime_stream :mtime_stream + + def ctime(content) + "" + end + # Return a "no checksum" def none_file(filename) "" end def none_stream + noop_digest = FakeChecksum.new + yield noop_digest + "" + end + + def none(content) "" end diff --git a/lib/puppet/util/command_line/filebucket b/lib/puppet/util/command_line/filebucket index 8302d7b12..34b01508e 100755 --- a/lib/puppet/util/command_line/filebucket +++ b/lib/puppet/util/command_line/filebucket @@ -91,7 +91,7 @@ # # = Copyright # -# Copyright (c) 2005 Reductive Labs, LLC +# Copyright (c) 2005 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:filebucket].run diff --git a/lib/puppet/util/command_line/pi b/lib/puppet/util/command_line/pi index ae3c46e9a..3d80eea8f 100755 --- a/lib/puppet/util/command_line/pi +++ b/lib/puppet/util/command_line/pi @@ -42,7 +42,7 @@ # # = Copyright # -# Copyright (c) 2005 Reductive Labs, LLC +# Copyright (c) 2005 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:describe].run diff --git a/lib/puppet/util/command_line/puppet b/lib/puppet/util/command_line/puppet index 7b6c0ae7f..e75b92af8 100755 --- a/lib/puppet/util/command_line/puppet +++ b/lib/puppet/util/command_line/puppet @@ -63,7 +63,7 @@ # # = Copyright # -# Copyright (c) 2005 Reductive Labs, LLC +# Copyright (c) 2005 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:apply].run diff --git a/lib/puppet/util/command_line/puppetca b/lib/puppet/util/command_line/puppetca index 4f1a88da5..317d99881 100755 --- a/lib/puppet/util/command_line/puppetca +++ b/lib/puppet/util/command_line/puppetca @@ -27,7 +27,7 @@ # parameter, so you can specify '--ssldir <directory>' as an argument. # # See the configuration file documentation at -# http://reductivelabs.com/projects/puppet/reference/configref.html for +# http://docs.puppetlabs.com/references/stable/configuration.html for # the full list of acceptable parameters. A commented list of all # configuration options can also be generated by running puppet cert with # '--genconfig'. @@ -45,7 +45,7 @@ # Remove all files related to a host from puppet cert's storage. This is # useful when rebuilding hosts, since new certificate signing requests # will only be honored if puppet cert does not have a copy of a signed -# certificate for that host. The certificate of the host remains valid. +# certificate for that host. The certificate of the host is also revoked. # If '--all' is specified then all host certificates, both signed and # unsigned, will be removed. # @@ -104,7 +104,7 @@ # # = Copyright # -# Copyright (c) 2005 Reductive Labs, LLC +# Copyright (c) 2005 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:cert].run diff --git a/lib/puppet/util/command_line/puppetd b/lib/puppet/util/command_line/puppetd index 571b15486..71b28429b 100755 --- a/lib/puppet/util/command_line/puppetd +++ b/lib/puppet/util/command_line/puppetd @@ -11,7 +11,7 @@ # # puppet agent [-D|--daemonize|--no-daemonize] [-d|--debug] # [--detailed-exitcodes] [--disable] [--enable] -# [-h|--help] [--fqdn <host name>] [-l|--logdest syslog|<file>|console] +# [-h|--help] [--certname <host name>] [-l|--logdest syslog|<file>|console] # [-o|--onetime] [--serve <handler>] [-t|--test] [--noop] # [--digest <digest>] [--fingerprint] [-V|--version] # [-v|--verbose] [-w|--waitforcert <seconds>] @@ -113,10 +113,11 @@ # # +puppet agent+ exits after executing this. # -# fqdn:: -# Set the fully-qualified domain name of the client. This is only used for -# certificate purposes, but can be used to override the discovered hostname. -# If you need to use this flag, it is generally an indication of a setup problem. +# certname:: +# Set the certname (unique ID) of the client. The master reads this unique +# identifying string, which is usually set to the node's fully-qualified domain +# name, to determine which configurations the node will receive. Use this option +# to debug setup problems or implement unusual node identification schemes. # # help:: # Print this help message @@ -150,7 +151,8 @@ # # test:: # Enable the most common options used for testing. These are +onetime+, -# +verbose+, +ignorecache, +no-daemonize+, and +no-usecacheonfailure+. +# +verbose+, +ignorecache, +no-daemonize+, +no-usecacheonfailure+, +# +detailed-exit-codes+, +no-splay+, and +show_diff+. # # noop:: # Use +noop+ mode where the daemon runs in a no-op or dry-run mode. This is useful @@ -180,7 +182,7 @@ # # = Copyright # -# Copyright (c) 2005, 2006 Reductive Labs, LLC +# Copyright (c) 2005, 2006 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:agent].run diff --git a/lib/puppet/util/command_line/puppetdoc b/lib/puppet/util/command_line/puppetdoc index 0fa1830d6..45a9c6518 100755 --- a/lib/puppet/util/command_line/puppetdoc +++ b/lib/puppet/util/command_line/puppetdoc @@ -3,7 +3,7 @@ # # = Synopsis # -# Generate a reference for all Puppet types. Largely meant for internal Reductive +# Generate a reference for all Puppet types. Largely meant for internal Puppet # Labs use. # # = Usage @@ -37,7 +37,7 @@ # Specifies the directory where to output the rdoc documentation in 'rdoc' mode. # # mode:: -# Determine the output mode. Valid modes are 'text', 'trac', 'pdf' and 'rdoc'. The 'pdf' mode creates PDF formatted files in the /tmp directory. The default mode is 'text'. In 'rdoc' mode you must provide 'manifests-path' +# Determine the output mode. Valid modes are 'text', 'pdf' and 'rdoc'. The 'pdf' mode creates PDF formatted files in the /tmp directory. The default mode is 'text'. In 'rdoc' mode you must provide 'manifests-path' # # reference:: # Build a particular reference. Get a list of references by running +puppet doc --list+. @@ -47,7 +47,7 @@ # # = Example # -# $ puppet doc -r type > /tmp/type_reference.rst +# $ puppet doc -r type > /tmp/type_reference.markdown # or # $ puppet doc --outputdir /tmp/rdoc --mode rdoc /path/to/manifests # or @@ -61,7 +61,7 @@ # # = Copyright # -# Copyright (c) 2005-2007 Reductive Labs, LLC +# Copyright (c) 2005-2007 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:doc].run diff --git a/lib/puppet/util/command_line/puppetmasterd b/lib/puppet/util/command_line/puppetmasterd index 74efb3818..445169820 100755 --- a/lib/puppet/util/command_line/puppetmasterd +++ b/lib/puppet/util/command_line/puppetmasterd @@ -9,6 +9,7 @@ # # puppet master [-D|--daemonize|--no-daemonize] [-d|--debug] [-h|--help] # [-l|--logdest <file>|console|syslog] [-v|--verbose] [-V|--version] +# [--compile <nodename>] [--apply <catalog>] # # = Description # @@ -49,6 +50,14 @@ # version:: # Print the puppet version number and exit. # +# compile:: +# Capability to compile a catalogue and output it in JSON from the Puppet master. Uses +# facts contained in the $vardir/yaml/ directory to compile the catalog. +# +# apply:: +# Capability to apply JSON catalog (such as one generated with --compile). You can either specify +# a JSON file or pipe in JSON from standard input. +# # = Example # # puppet master @@ -59,7 +68,7 @@ # # = Copyright # -# Copyright (c) 2005 Reductive Labs, LLC +# Copyright (c) 2005 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:master].run diff --git a/lib/puppet/util/command_line/puppetqd b/lib/puppet/util/command_line/puppetqd index 48fc952bd..81963d537 100755 --- a/lib/puppet/util/command_line/puppetqd +++ b/lib/puppet/util/command_line/puppetqd @@ -47,7 +47,7 @@ # # = Copyright # -# Copyright (c) 2009 Reductive Labs, LLC +# Copyright (c) 2009 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:queue].run diff --git a/lib/puppet/util/command_line/puppetrun b/lib/puppet/util/command_line/puppetrun index 27cd775b9..7eba3b2c4 100755 --- a/lib/puppet/util/command_line/puppetrun +++ b/lib/puppet/util/command_line/puppetrun @@ -120,7 +120,7 @@ # # = Copyright # -# Copyright (c) 2005 Reductive Labs, LLC +# Copyright (c) 2005 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:kick].run diff --git a/lib/puppet/util/command_line/ralsh b/lib/puppet/util/command_line/ralsh index 83338fcbc..5c1f719e2 100755 --- a/lib/puppet/util/command_line/ralsh +++ b/lib/puppet/util/command_line/ralsh @@ -83,7 +83,7 @@ # # = Copyright # -# Copyright (c) 2005-2007 Reductive Labs, LLC +# Copyright (c) 2005-2007 Puppet Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:resource].run diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb index a5aacc265..2f9f356bb 100644 --- a/lib/puppet/util/log.rb +++ b/lib/puppet/util/log.rb @@ -17,11 +17,12 @@ class Puppet::Util::Log # Create a new destination type. def self.newdesttype(name, options = {}, &block) - dest = genclass( - name, :parent => Puppet::Util::Log::Destination, :prefix => "Dest", - :block => block, - :hash => @desttypes, - + dest = genclass( + name, + :parent => Puppet::Util::Log::Destination, + :prefix => "Dest", + :block => block, + :hash => @desttypes, :attributes => options ) dest.match(dest.name) @@ -189,7 +190,7 @@ class Puppet::Util::Log @levels.include?(level) end - attr_accessor :time, :remote, :file, :line, :version, :source + attr_accessor :time, :remote, :file, :line, :source attr_reader :level, :message def initialize(args) @@ -203,7 +204,7 @@ class Puppet::Util::Log tags.each { |t| self.tag(t) } end - [:file, :line, :version].each do |attr| + [:file, :line].each do |attr| next unless value = args[attr] send(attr.to_s + "=", value) end @@ -234,7 +235,7 @@ class Puppet::Util::Log descriptors[:tags].each { |t| tag(t) } - [:file, :line, :version].each do |param| + [:file, :line].each do |param| next unless descriptors[param] send(param.to_s + "=", descriptors[param]) end diff --git a/lib/puppet/util/log_paths.rb b/lib/puppet/util/log_paths.rb index f59197ed1..2fefd4505 100644 --- a/lib/puppet/util/log_paths.rb +++ b/lib/puppet/util/log_paths.rb @@ -15,7 +15,7 @@ module Puppet::Util::LogPaths descriptors[:tags] = tags - [:path, :file, :line, :version].each do |param| + [:path, :file, :line].each do |param| next unless value = send(param) descriptors[param] = value end diff --git a/lib/puppet/util/logging.rb b/lib/puppet/util/logging.rb index f20444a3b..bc52b17f0 100644 --- a/lib/puppet/util/logging.rb +++ b/lib/puppet/util/logging.rb @@ -26,7 +26,7 @@ module Puppet::Util::Logging end def log_metadata - [:file, :line, :version, :tags].inject({}) do |result, attr| + [:file, :line, :tags].inject({}) do |result, attr| result[attr] = send(attr) if respond_to?(attr) result end diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb index 7fdc6951f..835e1d610 100644 --- a/lib/puppet/util/metric.rb +++ b/lib/puppet/util/metric.rb @@ -132,6 +132,7 @@ class Puppet::Util::Metric end def newvalue(name,value,label = nil) + raise ArgumentError.new("metric name #{name.inspect} is not a string") unless name.is_a? String label ||= self.class.labelize(name) @values.push [name,label,value] end diff --git a/lib/puppet/util/reference.rb b/lib/puppet/util/reference.rb index 95efeb1c1..a4921ed2a 100644 --- a/lib/puppet/util/reference.rb +++ b/lib/puppet/util/reference.rb @@ -120,16 +120,11 @@ class Puppet::Util::Reference str += "\n\n" end - # Remove all trac links. - def strip_trac(text) - text.gsub(/`\w+\s+([^`]+)`:trac:/) { |m| $1 } - end - def text puts output end - def to_rest(withcontents = true) + def to_markdown(withcontents = true) # First the header text = h(@title, 1) text += "\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on #{Time.now.to_s})*\n\n" @@ -142,8 +137,4 @@ class Puppet::Util::Reference text end - - def to_text(withcontents = true) - strip_trac(to_rest(withcontents)) - end end diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index ca4ecda35..626ed20eb 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -593,7 +593,7 @@ if @config.include?(:run_mode) end eachsection do |section| persection(section) do |obj| - str += obj.to_config + "\n" unless ReadOnly.include? obj.name + str += obj.to_config + "\n" unless ReadOnly.include? obj.name or obj.name == :genconfig end end |
