Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'next'

  • Loading branch information...
commit 4b35402ba85d8842d757becec5c8a7bf4d6f6654 2 parents a7bd163 + 2bc6727
@nicklewis nicklewis authored
Showing with 1,772 additions and 860 deletions.
  1. +45 −0 CHANGELOG
  2. +1 −1  Rakefile
  3. +0 −2  ext/puppetstoredconfigclean.rb
  4. +2 −0  lib/puppet/application/agent.rb
  5. +5 −18 lib/puppet/application/apply.rb
  6. +17 −6 lib/puppet/configurer.rb
  7. +2 −2 lib/puppet/daemon.rb
  8. +13 −6 lib/puppet/defaults.rb
  9. +1 −22 lib/puppet/external/pson/pure/generator.rb
  10. +8 −4 lib/puppet/indirector/catalog/compiler.rb
  11. +11 −0 lib/puppet/indirector/report/yaml.rb
  12. +2 −0  lib/puppet/network/handler/fileserver.rb
  13. +2 −2 lib/puppet/network/server.rb
  14. +4 −0 lib/puppet/node/environment.rb
  15. +34 −51 lib/puppet/parser/ast/collection.rb
  16. +10 −2 lib/puppet/parser/ast/leaf.rb
  17. +4 −2 lib/puppet/parser/functions.rb
  18. +3 −3 lib/puppet/parser/parser_support.rb
  19. +1 −0  lib/puppet/parser/templatewrapper.rb
  20. +2 −2 lib/puppet/provider.rb
  21. +1 −4 lib/puppet/provider/confine/exists.rb
  22. +23 −53 lib/puppet/provider/host/parsed.rb
  23. +5 −6 lib/puppet/provider/maillist/mailman.rb
  24. +1 −1  lib/puppet/provider/mount.rb
  25. +6 −10 lib/puppet/provider/package/openbsd.rb
  26. +8 −1 lib/puppet/provider/package/yum.rb
  27. +4 −1 lib/puppet/provider/service/freebsd.rb
  28. +2 −2 lib/puppet/provider/user/user_role_add.rb
  29. +2 −2 lib/puppet/provider/user/useradd.rb
  30. +2 −6 lib/puppet/rails.rb
  31. +0 −72 lib/puppet/rails/host.rb
  32. +12 −0 lib/puppet/resource.rb
  33. +209 −208 lib/puppet/simple_graph.rb
  34. +28 −11 lib/puppet/transaction/report.rb
  35. +1 −7 lib/puppet/type/exec.rb
  36. +9 −19 lib/puppet/type/host.rb
  37. +5 −5 lib/puppet/type/schedule.rb
  38. +20 −18 lib/puppet/util.rb
  39. +1 −1  lib/puppet/util/command_line.rb
  40. +6 −3 lib/puppet/util/file_locking.rb
  41. +1 −0  lib/puppet/util/log.rb
  42. +13 −1 lib/puppet/util/log/destinations.rb
  43. +4 −6 lib/puppet/util/metric.rb
  44. +15 −0 lib/puppet/util/monkey_patches.rb
  45. +5 −4 lib/puppet/util/rdoc.rb
  46. +6 −0 lib/puppet/util/rdoc/generators/puppet_generator.rb
  47. +1 −8 lib/puppet/util/reference.rb
  48. +1 −1  lib/puppet/util/suidmanager.rb
  49. +4 −1 lib/puppet/util/zaml.rb
  50. +38 −5 spec/integration/configurer_spec.rb
  51. +1 −0  spec/integration/defaults_spec.rb
  52. +21 −0 spec/integration/parser/functions_spec.rb
  53. +1 −1  spec/integration/parser/ruby_manifest_spec.rb
  54. +1 −3 spec/integration/ssl/certificate_authority_spec.rb
  55. +31 −11 spec/integration/util/file_locking_spec.rb
  56. +1 −1  spec/spec_helper.rb
  57. +7 −0 spec/unit/application/agent_spec.rb
  58. +23 −8 spec/unit/application/apply_spec.rb
  59. +1 −0  spec/unit/application/filebucket_spec.rb
  60. +1 −0  spec/unit/application/queue_spec.rb
  61. +61 −18 spec/unit/configurer_spec.rb
  62. +3 −9 spec/unit/daemon_spec.rb
  63. +1 −0  spec/unit/indirector/active_record_spec.rb
  64. +9 −8 spec/unit/indirector/catalog/compiler_spec.rb
  65. +1 −0  spec/unit/indirector/facts/active_record_spec.rb
  66. +38 −0 spec/unit/indirector/report/yaml_spec.rb
  67. +2 −4 spec/unit/network/handler/fileserver_spec.rb
  68. +1 −0  spec/unit/network/http/rack/xmlrpc_spec.rb
  69. +1 −0  spec/unit/network/http/rack_spec.rb
  70. +1 −0  spec/unit/network/http/webrick_spec.rb
  71. +3 −10 spec/unit/network/server_spec.rb
  72. +4 −0 spec/unit/parser/ast/collection_spec.rb
  73. +43 −1 spec/unit/parser/ast/leaf_spec.rb
  74. +2 −2 spec/unit/parser/ast/resource_spec.rb
  75. +1 −0  spec/unit/parser/collector_spec.rb
  76. +30 −23 spec/unit/parser/parser_spec.rb
  77. +1 −0  spec/unit/parser/templatewrapper_spec.rb
  78. +6 −13 spec/unit/provider/confine/exists_spec.rb
  79. +196 −0 spec/unit/provider/host/parsed_spec.rb
  80. +8 −1 spec/unit/provider/mount_spec.rb
  81. +67 −0 spec/unit/provider/package/yum_spec.rb
  82. +50 −0 spec/unit/provider/service/freebsd_spec.rb
  83. +1 −2  spec/unit/provider/service/upstart.rb
  84. +1 −2  spec/unit/provider/ssh_authorized_key/parsed_spec.rb
  85. +1 −0  spec/unit/rails/param_value_spec.rb
  86. +1 −0  spec/unit/rails/resource_spec.rb
  87. +82 −22 spec/unit/rails_spec.rb
  88. +3 −3 spec/unit/resource/catalog_spec.rb
  89. +228 −20 spec/unit/simple_graph_spec.rb
  90. +13 −2 spec/unit/transaction/report_spec.rb
  91. +83 −0 spec/unit/type/host_spec.rb
  92. +21 −49 spec/unit/type/schedule_spec.rb
  93. +2 −2 spec/unit/util/command_line_spec.rb
  94. +28 −24 spec/unit/util/file_locking_spec.rb
  95. +2 −2 spec/unit/util/log_spec.rb
  96. +26 −0 spec/unit/util/monkey_patches_spec.rb
  97. +15 −0 spec/unit/util/pson_spec.rb
  98. +9 −1 spec/unit/util/rdoc_spec.rb
  99. +2 −3 spec/unit/util/storage_spec.rb
  100. +0 −1  spec/unit/util/zaml_spec.rb
  101. +19 −0 test/data/providers/host/parsed/valid_hosts
  102. +2 −2 test/lib/puppettest.rb
  103. +1 −12 test/other/provider.rb
  104. +5 −14 test/puppet/tc_suidmanager.rb
  105. +1 −1  test/ral/manager/type.rb
  106. +6 −3 test/ral/providers/host/parsed.rb
  107. +3 −3 test/ral/providers/provider.rb
View
45 CHANGELOG
@@ -1,5 +1,50 @@
+2.6.3
+=====
+184733c [#5322] (#5322) Remove spec file that adds little value and causes failures
+178c2a6 Fix test failures that fixing #4726 exposed.
+74b6c09 (#4726) Fix RRD legacy support
+8662056 Fix for #4279 -- mount detection on HP-UX
+fbb096a Fix for #5055 -- adding to_sym to Puppet::Node::Environment
+b2ff6a5 Fix for #5298 -- Collections need to do type lookup
+1ce00dc Step towards [5298] -- cleanup indentation, etc. in AST::Collection
+722608b Fixed #5287 - Schedule documentation is incorrect
+c8b6fb5 Fixed #5296 - test warnings messages
+d221c05 (#5297) Fix schedule tests that were missing stubs for Time.now
+f2fd0d1 Fix for #5273 -- user resource constantly resetting password age
+544dcf8 Fix #5289 -- Bad copy/paste changes message on test failure
+
+2.6.3rc3
+========
+126681f Updated CHANGELOG for 2.6.3rc3
+b15231d Fix for #4299 -- Don't require which
+ea435a4 Fix #5020 - Prefer finding node name from REST uri over certname
+a097b93 Fix for #4894 -- retry tests if port is in use
+ee61b4e Fix for #4955 -- Race condition & memory leak in Puppet::Util
+f57425d Fix #4921 - race condition in Parser Functions creation
+9604f1c Fix #5252 - line number mis-attribution during parsing
+cc5224c Maint. fix for test broken by 00eedac5
+5f7d0fb Fix for #2568 -- Add a dbconnections option to set AR pool size
+ba4d22b Maint. Removing code for which no CLA has been signed
+4a3d5d7 Reimplementation of functionality removed by prior commit
+235d641 Refactor for CLA
+9ba0c8a Fix #4923 - close process race when truncating existing file
+cb16d3d Puppet-load: better and safer error reporting
+1d26742 Fix #5023 - puppet-load multiple nodes support
+00eedac capture stderr from exec resources
+4cbceab (#4573) FreeBSD service provider now supports versions <7 and >8
+06c8748 Fix #3808 - puppetdoc should use --force-update only if RDoc supports it
+6e6712b [#4813] Remove dead code from puppet/rails/host.rb
+956296a Fix #4911 - Do not generate doc for standard RDoc parser generated object
+4fa24bb Fix #5127 - error when accessing array elements
+abb8c66 (#5242) Fix schedule specs that fail near daylight savings
+ec667fd Kludge for #5206 -- port of fix for #3536 to yaml
+9a3b584 (#5062) Add envpuppet helper script to ext/
+aad7008 [#5225] Fix spec failure that depended on time change
+21db472 (#5233) Randomize tmp dir paths
+
2.6.3rc2
========
+244213c Updated CHANGELOG for 2.6.3rc2
76ac1f8 Fixed #5112 - Launchd Service broke in 2.6.2 with OS X 10.4 Clients.
776ea2a Fixed #5137 - Removed no longer required TOC references
31118fe Kludge for #5048 -- serialization compatibility with 0.25.x
View
2  Rakefile
@@ -43,7 +43,7 @@ desc "Create the tarball and the gem - use when releasing"
task :puppetpackages => [:create_gem, :package]
Spec::Rake::SpecTask.new do |t|
- t.spec_opts = ['--format','s', '--loadby','mtime','--color']
+ t.spec_opts = ['--format','s', '--loadby','mtime','--color', '--backtrace']
t.pattern ='spec/{unit,integration}/**/*.rb'
t.fail_on_error = false
end
View
2  ext/puppetstoredconfigclean.rb
@@ -70,8 +70,6 @@ def printusage(error_code)
args[:port] = pm_conf[:dbport] unless pm_conf[:dbport].to_s.empty?
socket = pm_conf[:dbsocket]
args[:socket] = socket unless socket.to_s.empty?
- connections = pm_conf[:dbconnections].to_i
- args[:pool] = connections if connections > 0
else
raise ArgumentError, "Invalid db adapter #{adapter}"
end
View
2  lib/puppet/application/agent.rb
@@ -229,6 +229,8 @@ def setup
Puppet::SSL::Host.ca_location = options[:fingerprint] ? :none : :remote
Puppet::Transaction::Report.terminus_class = :rest
+ # we want the last report to be persisted locally
+ Puppet::Transaction::Report.cache_class = :yaml
# Override the default; puppetd needs this, usually.
# You can still override this on the command-line with, e.g., :compiler.
View
23 lib/puppet/application/apply.rb
@@ -123,25 +123,9 @@ def main
require 'puppet/configurer'
configurer = Puppet::Configurer.new
- configurer.execute_prerun_command
+ report = configurer.run(:skip_plugin_download => true, :catalog => catalog)
- # And apply it
- if Puppet[:report]
- report = configurer.initialize_report
- Puppet::Util::Log.newdestination(report)
- end
- transaction = catalog.apply
-
- configurer.execute_postrun_command
-
- if Puppet[:report]
- Puppet::Util::Log.close(report)
- configurer.send_report(report, transaction)
- else
- transaction.generate_report
- end
-
- exit( Puppet[:noop] ? 0 : options[:detailed_exitcodes] ? transaction.report.exit_status : 0 )
+ exit( Puppet[:noop] ? 0 : options[:detailed_exitcodes] ? report.exit_status : 0 )
rescue => detail
puts detail.backtrace if Puppet[:trace]
$stderr.puts detail.message
@@ -164,6 +148,9 @@ def setup
exit(1)
end
+ # we want the last report to be persisted locally
+ Puppet::Transaction::Report.cache_class = :yaml
+
if options[:debug]
Puppet::Util::Log.level = :debug
elsif options[:verbose]
View
23 lib/puppet/configurer.rb
@@ -77,12 +77,12 @@ def initialize_report
end
# Prepare for catalog retrieval. Downloads everything necessary, etc.
- def prepare
+ def prepare(options)
dostorage
- download_plugins
+ download_plugins unless options[:skip_plugin_download]
- download_fact_plugins
+ download_fact_plugins unless options[:skip_plugin_download]
execute_prerun_command
end
@@ -126,7 +126,7 @@ def convert_catalog(result, duration)
# which accepts :tags and :ignoreschedules.
def run(options = {})
begin
- prepare
+ prepare(options)
rescue SystemExit,NoMemoryError
raise
rescue Exception => detail
@@ -168,19 +168,30 @@ def run(options = {})
execute_postrun_command
Puppet::Util::Log.close(report)
-
send_report(report, transaction)
end
def send_report(report, trans = nil)
trans.generate_report if trans
puts report.summary if Puppet[:summarize]
- report.save if Puppet[:report]
+ save_last_run_summary(report)
+ if Puppet[:report]
+ report.save
+ end
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not send report: #{detail}"
end
+ def save_last_run_summary(report)
+ Puppet::Util::FileLocking.writelock(Puppet[:lastrunfile], 0660) do |file|
+ file.print YAML.dump(report.raw_summary)
+ end
+ rescue => detail
+ puts detail.backtrace if Puppet[:trace]
+ Puppet.err "Could not save last run local report: #{detail}"
+ end
+
private
def self.timeout
View
4 lib/puppet/daemon.rb
@@ -43,7 +43,7 @@ def daemonize
# Create a pidfile for our daemon, so we can be stopped and others
# don't try to start.
def create_pidfile
- Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do
+ Puppet::Util.synchronize_on(Puppet[:name],Sync::EX) do
raise "Could not create PID file: #{pidfile}" unless Puppet::Util::Pidlock.new(pidfile).lock
end
end
@@ -73,7 +73,7 @@ def reload
# Remove the pid file for our daemon.
def remove_pidfile
- Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do
+ Puppet::Util.synchronize_on(Puppet[:name],Sync::EX) do
locker = Puppet::Util::Pidlock.new(pidfile)
locker.unlock or Puppet.err "Could not remove PID file #{pidfile}" if locker.locked?
end
View
19 lib/puppet/defaults.rb
@@ -116,7 +116,7 @@ module Puppet
:catalog_terminus => ["compiler", "Where to get node catalogs. This is useful to change if, for instance,
you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store."],
:facts_terminus => {
- :default => Puppet.application_name.to_s == "master" ? 'yaml' : 'facter',
+ :default => Puppet.application_name.to_s == "master" ? 'yaml' : 'facter',
:desc => "The node facts terminus.",
:hook => proc do |value|
require 'puppet/node/facts'
@@ -599,9 +599,17 @@ module Puppet
:inventory_port => ["$masterport",
"The port to communicate with the inventory_server."
],
- :report => [false,
+ :report => [true,
"Whether to send reports after every transaction."
],
+ :lastrunfile => { :default => "$statedir/last_run_summary.yaml",
+ :mode => 0660,
+ :desc => "Where puppet agent stores the last run report summary in yaml format."
+ },
+ :lastrunreport => { :default => "$statedir/last_run_report.yaml",
+ :mode => 0660,
+ :desc => "Where puppet agent stores the last run report in yaml format."
+ },
:graph => [false, "Whether to create dot graph files for the different
configuration graphs. These dot files can be interpreted by tools
like OmniGraffle or dot (which is part of ImageMagick)."],
@@ -656,7 +664,7 @@ module Puppet
setdefaults(
:tagmail,
:tagmap => ["$confdir/tagmail.conf", "The mapping between reporting tags and email addresses."],
- :sendmail => [%x{which sendmail 2>/dev/null}.chomp, "Where to find the sendmail binary with which to send email."],
+ :sendmail => [which('sendmail') || '', "Where to find the sendmail binary with which to send email."],
:reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."), "The 'from' email address for the reports."],
:smtpserver => ["none", "The server through which to send email reports."]
@@ -682,11 +690,10 @@ module Puppet
used when networked databases are used."],
:dbpassword => [ "puppet", "The database password for caching. Only
used when networked databases are used."],
+ :dbconnections => [ '', "The number of database connections for networked
+ databases. Will be ignored unless the value is a positive integer."],
:dbsocket => [ "", "The database socket location. Only used when networked
databases are used. Will be ignored if the value is an empty string."],
- :dbconnections => [ 0, "The number of database connections. Only used when
- networked databases are used. Will be ignored if the value is an empty
- string or is less than 1."],
:railslog => {:default => "$logdir/rails.log",
:mode => 0600,
:owner => "service",
View
23 lib/puppet/external/pson/pure/generator.rb
@@ -44,34 +44,13 @@ def utf8_to_pson(string) # :nodoc:
string << '' # XXX workaround: avoid buffer sharing
string.force_encoding(Encoding::ASCII_8BIT)
string.gsub!(/["\\\x0-\x1f]/) { MAP[$MATCH] }
- string.gsub!(/(
- (?:
- [\xc2-\xdf][\x80-\xbf] |
- [\xe0-\xef][\x80-\xbf]{2} |
- [\xf0-\xf4][\x80-\xbf]{3}
- )+ |
- [\x80-\xc1\xf5-\xff] # invalid
- )/nx) { |c|
- c.size == 1 and raise GeneratorError, "invalid utf8 byte: '#{c}'"
- s = PSON::UTF8toUTF16.iconv(c).unpack('H*')[0]
- s.gsub!(/.{4}/n, '\\\\u\&')
- }
- string.force_encoding(Encoding::UTF_8)
string
rescue Iconv::Failure => e
raise GeneratorError, "Caught #{e.class}: #{e}"
end
else
def utf8_to_pson(string) # :nodoc:
- string.
- gsub(/["\\\x0-\x1f]/n) { MAP[$MATCH] }.
- gsub(/((?:
- [\xc2-\xdf][\x80-\xbf] |
- [\xe0-\xef][\x80-\xbf]{2} |
- [\xf0-\xf4][\x80-\xbf]{3}
- )+)/nx) { |c|
- PSON::UTF8toUTF16.iconv(c).unpack('H*')[0].gsub(/.{4}/n, '\\\\u\&')
- }
+ string.gsub(/["\\\x0-\x1f]/n) { MAP[$MATCH] }
end
end
module_function :utf8_to_pson
View
12 lib/puppet/indirector/catalog/compiler.rb
@@ -108,10 +108,14 @@ def node_from_request(request)
return node
end
- # If the request is authenticated, then the 'node' info will
- # be available; if not, then we use the passed-in key. We rely
- # on our authorization system to determine whether this is allowed.
- name = request.node || request.key
+ # We rely on our authorization system to determine whether the connected
+ # node is allowed to compile the catalog's node referenced by key.
+ # By default the REST authorization system makes sure only the connected node
+ # can compile his catalog.
+ # This allows for instance monitoring systems or puppet-load to check several
+ # node's catalog with only one certificate and a modification to auth.conf
+ # If no key is provided we can only compile the currently connected node.
+ name = request.key || request.node
if node = find_node(name)
return node
end
View
11 lib/puppet/indirector/report/yaml.rb
@@ -0,0 +1,11 @@
+require 'puppet/transaction/report'
+require 'puppet/indirector/yaml'
+
+class Puppet::Transaction::Report::Yaml < Puppet::Indirector::Yaml
+ desc "Store last report as a flat file, serialized using YAML."
+
+ # Force report to be saved there
+ def path(name,ext='.yaml')
+ Puppet[:lastrunreport]
+ end
+end
View
2  lib/puppet/network/handler/fileserver.rb
@@ -6,6 +6,8 @@
require 'sync'
require 'xmlrpc/server'
+require 'puppet/network/handler'
+require 'puppet/network/xmlrpc/server'
require 'puppet/file_serving'
require 'puppet/file_serving/metadata'
require 'puppet/network/handler'
View
4 lib/puppet/network/server.rb
@@ -32,14 +32,14 @@ def daemonize
# Create a pidfile for our daemon, so we can be stopped and others
# don't try to start.
def create_pidfile
- Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do
+ Puppet::Util.synchronize_on(Puppet[:name],Sync::EX) do
raise "Could not create PID file: #{pidfile}" unless Puppet::Util::Pidlock.new(pidfile).lock
end
end
# Remove the pid file for our daemon.
def remove_pidfile
- Puppet::Util.sync(Puppet[:name]).synchronize(Sync::EX) do
+ Puppet::Util.synchronize_on(Puppet[:name],Sync::EX) do
locker = Puppet::Util::Pidlock.new(pidfile)
locker.unlock or Puppet.err "Could not remove PID file #{pidfile}" if locker.locked?
end
View
4 lib/puppet/node/environment.rb
@@ -124,6 +124,10 @@ def to_s
name.to_s
end
+ def to_sym
+ to_s.to_sym
+ end
+
# The only thing we care about when serializing an environment is its
# identity; everything else is ephemeral and should not be stored or
# transmitted.
View
85 lib/puppet/parser/ast/collection.rb
@@ -5,61 +5,44 @@
# An object that collects stored objects from the central cache and returns
# them to the current host, yo.
class Puppet::Parser::AST
-class Collection < AST::Branch
- attr_accessor :type, :query, :form
- attr_reader :override
-
- associates_doc
-
- # We return an object that does a late-binding evaluation.
- def evaluate(scope)
- if self.query
- str, code = self.query.safeevaluate scope
- else
- str = code = nil
- end
-
- newcoll = Puppet::Parser::Collector.new(scope, @type, str, code, self.form)
-
- scope.compiler.add_collection(newcoll)
-
- # overrides if any
- # Evaluate all of the specified params.
- if @override
- params = @override.collect do |param|
- param.safeevaluate(scope)
+ class Collection < AST::Branch
+ attr_accessor :type, :query, :form
+ attr_reader :override
+
+ associates_doc
+
+ # We return an object that does a late-binding evaluation.
+ def evaluate(scope)
+ str, code = query && query.safeevaluate(scope)
+
+ resource_type = scope.find_resource_type(@type)
+ newcoll = Puppet::Parser::Collector.new(scope, resource_type.name, str, code, self.form)
+
+ scope.compiler.add_collection(newcoll)
+
+ # overrides if any
+ # Evaluate all of the specified params.
+ if @override
+ params = @override.collect { |param| param.safeevaluate(scope) }
+ newcoll.add_override(
+ :parameters => params,
+ :file => @file,
+ :line => @line,
+ :source => scope.source,
+ :scope => scope
+ )
end
-
- newcoll.add_override(
-
- :parameters => params,
- :file => @file,
- :line => @line,
- :source => scope.source,
-
- :scope => scope
- )
+ newcoll
end
- newcoll
- end
-
- # Handle our parameter ourselves
- def override=(override)
- if override.is_a?(AST::ASTArray)
- @override = override
- else
-
- @override = AST::ASTArray.new(
-
- :line => override.line,
- :file => override.file,
-
- :children => [override]
- )
+ # Handle our parameter ourselves
+ def override=(override)
+ @override = if override.is_a?(AST::ASTArray)
+ override
+ else
+ AST::ASTArray.new(:line => override.line,:file => override.file,:children => [override])
+ end
end
end
-
-end
end
View
12 lib/puppet/parser/ast/leaf.rb
@@ -148,12 +148,20 @@ def evaluate_key(scope)
key.respond_to?(:evaluate) ? key.safeevaluate(scope) : key
end
+ def array_index_or_key(object, key)
+ if object.is_a?(Array)
+ raise Puppet::ParserError, "#{key} is not an integer, but is used as an index of an array" unless key = Puppet::Parser::Scope.number?(key)
+ end
+ key
+ end
+
def evaluate(scope)
object = evaluate_container(scope)
+ accesskey = evaluate_key(scope)
raise Puppet::ParseError, "#{variable} is not an hash or array when accessing it with #{accesskey}" unless object.is_a?(Hash) or object.is_a?(Array)
- object[evaluate_key(scope)]
+ object[array_index_or_key(object, accesskey)]
end
# Assign value to this hashkey or array index
@@ -166,7 +174,7 @@ def assign(scope, value)
end
# assign to hash or array
- object[accesskey] = value
+ object[array_index_or_key(object, accesskey)] = value
end
def to_s
View
6 lib/puppet/parser/functions.rb
@@ -73,8 +73,10 @@ def self.rmfunction(name)
def self.function(name)
name = symbolize(name)
- unless functions.include?(name) or functions(Puppet::Node::Environment.root).include?(name)
- autoloader.load(name,Environment.current || Environment.root)
+ @functions.synchronize do
+ unless functions.include?(name) or functions(Puppet::Node::Environment.root).include?(name)
+ autoloader.load(name,Environment.current || Environment.root)
+ end
end
( functions(Environment.root)[name] || functions[name] || {:name => false} )[:name]
View
6 lib/puppet/parser/parser_support.rb
@@ -37,12 +37,12 @@ def aryfy(arg)
# Create an AST object, and automatically add the file and line information if
# available.
def ast(klass, hash = {})
- klass.new ast_context(klass.use_docs).merge(hash)
+ klass.new ast_context(klass.use_docs, hash[:line]).merge(hash)
end
- def ast_context(include_docs = false)
+ def ast_context(include_docs = false, ast_line = nil)
result = {
- :line => lexer.line,
+ :line => ast_line || lexer.line,
:file => lexer.file
}
result[:doc] = lexer.getcomment(result[:line]) if include_docs
View
1  lib/puppet/parser/templatewrapper.rb
@@ -1,6 +1,7 @@
# A simple wrapper for templates, so they don't have full access to
# the scope objects.
require 'puppet/parser/files'
+require 'erb'
class Puppet::Parser::TemplateWrapper
attr_writer :scope
View
4 lib/puppet/provider.rb
@@ -12,7 +12,7 @@ class Puppet::Provider
Puppet::Util.logmethods(self, true)
class << self
- # Include the util module so we have access to things like 'binary'
+ # Include the util module so we have access to things like 'which'
include Puppet::Util, Puppet::Util::Docs
include Puppet::Util::Logging
attr_accessor :name
@@ -43,7 +43,7 @@ def self.command(name)
raise Puppet::DevError, "No command #{name} defined for provider #{self.name}"
end
- binary(command)
+ which(command)
end
# Define commands that are not optional.
View
5 lib/puppet/provider/confine/exists.rb
@@ -6,10 +6,7 @@ def self.summarize(confines)
end
def pass?(value)
- if for_binary?
- return false unless value = binary(value)
- end
- value and FileTest.exist?(value)
+ value && (for_binary? ? which(value) : FileTest.exist?(value))
end
def message(value)
View
76 lib/puppet/provider/host/parsed.rb
@@ -8,66 +8,36 @@
end
- Puppet::Type.type(:host).provide(
- :parsed,
- :parent => Puppet::Provider::ParsedFile,
- :default_target => hosts,
-
- :filetype => :flat
-) do
+Puppet::Type.type(:host).provide(:parsed,:parent => Puppet::Provider::ParsedFile,
+ :default_target => hosts,:filetype => :flat) do
confine :exists => hosts
text_line :comment, :match => /^#/
text_line :blank, :match => /^\s*$/
- record_line :parsed, :fields => %w{ip name host_aliases},
- :optional => %w{host_aliases},
- :rts => true do |line|
- hash = {}
- if line.sub!(/^(\S+)\s+(\S+)\s*/, '')
- hash[:ip] = $1
- hash[:name] = $2
-
- if line.empty?
- hash[:host_aliases] = []
+ record_line :parsed, :fields => %w{ip name host_aliases comment},
+ :optional => %w{host_aliases comment},
+ :match => /^(\S+)\s+(\S+)\s*(.*?)?(?:\s*#\s*(.*))?$/,
+ :post_parse => proc { |hash|
+ # 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
- line.sub!(/\s*/, '')
- line.sub!(/^([^#]+)\s*/) do |value|
- aliases = $1
- unless aliases =~ /^\s*$/
- hash[:host_aliases] = aliases.split(/\s+/)
- end
-
- ""
- end
+ hash[:host_aliases] = []
end
- else
- raise Puppet::Error, "Could not match '#{line}'"
- end
-
- hash[:host_aliases] = [] if hash[:host_aliases] == ""
-
- return hash
- end
-
- # Convert the current object into a host-style string.
- def self.to_line(hash)
- return super unless hash[:record_type] == :parsed
- [:ip, :name].each do |n|
- 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?
- if hash[:host_aliases].is_a? Array
+ },
+ :to_line => proc { |hash|
+ [:ip, :name].each do |n|
+ 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")}"
- else
- raise ArgumentError, "Host aliases must be specified as an array"
end
- end
-
- str
- end
+ if hash.include? :comment and !hash[:comment].empty?
+ str += "\t# #{hash[:comment]}"
+ end
+ str
+ }
end
-
View
11 lib/puppet/provider/maillist/mailman.rb
@@ -2,11 +2,11 @@
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", :rmlist => "/usr/lib/mailman/bin/rmlist", :newlist => "/usr/lib/mailman/bin/newlist"
+ commands :list_lists => "/usr/lib/mailman/bin/list_lists --bare", :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", :rmlist => "rmlist", :newlist => "newlist"
+ commands :list_lists => "list_lists --bare", :rmlist => "rmlist", :newlist => "newlist"
commands :mailman => "/var/lib/mailman/mail/mailman"
end
@@ -14,10 +14,9 @@
# Return a list of existing mailman instances.
def self.instances
- list_lists.split("\n").reject { |line| line.include?("matching mailing lists") }.collect do |line|
- name, description = line.sub(/^\s+/, '').sub(/\s+$/, '').split(/\s+-\s+/)
- description = :absent if description.include?("no description available")
- new(:ensure => :present, :name => name, :description => description)
+ list_lists.split("\n").collect do |line|
+ name = line.strip
+ new(:ensure => :present, :name => name)
end
end
View
2  lib/puppet/provider/mount.rb
@@ -41,7 +41,7 @@ def mounted?
case platform
when "Darwin"
line =~ / on #{name} / or line =~ %r{ on /private/var/automount#{name}}
- when "Solaris"
+ when "Solaris", "HP-UX"
line =~ /^#{name} on /
else
line =~ / on #{name} /
View
16 lib/puppet/provider/package/openbsd.rb
@@ -2,7 +2,6 @@
# Packaging on OpenBSD. Doesn't work anywhere else that I know of.
Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Package do
- include Puppet::Util::Execution
desc "OpenBSD's form of `pkg_add` support."
commands :pkginfo => "pkg_info", :pkgadd => "pkg_add", :pkgdelete => "pkg_delete"
@@ -61,18 +60,15 @@ def install
"You must specify a package source for BSD packages"
end
- old_ensure = @resource[:ensure]
-
- if @resource[:source] =~ /\/$/
- withenv :PKG_PATH => @resource[:source] do
- @resource[:ensure] = old_ensure if (@resource[:ensure] = get_version) == nil
- full_name = [ @resource[:name], @resource[:ensure], @resource[:flavor] ]
- pkgadd full_name.join('-').chomp('-')
- end
+ if @resource[:source][-1,1] == ::File::PATH_SEPARATOR
+ e_vars = { :PKG_PATH => @resource[:source] }
+ full_name = [ @resource[:name], get_version || @resource[:ensure], @resource[:flavor] ].join('-').chomp('-')
else
- pkgadd @resource[:source]
+ e_vars = {}
+ full_name = @resource[:source]
end
+ Puppet::Util::Execution::withenv(e_vars) { pkgadd full_name }
end
def get_version
View
9 lib/puppet/provider/package/yum.rb
@@ -1,3 +1,4 @@
+require 'puppet/util/package'
Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
desc "Support via `yum`."
@@ -52,6 +53,7 @@ def install
should = @resource.should(:ensure)
self.debug "Ensuring => #{should}"
wanted = @resource[:name]
+ operation = :install
# XXX: We don't actually deal with epochs here.
case should
@@ -61,9 +63,14 @@ def install
else
# Add the package version
wanted += "-#{should}"
+ is = self.query
+ if is && Puppet::Util::Package.versioncmp(should, is[:ensure]) < 0
+ self.debug "Downgrading package #{@resource[:name]} from version #{is[:ensure]} to #{should}"
+ operation = :downgrade
+ end
end
- output = yum "-d", "0", "-e", "0", "-y", :install, wanted
+ output = yum "-d", "0", "-e", "0", "-y", operation, wanted
is = self.query
raise Puppet::Error, "Could not find package #{self.name}" unless is
View
5 lib/puppet/provider/service/freebsd.rb
@@ -18,6 +18,9 @@ def self.defpath
def rcvar
rcvar = execute([self.initscript, :rcvar], :failonfail => true, :squelch => false)
rcvar = rcvar.split("\n")
+ rcvar.delete_if {|str| str =~ /^#\s*$/}
+ rcvar[1] = rcvar[1].gsub(/^\$/, '')
+ rcvar
end
# Extract service name
@@ -44,7 +47,7 @@ def rcvar_name
def rcvar_value
value = self.rcvar[1]
self.error("No rcvar value found in rcvar") if value.nil?
- value = value.gsub!(/(.*)_enable=\"?(.*)\"?/, '\2')
+ value = value.gsub!(/(.*)_enable="?(\w+)"?/, '\2')
self.error("rcvar value is empty") if value.nil?
self.debug("rcvar value is #{value}")
value
View
4 lib/puppet/provider/user/user_role_add.rb
@@ -156,11 +156,11 @@ def password
shadow_entry[1] if shadow_entry
end
- def min_age
+ def password_min_age
shadow_entry ? shadow_entry[3] : :absent
end
- def max_age
+ def password_max_age
shadow_entry ? shadow_entry[4] : :absent
end
View
4 lib/puppet/provider/user/useradd.rb
@@ -78,7 +78,7 @@ def passcmd
end
end
- def min_age
+ def password_min_age
if Puppet.features.libshadow?
if ent = Shadow::Passwd.getspnam(@resource.name)
return ent.sp_min
@@ -87,7 +87,7 @@ def min_age
:absent
end
- def max_age
+ def password_max_age
if Puppet.features.libshadow?
if ent = Shadow::Passwd.getspnam(@resource.name)
return ent.sp_max
View
8 lib/puppet/rails.rb
@@ -52,21 +52,17 @@ def self.database_arguments
args[:port] = Puppet[:dbport] unless Puppet[:dbport].to_s.empty?
args[:username] = Puppet[:dbuser] unless Puppet[:dbuser].to_s.empty?
args[:password] = Puppet[:dbpassword] unless Puppet[:dbpassword].to_s.empty?
+ args[:pool] = Puppet[:dbconnections].to_i unless Puppet[:dbconnections].to_i <= 0
args[:database] = Puppet[:dbname]
args[:reconnect]= true
socket = Puppet[:dbsocket]
args[:socket] = socket unless socket.to_s.empty?
-
- connections = Puppet[:dbconnections].to_i
- args[:pool] = connections if connections > 0
when "oracle_enhanced":
args[:database] = Puppet[:dbname] unless Puppet[:dbname].to_s.empty?
args[:username] = Puppet[:dbuser] unless Puppet[:dbuser].to_s.empty?
args[:password] = Puppet[:dbpassword] unless Puppet[:dbpassword].to_s.empty?
-
- connections = Puppet[:dbconnections].to_i
- args[:pool] = connections if connections > 0
+ args[:pool] = Puppet[:dbconnections].to_i unless Puppet[:dbconnections].to_i <= 0
else
raise ArgumentError, "Invalid db adapter #{adapter}"
end
View
72 lib/puppet/rails/host.rb
@@ -16,16 +16,6 @@ class Puppet::Rails::Host < ActiveRecord::Base
belongs_to :source_file
has_many :resources, :dependent => :destroy, :class_name => "Puppet::Rails::Resource"
- # If the host already exists, get rid of its objects
- def self.clean(host)
- if obj = self.find_by_name(host)
- obj.rails_objects.clear
- return obj
- else
- return nil
- end
- end
-
def self.from_puppet(node)
host = find_by_name(node.name) || new(:name => node.name)
@@ -38,63 +28,6 @@ def self.from_puppet(node)
host
end
- # Store our host in the database.
- def self.store(node, resources)
- args = {}
-
- host = nil
- railsmark "Stored node" do
- transaction do
- #unless host = find_by_name(name)
-
- debug_benchmark("Searched for host")do
- unless host = find_by_name(node.name)
- host = new(:name => node.name)
- end
- end
- if ip = node.parameters["ipaddress"]
- host.ip = ip
- end
-
- if env = node.environment
- host.environment = env
- end
-
- # Store the facts into the database.
- host.merge_facts(node.parameters)
-
- debug_benchmark("Handled resources") {
- host.merge_resources(resources)
- }
-
- host.last_compile = Time.now
-
- debug_benchmark("Saved host") {
- host.save
- }
- end
-
- end
-
- # This only runs if time debugging is enabled.
- write_benchmarks
-
- host
- end
-
- # Return the value of a fact.
- def fact(name)
-
- if fv = self.fact_values.find(
- :all, :include => :fact_name,
-
- :conditions => "fact_names.name = '#{name}'")
- return fv
- else
- return nil
- end
- end
-
# returns a hash of fact_names.name => [ fact_values ] for this host.
# Note that 'fact_values' is actually a list of the value instances, not
# just actual values.
@@ -305,11 +238,6 @@ def find_resources_tags(resources)
end
end
- def update_connect_time
- self.last_connect = Time.now
- save
- end
-
def to_puppet
node = Puppet::Node.new(self.name)
{"ip" => "ipaddress", "environment" => "environment"}.each do |myparam, itsparam|
View
12 lib/puppet/resource.rb
@@ -80,6 +80,18 @@ def self.value_to_pson_data(value)
end
end
+ def yaml_property_munge(x)
+ case x
+ when Hash
+ x.inject({}) { |h,kv|
+ k,v = kv
+ h[k] = self.class.value_to_pson_data(v)
+ h
+ }
+ else self.class.value_to_pson_data(x)
+ end
+ end
+
def to_pson(*args)
to_pson_data_hash.to_pson(*args)
end
View
417 lib/puppet/simple_graph.rb
@@ -7,123 +7,45 @@
# A hopefully-faster graph class to replace the use of GRATR.
class Puppet::SimpleGraph
- # An internal class for handling a vertex's edges.
- class VertexWrapper
- attr_accessor :in, :out, :vertex
-
- # Remove all references to everything.
- def clear
- @adjacencies[:in].clear
- @adjacencies[:out].clear
- @vertex = nil
- end
-
- def initialize(vertex)
- @vertex = vertex
- @adjacencies = {:in => {}, :out => {}}
- end
-
- # Find adjacent vertices or edges.
- def adjacent(options)
- direction = options[:direction] || :out
- options[:type] ||= :vertices
-
- return send(direction.to_s + "_edges") if options[:type] == :edges
-
- @adjacencies[direction].keys.reject { |vertex| @adjacencies[direction][vertex].empty? }
- end
-
- # Add an edge to our list.
- def add_edge(direction, edge)
- opposite_adjacencies(direction, edge) << edge
- end
-
- # Return all known edges.
- def edges
- in_edges + out_edges
- end
-
- # Test whether we share an edge with a given vertex.
- def has_edge?(direction, vertex)
- return(vertex_adjacencies(direction, vertex).length > 0 ? true : false)
- end
-
- # Create methods for returning the degree and edges.
- [:in, :out].each do |direction|
- # LAK:NOTE If you decide to create methods for directly
- # testing the degree, you'll have to get the values and flatten
- # the results -- you might have duplicate edges, which can give
- # a false impression of what the degree is. That's just
- # as expensive as just getting the edge list, so I've decided
- # to only add this method.
- define_method("#{direction}_edges") do
- @adjacencies[direction].values.inject([]) { |total, adjacent| total += adjacent.to_a; total }
- end
- end
-
- # The other vertex in the edge.
- def other_vertex(direction, edge)
- case direction
- when :in; edge.source
- else
- edge.target
- end
- end
-
- # Remove an edge from our list. Assumes that we've already checked
- # that the edge is valid.
- def remove_edge(direction, edge)
- opposite_adjacencies(direction, edge).delete(edge)
- end
-
- def to_s
- vertex.to_s
- end
-
- private
-
- # These methods exist so we don't need a Hash with a default proc.
-
- # Look up the adjacencies for a vertex at the other end of an
- # edge.
- def opposite_adjacencies(direction, edge)
- opposite_vertex = other_vertex(direction, edge)
- vertex_adjacencies(direction, opposite_vertex)
- end
-
- # Look up the adjacencies for a given vertex.
- def vertex_adjacencies(direction, vertex)
- @adjacencies[direction][vertex] ||= Set.new
- @adjacencies[direction][vertex]
- end
- end
-
+ #
+ # All public methods of this class must maintain (assume ^ ensure) the following invariants, where "=~=" means
+ # equiv. up to order:
+ #
+ # @in_to.keys =~= @out_to.keys =~= all vertices
+ # @in_to.values.collect { |x| x.values }.flatten =~= @out_from.values.collect { |x| x.values }.flatten =~= all edges
+ # @in_to[v1][v2] =~= @out_from[v2][v1] =~= all edges from v1 to v2
+ # @in_to [v].keys =~= vertices with edges leading to v
+ # @out_from[v].keys =~= vertices with edges leading from v
+ # no operation may shed reference loops (for gc)
+ # recursive operation must scale with the depth of the spanning trees, or better (e.g. no recursion over the set
+ # of all vertices, etc.)
+ #
+ # This class is intended to be used with DAGs. However, if the
+ # graph has a cycle, it will not cause non-termination of any of the
+ # algorithms. The topsort method detects and reports cycles.
+ #
def initialize
- @vertices = {}
- @edges = []
+ @in_to = {}
+ @out_from = {}
+ @upstream_from = {}
+ @downstream_from = {}
end
# Clear our graph.
def clear
- @vertices.each { |vertex, wrapper| wrapper.clear }
- @vertices.clear
- @edges.clear
- end
-
- # Which resources a given resource depends upon.
- def dependents(resource)
- tree_from_vertex(resource).keys
+ @in_to.clear
+ @out_from.clear
+ @upstream_from.clear
+ @downstream_from.clear
end
# Which resources depend upon the given resource.
def dependencies(resource)
- # Cache the reversal graph, because it's somewhat expensive
- # to create.
- @reversal ||= reversal
- # Strangely, it's significantly faster to search a reversed
- # tree in the :out direction than to search a normal tree
- # in the :in direction.
- @reversal.tree_from_vertex(resource, :out).keys
+ vertex?(resource) ? upstream_from_vertex(resource).keys : []
+ end
+
+ def dependents(resource)
+ vertex?(resource) ? downstream_from_vertex(resource).keys : []
end
# Whether our graph is directed. Always true. Used to produce dot files.
@@ -133,8 +55,7 @@ def directed?
# Determine all of the leaf nodes below a given vertex.
def leaves(vertex, direction = :out)
- tree = tree_from_vertex(vertex, direction)
- l = tree.keys.find_all { |c| adjacent(c, :direction => direction).empty? }
+ tree_from_vertex(vertex, direction).keys.find_all { |c| adjacent(c, :direction => direction).empty? }
end
# Collect all of the edges that the passed events match. Returns
@@ -149,9 +70,7 @@ def matching_edges(event, base = nil)
# Get all of the edges that this vertex should forward events
# to, which is the same thing as saying all edges directly below
# This vertex in the graph.
- adjacent(source, :direction => :out, :type => :edges).find_all do |edge|
- edge.match?(event.name)
- end
+ @out_from[source].values.flatten.find_all { |edge| edge.match?(event.name) }
end
# Return a reversed version of this graph.
@@ -159,20 +78,50 @@ def reversal
result = self.class.new
vertices.each { |vertex| result.add_vertex(vertex) }
edges.each do |edge|
- newedge = edge.class.new(edge.target, edge.source, edge.label)
- result.add_edge(newedge)
+ result.add_edge edge.class.new(edge.target, edge.source, edge.label)
end
result
end
# Return the size of the graph.
def size
- @vertices.length
+ vertices.size
end
- # Return the graph as an array.
def to_a
- @vertices.keys
+ vertices
+ end
+
+ # Provide a topological sort with cycle reporting
+ def topsort_with_cycles
+ degree = {}
+ zeros = []
+ result = []
+
+ # Collect each of our vertices, with the number of in-edges each has.
+ vertices.each do |v|
+ edges = @in_to[v].dup
+ zeros << v if edges.empty?
+ degree[v] = edges
+ end
+
+ # Iterate over each 0-degree vertex, decrementing the degree of
+ # each of its out-edges.
+ while v = zeros.pop
+ result << v
+ @out_from[v].each { |v2,es|
+ degree[v2].delete(v)
+ zeros << v2 if degree[v2].empty?
+ }
+ end
+
+ # If we have any vertices left with non-zero in-degrees, then we've found a cycle.
+ if cycles = degree.values.reject { |ns| ns.empty? } and cycles.length > 0
+ message = cycles.collect { |edges| '('+edges.collect { |e| e.to_s }.join(", ")+')' }.join(", ")
+ raise Puppet::Error, "Found dependency cycles in the following relationships: #{message}; try using the '--graph' option and open the '.dot' files in OmniGraffle or GraphViz"
+ end
+
+ result
end
# Provide a topological sort.
@@ -182,26 +131,24 @@ def topsort
result = []
# Collect each of our vertices, with the number of in-edges each has.
- @vertices.each do |name, wrapper|
- edges = wrapper.in_edges
- zeros << wrapper if edges.length == 0
- degree[wrapper.vertex] = edges
+ vertices.each do |v|
+ edges = @in_to[v]
+ zeros << v if edges.empty?
+ degree[v] = edges.length
end
# Iterate over each 0-degree vertex, decrementing the degree of
# each of its out-edges.
- while wrapper = zeros.pop
- result << wrapper.vertex
- wrapper.out_edges.each do |edge|
- degree[edge.target].delete(edge)
- zeros << @vertices[edge.target] if degree[edge.target].length == 0
- end
+ while v = zeros.pop
+ result << v
+ @out_from[v].each { |v2,es|
+ zeros << v2 if (degree[v2] -= 1) == 0
+ }
end
# If we have any vertices left with non-zero in-degrees, then we've found a cycle.
- if cycles = degree.find_all { |vertex, edges| edges.length > 0 } and cycles.length > 0
- message = cycles.collect { |vertex, edges| edges.collect { |e| e.to_s }.join(", ") }.join(", ")
- raise Puppet::Error, "Found dependency cycles in the following relationships: #{message}; try using the '--graph' option and open the '.dot' files in OmniGraffle or GraphViz"
+ if cycles = degree.values.reject { |ns| ns == 0 } and cycles.length > 0
+ topsort_with_cycles
end
result
@@ -209,103 +156,80 @@ def topsort
# Add a new vertex to the graph.
def add_vertex(vertex)
- @reversal = nil
- return false if vertex?(vertex)
- setup_vertex(vertex)
- true # don't return the VertexWrapper instance.
+ @in_to[vertex] ||= {}
+ @out_from[vertex] ||= {}
end
# Remove a vertex from the graph.
- def remove_vertex!(vertex)
- return nil unless vertex?(vertex)
- @vertices[vertex].edges.each { |edge| remove_edge!(edge) }
- @edges -= @vertices[vertex].edges
- @vertices[vertex].clear
- @vertices.delete(vertex)
+ def remove_vertex!(v)
+ return unless vertex?(v)
+ @upstream_from.clear
+ @downstream_from.clear
+ (@in_to[v].values+@out_from[v].values).flatten.each { |e| remove_edge!(e) }
+ @in_to.delete(v)
+ @out_from.delete(v)
end
# Test whether a given vertex is in the graph.
- def vertex?(vertex)
- @vertices.include?(vertex)
+ def vertex?(v)
+ @in_to.include?(v)
end
# Return a list of all vertices.
def vertices
- @vertices.keys
+ @in_to.keys
end
# Add a new edge. The graph user has to create the edge instance,
# since they have to specify what kind of edge it is.
- def add_edge(source, target = nil, label = nil)
- @reversal = nil
- if target
- edge = Puppet::Relationship.new(source, target, label)
- else
- edge = source
- end
- [edge.source, edge.target].each { |vertex| setup_vertex(vertex) unless vertex?(vertex) }
- @vertices[edge.source].add_edge :out, edge
- @vertices[edge.target].add_edge :in, edge
- @edges << edge
- true
+ def add_edge(e,*a)
+ return add_relationship(e,*a) unless a.empty?
+ @upstream_from.clear
+ @downstream_from.clear
+ add_vertex(e.source)
+ add_vertex(e.target)
+ @in_to[ e.target][e.source] ||= []; @in_to[ e.target][e.source] |= [e]
+ @out_from[e.source][e.target] ||= []; @out_from[e.source][e.target] |= [e]
end
- # Find a matching edge. Note that this only finds the first edge,
- # not all of them or whatever.
- def edge(source, target)
- @edges.each_with_index { |test_edge, index| return test_edge if test_edge.source == source and test_edge.target == target }
+ def add_relationship(source, target, label = nil)
+ add_edge Puppet::Relationship.new(source, target, label)
end
- def edge_label(source, target)
- return nil unless edge = edge(source, target)
- edge.label
+ # Find all matching edges.
+ def edges_between(source, target)
+ (@out_from[source] || {})[target] || []
end
# Is there an edge between the two vertices?
def edge?(source, target)
- return false unless vertex?(source) and vertex?(target)
-
- @vertices[source].has_edge?(:out, target)
+ vertex?(source) and vertex?(target) and @out_from[source][target]
end
def edges
- @edges.dup
+ @in_to.values.collect { |x| x.values }.flatten
end
- # Remove an edge from our graph.
- def remove_edge!(edge)
- @vertices[edge.source].remove_edge(:out, edge)
- @vertices[edge.target].remove_edge(:in, edge)
-
- @edges.delete(edge)
- nil
+ def each_edge
+ @in_to.each { |t,ns| ns.each { |s,es| es.each { |e| yield e }}}
end
- # Find adjacent edges.
- def adjacent(vertex, options = {})
- return [] unless wrapper = @vertices[vertex]
- wrapper.adjacent(options)
+ # Remove an edge from our graph.
+ def remove_edge!(e)
+ if edge?(e.source,e.target)
+ @upstream_from.clear
+ @downstream_from.clear
+ @in_to [e.target].delete e.source if (@in_to [e.target][e.source] -= [e]).empty?
+ @out_from[e.source].delete e.target if (@out_from[e.source][e.target] -= [e]).empty?
+ end
end
- private
-
- # An internal method that skips the validation, so we don't have
- # duplicate validation calls.
- def setup_vertex(vertex)
- @vertices[vertex] = VertexWrapper.new(vertex)
+ # Find adjacent edges.
+ def adjacent(v, options = {})
+ return [] unless ns = (options[:direction] == :in) ? @in_to[v] : @out_from[v]
+ (options[:type] == :edges) ? ns.values.flatten : ns.keys
end
-
- public
-
-# # For some reason, unconnected vertices do not show up in
-# # this graph.
-# def to_jpg(path, name)
-# gv = vertices
-# Dir.chdir(path) do
-# induced_subgraph(gv).write_to_graphic_file('jpg', name)
-# end
-# end
-
+
# Take container information from another graph and use it
# to replace any container vertices with their respective leaves.
# This creates direct relationships where there were previously
@@ -381,6 +305,26 @@ def tree_from_vertex(start, direction = :out)
predecessor
end
+ def downstream_from_vertex(v)
+ return @downstream_from[v] if @downstream_from[v]
+ result = @downstream_from[v] = {}
+ @out_from[v].keys.each do |node|
+ result[node] = 1
+ result.update(downstream_from_vertex(node))
+ end
+ result
+ end
+
+ def upstream_from_vertex(v)
+ return @upstream_from[v] if @upstream_from[v]
+ result = @upstream_from[v] = {}
+ @in_to[v].keys.each do |node|
+ result[node] = 1
+ result.update(upstream_from_vertex(node))
+ end
+ result
+ end
+
# LAK:FIXME This is just a paste of the GRATR code with slight modifications.
# Return a DOT::DOTDigraph for directed graphs or a DOT::DOTSubgraph for an
@@ -422,18 +366,6 @@ def dotty (params = {}, dotfile = 'graph.dot')
system('dotty', dotfile)
end
- # Use +dot+ to create a graphical representation of the graph. Returns the
- # filename of the graphics file.
- def write_to_graphic_file (fmt='png', dotfile='graph')
- src = dotfile + '.dot'
- dot = dotfile + '.' + fmt
-
- File.open(src, 'w') {|f| f << self.to_dot << "\n"}
-
- system( "dot -T#{fmt} #{src} -o #{dot}" )
- dot
- end
-
# Produce the graph files if requested.
def write_graph(name)
return unless Puppet[:graph]
@@ -445,4 +377,73 @@ def write_graph(name)
f.puts to_dot("name" => name.to_s.capitalize)
}
end
+
+ # This flag may be set to true to use the new YAML serialzation
+ # format (where @vertices is a simple list of vertices rather than a
+ # list of VertexWrapper objects). Deserialization supports both
+ # formats regardless of the setting of this flag.
+ class << self
+ attr_accessor :use_new_yaml_format
+ end
+ self.use_new_yaml_format = false
+
+ # Stub class to allow graphs to be represented in YAML using the old
+ # (version 2.6) format.
+ class VertexWrapper
+ attr_reader :vertex, :adjacencies
+ def initialize(vertex, adjacencies)
+ @vertex = vertex
+ @adjacencies = adjacencies
+ end
+ end
+
+ # instance_variable_get is used by Object.to_zaml to get instance
+ # variables. Override it so that we can simulate the presence of
+ # instance variables @edges and @vertices for serialization.
+ def instance_variable_get(v)
+ case v.to_s
+ when '@edges' then
+ edges
+ when '@vertices' then
+ if self.class.use_new_yaml_format
+ vertices
+ else
+ result = {}
+ vertices.each do |vertex|
+ adjacencies = {}
+ [:in, :out].each do |direction|
+ adjacencies[direction] = {}
+ adjacent(vertex, :direction => direction, :type => :edges).each do |edge|
+ other_vertex = direction == :in ? edge.source : edge.target
+ (adjacencies[direction][other_vertex] ||= Set.new).add(edge)
+ end
+ end
+ result[vertex] = Puppet::SimpleGraph::VertexWrapper.new(vertex, adjacencies)
+ end
+ result
+ end
+ else
+ super(v)
+ end
+ end
+
+ def to_yaml_properties
+ other_vars = instance_variables.reject { |v| %w{@in_to @out_from @upstream_from @downstream_from}.include?(v) }
+ (other_vars + %w{@vertices @edges}).sort.uniq
+ end
+
+ def yaml_initialize(tag, var)
+ initialize()
+ vertices = var.delete('vertices')
+ edges = var.delete('edges')
+ if vertices.is_a?(Hash)
+ # Support old (2.6) format
+ vertices = vertices.keys
+ end
+ vertices.each { |v| add_vertex(v) }
+ edges.each { |e| add_edge(e) }
+ var.each do |varname, value|
+ instance_variable_set("@#{varname}", value)
+ end
+ end
end
View
39 lib/puppet/transaction/report.rb
@@ -62,30 +62,49 @@ def name
host
end
- # Provide a summary of this report.
+ # Provide a human readable textual summary of this report.
def summary
+ report = raw_summary
+
ret = ""
+ report.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |key|
+ ret += "#{Puppet::Util::Metric.labelize(key)}:\n"
- @metrics.sort { |a,b| a[1].label <=> b[1].label }.each do |name, metric|
- ret += "#{metric.label}:\n"
- metric.values.sort { |a,b|
+ report[key].keys.sort { |a,b|
# sort by label
- if a[0] == :total
+ if a == :total
1
- elsif b[0] == :total
+ elsif b == :total
-1
else
- a[1] <=> b[1]
+ report[key][a].to_s <=> report[key][b].to_s
end
- }.each do |name, label, value|
+ }.each do |label|
+ value = report[key][label]
next if value == 0
value = "%0.2f" % value if value.is_a?(Float)
- ret += " %15s %s\n" % [label + ":", value]
+ ret += " %15s %s\n" % [Puppet::Util::Metric.labelize(label) + ":", value]
end
end
ret
end
+ # Provide a raw hash summary of this report.
+ def raw_summary
+ report = {}
+
+ @metrics.each do |name, metric|
+ key = metric.name.to_s
+ report[key] = {}
+ metric.values.each do |name, label, value|
+ report[key][name.to_s] = value
+ end
+ report[key]["total"] = 0 unless key == "time" or report[key].include?("total")
+ end
+ (report["time"] ||= {})["last_run"] = Time.now.tv_sec
+ report
+ end
+
# Based on the contents of this report's metrics, compute a single number
# that represents the report. The resulting number is a bitmask where
# individual bits represent the presence of different metrics.
@@ -103,7 +122,6 @@ def calculate_change_metrics
resource_statuses.each do |name, status|
metrics[:total] += status.change_count if status.change_count
end
-
add_metric(:changes, metrics)
end
@@ -124,7 +142,6 @@ def calculate_resource_metrics
metrics[:total] = resource_statuses.length
resource_statuses.each do |name, status|
-
Puppet::Resource::Status::STATES.each do |state|
metrics[state] += 1 if status.send(state)
end
View
8 lib/puppet/type/exec.rb
@@ -553,13 +553,7 @@ def checkexe(cmd)
if self[:path]
if Puppet.features.posix? and !File.exists?(exe)
withenv :PATH => self[:path].join(File::PATH_SEPARATOR) do
- path = %x{which #{exe}}.chomp
- if path == ""
- raise ArgumentError,
- "Could not find command '#{exe}'"
- else
- exe = path
- end
+ exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'")
end
elsif Puppet.features.microsoft_windows? and !File.exists?(exe)
self[:path].each do |path|
View
28 lib/puppet/type/host.rb
@@ -5,11 +5,11 @@ module Puppet
newproperty(:ip) do
desc "The host's IP address, IPv4 or IPv6."
- validate do |value|
- unless value =~ /((([0-9a-fA-F]+:){7}[0-9a-fA-F]+)|(([0-9a-fA-F]+:)*[0-9a-fA-F]+)?::(([0-9a-fA-F]+:)*[0-9a-fA-F]+)?)|((25[0-5]|2[0-4][\d]|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})/
- raise Puppet::Error, "Invalid IP address"
+ validate do |value|
+ unless value =~ /^((([0-9a-fA-F]+:){7}[0-9a-fA-F]+)|(([0-9a-fA-F]+:)*[0-9a-fA-F]+)?::(([0-9a-fA-F]+:)*[0-9a-fA-F]+)?)|((25[0-5]|2[0-4][\d]|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$/
+ raise Puppet::Error, "Invalid IP address"
+ end
end
- end
end
@@ -26,21 +26,6 @@ def is_to_s(currentvalue = @is)
currentvalue.join(" ")
end
- def retrieve
- is = super
- case is
- when String
- is = is.split(/\s*,\s*/)
- when Symbol
- is = [is]
- when Array
- # nothing
- else
- raise Puppet::DevError, "Invalid @is type #{is.class}"
- end
- is
- end
-
# We actually want to return the whole array here, not just the first
# value.
def should
@@ -61,9 +46,14 @@ def should_to_s(newvalue = @should)
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
+ desc "A comment that will be attached to the line with a # character"
+ end
+
newproperty(:target) do
desc "The file in which to store service information. Only used by
those providers that write to disk."
View
10 lib/puppet/type/schedule.rb
@@ -16,7 +16,7 @@ module Puppet
Thus, it behooves you to use wider scheduling (e.g., over a couple of
hours) combined with periods and repetitions. For instance, if you
wanted to restrict certain resources to only running once, between
- the hours of two and 4 AM, then you would use this schedule::
+ the hours of two and 4 AM, then you would use this schedule:
schedule { maint:
range => \"2 - 4\",
@@ -33,7 +33,7 @@ module Puppet
Puppet automatically creates a schedule for each valid period with the
same name as that period (e.g., hourly and daily). Additionally,
a schedule named *puppet* is created and used as the default,
- with the following attributes::
+ with the following attributes:
schedule { puppet:
period => hourly,
@@ -45,11 +45,11 @@ module Puppet
newparam(:name) do
desc "The name of the schedule. This name is used to retrieve the
- schedule when assigning it to an object::
+ schedule when assigning it to an object:
schedule { daily:
period => daily,
- range => [2, 4]
+ range => \"2 - 4\",
}
exec { \"/usr/bin/apt-get update\":
@@ -65,7 +65,7 @@ module Puppet
is always a range within a 24 hour period, and hours must be
specified in numbers between 0 and 23, inclusive. Minutes and
seconds can be provided, using the normal colon as a separator.
- For instance::
+ For instance:
schedule { maintenance:
range => \"1:30 - 4:30\"
View
38 lib/puppet/util.rb
@@ -3,6 +3,7 @@
require 'puppet/util/monkey_patches'
require 'sync'
require 'puppet/external/lock'
+require 'monitor'
module Puppet
# A command failed to execute.
@@ -17,8 +18,7 @@ module Util
require 'puppet/util/posix'
extend Puppet::Util::POSIX
- # Create a hash to store the different sync objects.
- @@syncresources = {}
+ @@sync_objects = {}.extend MonitorMixin
def self.activerecord_version
if (defined?(::ActiveRecord) and defined?(::ActiveRecord::VERSION) and defined?(::ActiveRecord::VERSION::MAJOR) and defined?(::ActiveRecord::VERSION::MINOR))
@@ -28,10 +28,19 @@ def self.activerecord_version
end
end
- # Return the sync object associated with a given resource.
- def self.sync(resource)
- @@syncresources[resource] ||= Sync.new
- @@syncresources[resource]
+
+ def self.synchronize_on(x,type)
+ sync_object,users = 0,1
+ begin
+ @@sync_objects.synchronize {
+ (@@sync_objects[x] ||= [Sync.new,0])[users] += 1
+ }
+ @@sync_objects[x][sync_object].synchronize(type) { yield }
+ ensure
+ @@sync_objects.synchronize {
+ @@sync_objects.delete(x) unless (@@sync_objects[x][users] -= 1) > 0
+ }
+ end
end
# Change the process to a different user
@@ -189,7 +198,7 @@ def benchmark(*args)
end
end
- def binary(bin)
+ def which(bin)
if bin =~ /^\//
return bin if FileTest.file? bin and FileTest.executable? bin
else
@@ -200,7 +209,7 @@ def binary(bin)
end
nil
end
- module_function :binary
+ module_function :which
# Execute the provided command in a pipe, yielding the pipe object.
def execpipe(command, failonfail = true)
@@ -359,9 +368,7 @@ def execute(command, arguments = {:failonfail => true, :combine => true})
# Create an exclusive lock.
def threadlock(resource, type = Sync::EX)
- Puppet::Util.sync(resource).synchronize(type) do
- yield
- end
+ Puppet::Util.synchronize_on(resource,type) { yield }
end
# Because some modules provide their own version of this method.
@@ -371,15 +378,10 @@ def threadlock(resource, type = Sync::EX)
def memory
unless defined?(@pmap)
- pmap = %x{which pmap 2>/dev/null}.chomp
- if $CHILD_STATUS != 0 or pmap =~ /^no/
- @pmap = nil
- else
- @pmap = pmap
- end
+ @pmap = which('pmap')
end
if @pmap
- return %x{pmap #{Process.pid}| grep total}.chomp.sub(/^\s*total\s+/, '').sub(/K$/, '').to_i
+ %x{#{@pmap} #{Process.pid}| grep total}.chomp.sub(/^\s*total\s+/, '').sub(/K$/, '').to_i
else
0
end
View
2  lib/puppet/util/command_line.rb
@@ -62,7 +62,7 @@ def execute_external_subcommand
external_command = "puppet-#{subcommand_name}"
require 'puppet/util'
- path_to_subcommand = Puppet::Util.binary( external_command )
+ path_to_subcommand = Puppet::Util.which( external_command )
return false unless path_to_subcommand
system( path_to_subcommand, *args )
View
9 lib/puppet/util/file_locking.rb
@@ -6,7 +6,7 @@ module Puppet::Util::FileLocking
# Create a shared lock for reading
def readlock(file)
raise ArgumentError, "#{file} is not a file" unless !File.exists?(file) or File.file?(file)
- Puppet::Util.sync(file).synchronize(Sync::SH) do
+ Puppet::Util.synchronize_on(file,Sync::SH) do
File.open(file) { |f|
f.lock_shared { |lf| yield lf }
}
@@ -33,9 +33,12 @@ def writelock(file, mode = nil)
end
end
- Puppet::Util.sync(file).synchronize(Sync::EX) do
- File.open(file, "w", mode) do |rf|
+ Puppet::Util.synchronize_on(file,Sync::EX) do
+ File.open(file, File::Constants::CREAT | File::Constants::WRONLY, mode) do |rf|
rf.lock_exclusive do |lrf|
+ # poor's man open(2) O_EXLOCK|O_TRUNC
+ lrf.seek(0, IO::SEEK_SET)
+ lrf.truncate(0)
yield lrf
end
end
View
1  lib/puppet/util/log.rb
@@ -57,6 +57,7 @@ def self.close_all
destinations.keys.each { |dest|
close(dest)
}
+ raise Puppet::DevError.new("Log.close_all failed to close #{@destinations.keys.inspect}") if !@destinations.empty?
end
# Flush any log destinations that support such operations.
View
14 lib/puppet/util/log/destinations.rb
@@ -203,8 +203,20 @@ def handle(msg)
end
# Log to an array, just for testing.
+module Puppet::Test
+ class LogCollector
+ def initialize(logs)
+ @logs = logs
+ end
+
+ def <<(value)
+ @logs << value
+ end
+ end
+end
+
Puppet::Util::Log.newdesttype :array do
- match "Array"
+ match "Puppet::Test::LogCollector"
def initialize(messages)
@messages = messages
View
10 lib/puppet/util/metric.rb
@@ -64,7 +64,7 @@ def dump
end
def graph(range = nil)
- unless Puppet.features.rrd?
+ unless Puppet.features.rrd? || Puppet.features.rrd_legacy?
Puppet.warning "RRD library is missing; cannot graph metrics"
return