puppet 2.6.12 → 2.6.13

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (43) hide show
  1. data/CHANGELOG +30 -0
  2. data/CONTRIBUTING.md +299 -0
  3. data/conf/redhat/puppet.spec +4 -1
  4. data/ext/upload_facts.rb +120 -0
  5. data/lib/puppet.rb +1 -1
  6. data/lib/puppet/application/inspect.rb +5 -2
  7. data/lib/puppet/application/queue.rb +11 -1
  8. data/lib/puppet/application/resource.rb +3 -0
  9. data/lib/puppet/defaults.rb +2 -1
  10. data/lib/puppet/indirector/facts/inventory_service.rb +20 -0
  11. data/lib/puppet/indirector/report/processor.rb +2 -0
  12. data/lib/puppet/network/handler/filebucket.rb +2 -0
  13. data/lib/puppet/network/handler/fileserver.rb +1 -0
  14. data/lib/puppet/network/handler/master.rb +1 -0
  15. data/lib/puppet/network/handler/report.rb +2 -0
  16. data/lib/puppet/network/handler/runner.rb +1 -0
  17. data/lib/puppet/network/handler/status.rb +2 -0
  18. data/lib/puppet/network/http_server.rb +3 -0
  19. data/lib/puppet/network/http_server/mongrel.rb +129 -0
  20. data/lib/puppet/provider/exec/posix.rb +6 -3
  21. data/lib/puppet/provider/exec/shell.rb +11 -2
  22. data/lib/puppet/resource/catalog.rb +6 -3
  23. data/lib/puppet/ssl/host.rb +2 -0
  24. data/lib/puppet/type/cron.rb +13 -12
  25. data/lib/puppet/type/file.rb +2 -2
  26. data/lib/puppet/type/file/source.rb +1 -1
  27. data/lib/puppet/type/user.rb +8 -0
  28. data/lib/puppet/util.rb +16 -41
  29. data/lib/puppet/util/settings.rb +1 -1
  30. data/lib/puppet/util/suidmanager.rb +48 -14
  31. data/spec/unit/application/inspect_spec.rb +5 -0
  32. data/spec/unit/application/resource_spec.rb +25 -0
  33. data/spec/unit/configurer_spec.rb +5 -0
  34. data/spec/unit/indirector/facts/inventory_service_spec.rb +22 -0
  35. data/spec/unit/indirector/report/processor_spec.rb +7 -5
  36. data/spec/unit/resource/catalog_spec.rb +54 -7
  37. data/spec/unit/ssl/host_spec.rb +58 -9
  38. data/spec/unit/type/file_spec.rb +6 -0
  39. data/spec/unit/type/user_spec.rb +8 -0
  40. data/spec/unit/util/settings_spec.rb +11 -0
  41. data/spec/unit/util/suidmanager_spec.rb +210 -0
  42. metadata +11 -5
  43. data/test/puppet/tc_suidmanager.rb +0 -120
data/lib/puppet.rb CHANGED
@@ -24,7 +24,7 @@ require 'puppet/util/run_mode'
24
24
  # it's also a place to find top-level commands like 'debug'
25
25
 
26
26
  module Puppet
27
- PUPPETVERSION = '2.6.12'
27
+ PUPPETVERSION = '2.6.13'
28
28
 
29
29
  def Puppet.version
30
30
  PUPPETVERSION
@@ -1,6 +1,4 @@
1
- require 'puppet'
2
1
  require 'puppet/application'
3
- require 'puppet/file_bucket/dipper'
4
2
 
5
3
  class Puppet::Application::Inspect < Puppet::Application
6
4
 
@@ -97,6 +95,11 @@ Licensed under the GNU General Public License version 2
97
95
  Puppet::Resource::Catalog.terminus_class = :yaml
98
96
  end
99
97
 
98
+ def preinit
99
+ require 'puppet'
100
+ require 'puppet/file_bucket/dipper'
101
+ end
102
+
100
103
  def run_command
101
104
  benchmark(:notice, "Finished inspection") do
102
105
  retrieval_starttime = Time.now
@@ -10,7 +10,6 @@ class Puppet::Application::Queue < Puppet::Application
10
10
  require 'puppet/daemon'
11
11
  @daemon = Puppet::Daemon.new
12
12
  @daemon.argv = ARGV.dup
13
- Puppet::Util::Log.newdestination(:console)
14
13
 
15
14
  # Do an initial trap, so that cancels don't get a stack trace.
16
15
 
@@ -37,6 +36,16 @@ class Puppet::Application::Queue < Puppet::Application
37
36
  option("--debug","-d")
38
37
  option("--verbose","-v")
39
38
 
39
+ option("--logdest DEST", "-l DEST") do |arg|
40
+ begin
41
+ Puppet::Util::Log.newdestination(arg)
42
+ options[:setdest] = true
43
+ rescue => detail
44
+ puts detail.backtrace if Puppet[:debug]
45
+ $stderr.puts detail.to_s
46
+ end
47
+ end
48
+
40
49
  def main
41
50
  require 'puppet/indirector/catalog/queue' # provides Puppet::Indirector::Queue.subscribe
42
51
  Puppet.notice "Starting puppetqd #{Puppet.version}"
@@ -67,6 +76,7 @@ class Puppet::Application::Queue < Puppet::Application
67
76
  Puppet::Util::Log.level = :info
68
77
  end
69
78
  end
79
+ Puppet::Util::Log.newdestination(:syslog) unless options[:setdest]
70
80
  end
71
81
 
72
82
  def setup
@@ -81,6 +81,9 @@ class Puppet::Application::Resource < Puppet::Application
81
81
  [ Puppet::Resource.new( type, name, :parameters => params ).save( key ) ]
82
82
  end
83
83
  else
84
+ if type == "file"
85
+ raise "Listing all file instances is not supported. Please specify a file or directory, e.g. puppet resource file /etc"
86
+ end
84
87
  Puppet::Resource.search( key, {} )
85
88
  end.map(&format).join("\n")
86
89
 
@@ -124,7 +124,8 @@ module Puppet
124
124
  :desc => "The node facts terminus.",
125
125
  :hook => proc do |value|
126
126
  require 'puppet/node/facts'
127
- if value.to_s == "rest"
127
+ # Cache to YAML if we're uploading facts away
128
+ if %w[rest inventory_service].include? value.to_s
128
129
  Puppet::Node::Facts.cache_class = :yaml
129
130
  end
130
131
  end
@@ -0,0 +1,20 @@
1
+ require 'puppet/node/facts'
2
+ require 'puppet/indirector/rest'
3
+
4
+ class Puppet::Node::Facts::InventoryService < Puppet::Indirector::REST
5
+ desc "Find and save facts about nodes using a remote inventory service."
6
+ use_server_setting(:inventory_server)
7
+ use_port_setting(:inventory_port)
8
+
9
+ # We don't want failing to upload to the inventory service to cause any
10
+ # failures, so we just suppress them and warn.
11
+ def save(request)
12
+ begin
13
+ super
14
+ true
15
+ rescue => e
16
+ Puppet.warning "Could not upload facts for #{request.key} to inventory service: #{e.to_s}"
17
+ false
18
+ end
19
+ end
20
+ end
@@ -20,9 +20,11 @@ class Puppet::Transaction::Report::Processor < Puppet::Indirector::Code
20
20
  # LAK:NOTE This isn't necessarily the best design, but it's backward
21
21
  # compatible and that's good enough for now.
22
22
  def process(report)
23
+ Puppet.debug "Recieved report to process from #{report.host}"
23
24
  return if Puppet[:reports] == "none"
24
25
 
25
26
  reports.each do |name|
27
+ Puppet.debug "Processing report from #{report.host} with processor #{name}"
26
28
  if mod = Puppet::Reports.report(name)
27
29
  # We have to use a dup because we're including a module in the
28
30
  # report.
@@ -1,6 +1,8 @@
1
1
  require 'fileutils'
2
2
  require 'digest/md5'
3
3
  require 'puppet/external/base64'
4
+ require 'puppet/network/handler'
5
+ require 'xmlrpc/server'
4
6
 
5
7
  class Puppet::Network::Handler # :nodoc:
6
8
  # Accept files and store them by md5 sum, returning the md5 sum back
@@ -4,6 +4,7 @@ require 'webrick/httpstatus'
4
4
  require 'cgi'
5
5
  require 'delegate'
6
6
  require 'sync'
7
+ require 'puppet/network/handler'
7
8
 
8
9
  require 'puppet/network/handler'
9
10
  require 'puppet/network/xmlrpc/server'
@@ -2,6 +2,7 @@ require 'openssl'
2
2
  require 'puppet'
3
3
  require 'xmlrpc/server'
4
4
  require 'yaml'
5
+ require 'puppet/network/handler'
5
6
 
6
7
  class Puppet::Network::Handler
7
8
  class MasterError < Puppet::Error; end
@@ -1,5 +1,7 @@
1
1
  require 'puppet/util/instance_loader'
2
2
  require 'puppet/reports'
3
+ require 'puppet/network/handler'
4
+ require 'xmlrpc/server'
3
5
 
4
6
  # A simple server for triggering a new run on a Puppet client.
5
7
  class Puppet::Network::Handler
@@ -1,5 +1,6 @@
1
1
  require 'puppet/run'
2
2
  require 'puppet/network/handler'
3
+ require 'xmlrpc/server'
3
4
 
4
5
  class Puppet::Network::Handler
5
6
  class MissingMasterError < RuntimeError; end # Cannot find the master client
@@ -1,3 +1,5 @@
1
+ require 'puppet/network/handler'
2
+ require 'xmlrpc/server'
1
3
  class Puppet::Network::Handler
2
4
  class Status < Handler
3
5
  desc "A simple interface for testing Puppet connectivity."
@@ -0,0 +1,3 @@
1
+ # Just a stub, so we can correctly scope other classes.
2
+ module Puppet::Network::HTTPServer # :nodoc:
3
+ end
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ # File: 06-11-14-mongrel_xmlrpc.rb
3
+ # Author: Manuel Holtgrewe <purestorm at ggnore.net>
4
+ #
5
+ # Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies
6
+ #
7
+ # This file is based heavily on a file retrieved from
8
+ # http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/
9
+
10
+ require 'rubygems'
11
+ require 'mongrel'
12
+ require 'xmlrpc/server'
13
+ require 'puppet/network/xmlrpc/server'
14
+ require 'puppet/network/http_server'
15
+ require 'puppet/network/client_request'
16
+ require 'puppet/network/handler'
17
+
18
+ require 'resolv'
19
+
20
+ # This handler can be hooked into Mongrel to accept HTTP requests. After
21
+ # checking whether the request itself is sane, the handler forwards it
22
+ # to an internal instance of XMLRPC::BasicServer to process it.
23
+ #
24
+ # You can access the server by calling the Handler's "xmlrpc_server"
25
+ # attribute accessor method and add XMLRPC handlers there. For example:
26
+ #
27
+ # <pre>
28
+ # handler = XmlRpcHandler.new
29
+ # handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
30
+ # </pre>
31
+ module Puppet::Network
32
+ class HTTPServer::Mongrel < ::Mongrel::HttpHandler
33
+ attr_reader :xmlrpc_server
34
+
35
+ def initialize(handlers)
36
+ if Puppet[:debug]
37
+ $mongrel_debug_client = true
38
+ Puppet.debug 'Mongrel client debugging enabled. [$mongrel_debug_client = true].'
39
+ end
40
+ # Create a new instance of BasicServer. We are supposed to subclass it
41
+ # but that does not make sense since we would not introduce any new
42
+ # behaviour and we have to subclass Mongrel::HttpHandler so our handler
43
+ # works for Mongrel.
44
+ @xmlrpc_server = Puppet::Network::XMLRPCServer.new
45
+ handlers.each do |name|
46
+ unless handler = Puppet::Network::Handler.handler(name)
47
+ raise ArgumentError, "Invalid handler #{name}"
48
+ end
49
+ @xmlrpc_server.add_handler(handler.interface, handler.new({}))
50
+ end
51
+ end
52
+
53
+ # This method produces the same results as XMLRPC::CGIServer.serve
54
+ # from Ruby's stdlib XMLRPC implementation.
55
+ def process(request, response)
56
+ # Make sure this has been a POST as required for XMLRPC.
57
+ request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET
58
+ if request_method != "POST"
59
+ response.start(405) { |head, out| out.write("Method Not Allowed") }
60
+ return
61
+ end
62
+
63
+ # Make sure the user has sent text/xml data.
64
+ request_mime = request.params["CONTENT_TYPE"] || "text/plain"
65
+ if parse_content_type(request_mime).first != "text/xml"
66
+ response.start(400) { |head, out| out.write("Bad Request") }
67
+ return
68
+ end
69
+
70
+ # Make sure there is data in the body at all.
71
+ length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i
72
+ if length <= 0
73
+ response.start(411) { |head, out| out.write("Length Required") }
74
+ return
75
+ end
76
+
77
+ # Check the body to be valid.
78
+ if request.body.nil? or request.body.size != length
79
+ response.start(400) { |head, out| out.write("Bad Request") }
80
+ return
81
+ end
82
+
83
+ info = client_info(request)
84
+
85
+ # All checks above passed through
86
+ response.start(200) do |head, out|
87
+ head["Content-Type"] = "text/xml; charset=utf-8"
88
+ begin
89
+ out.write(@xmlrpc_server.process(request.body, info))
90
+ rescue => detail
91
+ puts detail.backtrace
92
+ raise
93
+ end
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def client_info(request)
100
+ params = request.params
101
+ ip = params["HTTP_X_FORWARDED_FOR"] ? params["HTTP_X_FORWARDED_FOR"].split(',').last.strip : params["REMOTE_ADDR"]
102
+ # JJM #906 The following dn.match regular expression is forgiving
103
+ # enough to match the two Distinguished Name string contents
104
+ # coming from Apache, Pound or other reverse SSL proxies.
105
+ if dn = params[Puppet[:ssl_client_header]] and dn_matchdata = dn.match(/^.*?CN\s*=\s*(.*)/)
106
+ client = dn_matchdata[1].to_str
107
+ valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS')
108
+ else
109
+ begin
110
+ client = Resolv.getname(ip)
111
+ rescue => detail
112
+ Puppet.err "Could not resolve #{ip}: #{detail}"
113
+ client = "unknown"
114
+ end
115
+ valid = false
116
+ end
117
+
118
+ info = Puppet::Network::ClientRequest.new(client, ip, valid)
119
+
120
+ info
121
+ end
122
+
123
+ # Taken from XMLRPC::ParseContentType
124
+ def parse_content_type(str)
125
+ a, *b = str.split(";")
126
+ return a.strip, *b
127
+ end
128
+ end
129
+ end
@@ -4,9 +4,12 @@ Puppet::Type.type(:exec).provide :posix do
4
4
  confine :feature => :posix
5
5
  defaultfor :feature => :posix
6
6
 
7
- desc "Execute external binaries directly, on POSIX systems.
8
- This does not pass through a shell, or perform any interpolation, but
9
- only directly calls the command with the arguments given."
7
+ desc <<-EOT
8
+ Executes external binaries directly, without passing through a shell or
9
+ performing any interpolation. This is a safer and more predictable way
10
+ to execute most commands, but prevents the use of globbing and shell
11
+ built-ins (including control logic like "for" and "if" statements).
12
+ EOT
10
13
 
11
14
  def run(command, check = false)
12
15
  output = nil
@@ -3,8 +3,17 @@ Puppet::Type.type(:exec).provide :shell, :parent => :posix do
3
3
 
4
4
  confine :feature => :posix
5
5
 
6
- desc "Execute external binaries directly, on POSIX systems.
7
- passing through a shell so that shell built ins are available."
6
+ desc <<-EOT
7
+ Passes the provided command through `/bin/sh`; only available on
8
+ POSIX systems. This allows the use of shell globbing and built-ins, and
9
+ does not require that the path to a command be fully-qualified. Although
10
+ this can be more convenient than the `posix` provider, it also means that
11
+ you need to be more careful with escaping; as ever, with great power comes
12
+ etc. etc.
13
+
14
+ This provider closely resembles the behavior of the `exec` type
15
+ in Puppet 0.25.x.
16
+ EOT
8
17
 
9
18
  def run(command, check = false)
10
19
  command = %Q{/bin/sh -c "#{command.gsub(/"/,'\"')}"}
@@ -98,7 +98,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
98
98
  resource.ref =~ /^(.+)\[/
99
99
  class_name = $1 || resource.class.name
100
100
 
101
- newref = [class_name, key]
101
+ newref = [class_name, key].flatten
102
102
 
103
103
  if key.is_a? String
104
104
  ref_string = "#{class_name}[#{key}]"
@@ -111,7 +111,10 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
111
111
  # isn't sufficient.
112
112
  if existing = @resource_table[newref]
113
113
  return if existing == resource
114
- raise(ArgumentError, "Cannot alias #{resource.ref} to #{key.inspect}; resource #{newref.inspect} already exists")
114
+ resource_definition = " at #{resource.file}:#{resource.line}" if resource.file and resource.line
115
+ existing_definition = " at #{existing.file}:#{existing.line}" if existing.file and existing.line
116
+ msg = "Cannot alias #{resource.ref} to #{key.inspect}#{resource_definition}; resource #{newref.inspect} already defined#{existing_definition}"
117
+ raise ArgumentError, msg
115
118
  end
116
119
  @resource_table[newref] = resource
117
120
  @aliases[resource.ref] ||= []
@@ -377,7 +380,7 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph
377
380
  res = Puppet::Resource.new(nil, type)
378
381
  end
379
382
  title_key = [res.type, res.title.to_s]
380
- uniqueness_key = [res.type, res.uniqueness_key]
383
+ uniqueness_key = [res.type, res.uniqueness_key].flatten
381
384
  @resource_table[title_key] || @resource_table[uniqueness_key]
382
385
  end
383
386
 
@@ -151,6 +151,8 @@ class Puppet::SSL::Host
151
151
  # ...add our configured dns_alt_names
152
152
  if Puppet[:dns_alt_names] and Puppet[:dns_alt_names] != ''
153
153
  options[:dns_alt_names] ||= Puppet[:dns_alt_names]
154
+ elsif Puppet::SSL::CertificateAuthority.ca? and fqdn = Facter.value(:fqdn) and domain = Facter.value(:domain)
155
+ options[:dns_alt_names] = "puppet, #{fqdn}, puppet.#{domain}"
154
156
  end
155
157
  end
156
158
 
@@ -3,11 +3,12 @@ require 'facter'
3
3
  require 'puppet/util/filetype'
4
4
 
5
5
  Puppet::Type.newtype(:cron) do
6
- @doc = "Installs and manages cron jobs. All fields except the command
7
- and the user are optional, although specifying no periodic
8
- fields would result in the command being executed every
9
- minute. While the name of the cron job is not part of the actual
10
- job, it is used by Puppet to store and retrieve it.
6
+ @doc = <<-EOT
7
+ Installs and manages cron jobs. Every cron resource requires a command
8
+ and user attribute, as well as at least one periodic attribute (hour,
9
+ minute, month, monthday, weekday, or special). While the name of the cron
10
+ job is not part of the actual job, it is used by Puppet to store and
11
+ retrieve it.
11
12
 
12
13
  If you specify a cron job that matches an existing job in every way
13
14
  except name, then the jobs will be considered equivalent and the
@@ -18,30 +19,30 @@ Puppet::Type.newtype(:cron) do
18
19
  Example:
19
20
 
20
21
  cron { logrotate:
21
- command => \"/usr/sbin/logrotate\",
22
+ command => "/usr/sbin/logrotate",
22
23
  user => root,
23
24
  hour => 2,
24
25
  minute => 0
25
26
  }
26
27
 
27
- Note that all cron values can be specified as an array of values:
28
+ Note that all periodic attributes can be specified as an array of values:
28
29
 
29
30
  cron { logrotate:
30
- command => \"/usr/sbin/logrotate\",
31
+ command => "/usr/sbin/logrotate",
31
32
  user => root,
32
33
  hour => [2, 4]
33
34
  }
34
35
 
35
- Or using ranges, or the step syntax `*/2` (although there's no guarantee that
36
- your `cron` daemon supports it):
36
+ ...or using ranges or the step syntax `*/2` (although there's no guarantee
37
+ that your `cron` daemon supports these):
37
38
 
38
39
  cron { logrotate:
39
- command => \"/usr/sbin/logrotate\",
40
+ command => "/usr/sbin/logrotate",
40
41
  user => root,
41
42
  hour => ['2-4'],
42
43
  minute => '*/10'
43
44
  }
44
- "
45
+ EOT
45
46
  ensurable
46
47
 
47
48
  # A base class for all of the Cron parameters, since they all have
@@ -309,8 +309,8 @@ Puppet::Type.newtype(:file) do
309
309
  super(path.gsub(/\/+/, '/').sub(/\/$/, ''))
310
310
  end
311
311
 
312
- def self.instances(base = '/')
313
- return self.new(:name => base, :recurse => true, :recurselimit => 1, :audit => :all).recurse_local.values
312
+ def self.instances
313
+ return []
314
314
  end
315
315
 
316
316
  @depthfirst = false