puppet 2.6.7 → 2.6.8

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 (80) hide show
  1. data/CHANGELOG +49 -0
  2. data/install.rb +6 -2
  3. data/lib/puppet.rb +1 -1
  4. data/lib/puppet/application.rb +16 -8
  5. data/lib/puppet/application/agent.rb +2 -0
  6. data/lib/puppet/application/apply.rb +3 -0
  7. data/lib/puppet/application/master.rb +1 -1
  8. data/lib/puppet/configurer.rb +10 -1
  9. data/lib/puppet/defaults.rb +9 -0
  10. data/lib/puppet/file_serving/fileset.rb +1 -0
  11. data/lib/puppet/indirector/exec.rb +1 -2
  12. data/lib/puppet/indirector/report/yaml.rb +11 -0
  13. data/lib/puppet/node/environment.rb +1 -1
  14. data/lib/puppet/parameter.rb +2 -0
  15. data/lib/puppet/parameter/path.rb +42 -0
  16. data/lib/puppet/parser/compiler.rb +1 -1
  17. data/lib/puppet/parser/lexer.rb +3 -2
  18. data/lib/puppet/parser/parser_support.rb +0 -1
  19. data/lib/puppet/provider/exec/posix.rb +112 -0
  20. data/lib/puppet/provider/exec/shell.rb +17 -0
  21. data/lib/puppet/provider/group/groupadd.rb +3 -0
  22. data/lib/puppet/provider/nameservice/#directoryservice.rb# +519 -0
  23. data/lib/puppet/provider/package/gem.rb +2 -2
  24. data/lib/puppet/provider/package/macports.rb +106 -0
  25. data/lib/puppet/provider/service/debian.rb +6 -2
  26. data/lib/puppet/rails/inventory_node.rb +5 -0
  27. data/lib/puppet/reference/#providers.rb# +123 -0
  28. data/lib/puppet/resource/type_collection.rb +6 -1
  29. data/lib/puppet/simple_graph.rb +1 -1
  30. data/lib/puppet/transaction.rb +1 -1
  31. data/lib/puppet/transaction/report.rb +28 -10
  32. data/lib/puppet/type/cron.rb +3 -1
  33. data/lib/puppet/type/exec.rb +30 -167
  34. data/lib/puppet/type/file.rb +12 -1
  35. data/lib/puppet/type/file/source.rb +1 -0
  36. data/lib/puppet/type/group.rb +11 -1
  37. data/lib/puppet/type/service.rb +19 -11
  38. data/lib/puppet/util/command_line.rb +15 -12
  39. data/lib/puppet/util/command_line/puppetrun +0 -1
  40. data/lib/puppet/util/loadedfile.rb +1 -5
  41. data/lib/puppet/util/metric.rb +3 -5
  42. data/lib/puppet/util/plugins.rb +82 -0
  43. data/spec/integration/configurer_spec.rb +38 -5
  44. data/spec/integration/transaction_spec.rb +43 -42
  45. data/spec/lib/puppet_spec/verbose.rb +9 -0
  46. data/spec/shared_behaviours/path_parameters.rb +185 -0
  47. data/spec/spec_helper.rb +6 -0
  48. data/spec/unit/application/agent_spec.rb +7 -0
  49. data/spec/unit/application/apply_spec.rb +6 -0
  50. data/spec/unit/application/master_spec.rb +2 -2
  51. data/spec/unit/configurer_spec.rb +48 -0
  52. data/spec/unit/file_serving/fileset_spec.rb +8 -0
  53. data/spec/unit/indirector/certificate_status/#file_spec.rb# +188 -0
  54. data/spec/unit/indirector/exec_spec.rb +2 -3
  55. data/spec/unit/indirector/facts/inventory_active_record_spec.rb +5 -1
  56. data/spec/unit/indirector/report/yaml_spec.rb +38 -0
  57. data/spec/unit/node/environment_spec.rb +15 -14
  58. data/spec/unit/parameter/path_spec.rb +24 -0
  59. data/spec/unit/parser/compiler_spec.rb +1 -2
  60. data/spec/unit/parser/lexer_spec.rb +12 -0
  61. data/spec/unit/provider/exec/posix_spec.rb +120 -0
  62. data/spec/unit/provider/exec/shell_spec.rb +50 -0
  63. data/spec/unit/provider/group/groupadd_spec.rb +11 -1
  64. data/spec/unit/provider/package/gem_spec.rb +11 -1
  65. data/spec/unit/provider/package/macports_spec.rb +122 -0
  66. data/spec/unit/provider/service/debian_spec.rb +14 -2
  67. data/spec/unit/resource/#type_collection_spec.rb# +463 -0
  68. data/spec/unit/resource/type_collection_spec.rb +21 -17
  69. data/spec/unit/transaction/report_spec.rb +13 -2
  70. data/spec/unit/type/cron_spec.rb +466 -18
  71. data/spec/unit/type/exec_spec.rb +633 -106
  72. data/spec/unit/type/file/source_spec.rb +1 -0
  73. data/spec/unit/type/group_spec.rb +8 -1
  74. data/spec/unit/type_spec.rb +1 -1
  75. data/spec/unit/util/loadedfile_spec.rb +7 -0
  76. data/spec/unit/util/rdoc/parser_spec.rb +2 -1
  77. data/tasks/rake/git_workflow.rake +3 -1
  78. data/test/ral/type/exec.rb +87 -176
  79. metadata +21 -5
  80. data/lib/puppet/provider/package/darwinport.rb +0 -86
@@ -22,7 +22,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d
22
22
  end
23
23
 
24
24
  if name = hash[:justme]
25
- command << name
25
+ command << name + "$"
26
26
  end
27
27
 
28
28
  begin
@@ -94,7 +94,7 @@ Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package d
94
94
  command << "--source" << "#{source}" << resource[:name]
95
95
  end
96
96
  else
97
- command << resource[:name]
97
+ command << "--no-rdoc" << "--no-ri" << resource[:name]
98
98
  end
99
99
 
100
100
  output = execute(command)
@@ -0,0 +1,106 @@
1
+ require 'puppet/provider/package'
2
+
3
+ Puppet::Type.type(:package).provide :macports, :parent => Puppet::Provider::Package do
4
+ desc "Package management using MacPorts on OS X.
5
+
6
+ Supports MacPorts versions and revisions, but not variants.
7
+ Variant preferences may be specified using the MacPorts variants.conf file
8
+ http://guide.macports.org/chunked/internals.configuration-files.html#internals.configuration-files.variants-conf
9
+
10
+ When specifying a version in the Puppet DSL, only specify the version, not the revision
11
+ Revisions are only used internally for ensuring the latest version/revision of a port.
12
+ "
13
+
14
+ confine :operatingsystem => :darwin
15
+ commands :port => "/opt/local/bin/port"
16
+
17
+ has_feature :installable
18
+ has_feature :uninstallable
19
+ has_feature :upgradeable
20
+ has_feature :versionable
21
+
22
+
23
+ def self.parse_installed_query_line(line)
24
+ regex = /(\S+)\s+@(\S+)_(\S+)\s+\(active\)/
25
+ fields = [:name, :ensure, :revision]
26
+ hash_from_line(line, regex, fields)
27
+ end
28
+
29
+ def self.parse_info_query_line(line)
30
+ regex = /(\S+)\s+(\S+)/
31
+ fields = [:version, :revision]
32
+ hash_from_line(line, regex, fields)
33
+ end
34
+
35
+ def self.hash_from_line(line, regex, fields)
36
+ hash = {}
37
+ if match = regex.match(line)
38
+ fields.zip(match.captures) { |field, value|
39
+ hash[field] = value
40
+ }
41
+ hash[:provider] = self.name
42
+ return hash
43
+ end
44
+ nil
45
+ end
46
+
47
+ def self.instances
48
+ packages = []
49
+ port("-q", :installed).each do |line|
50
+ if hash = parse_installed_query_line(line)
51
+ packages << new(hash)
52
+ end
53
+ end
54
+ packages
55
+ end
56
+
57
+ def install
58
+ should = @resource.should(:ensure)
59
+ if [:latest, :installed, :present].include?(should)
60
+ output = port("-q", :install, @resource[:name])
61
+ else
62
+ output = port("-q", :install, @resource[:name], "@#{should}")
63
+ end
64
+ # MacPorts now correctly exits non-zero with appropriate errors in
65
+ # situations where a port cannot be found or installed.
66
+ end
67
+
68
+ def query
69
+ return self.class.parse_installed_query_line(port("-q", :installed, @resource[:name]))
70
+ end
71
+
72
+ def latest
73
+ # We need both the version and the revision to be confident
74
+ # we've got the latest revision of a specific version
75
+ # Note we're still not doing anything with variants here.
76
+ info_line = port("-q", :info, "--line", "--version", "--revision", @resource[:name])
77
+ return nil if info_line == ""
78
+
79
+ if newest = self.class.parse_info_query_line(info_line)
80
+ current = query
81
+ # We're doing some fiddling behind the scenes here to cope with updated revisions.
82
+ # If we're already at the latest version/revision, then just return the version
83
+ # so the current and desired values match. Otherwise return version and revision
84
+ # to trigger an upgrade to the latest revision.
85
+ if newest[:version] == current[:ensure] and newest[:revision] == current[:revision]
86
+ return current[:ensure]
87
+ else
88
+ return "#{newest[:version]}_#{newest[:revision]}"
89
+ end
90
+ end
91
+ nil
92
+ end
93
+
94
+ def uninstall
95
+ port("-q", :uninstall, @resource[:name])
96
+ end
97
+
98
+ def update
99
+ if query[:name] == @resource[:name] # 'port upgrade' cannot install new ports
100
+ port("-q", :upgrade, @resource[:name])
101
+ else
102
+ install
103
+ end
104
+ end
105
+ end
106
+
@@ -22,8 +22,12 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do
22
22
 
23
23
  # Remove the symlinks
24
24
  def disable
25
- update_rc "-f", @resource[:name], "remove"
26
- update_rc @resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", "."
25
+ if `dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?`.to_i == 0
26
+ update_rc @resource[:name], "disable"
27
+ else
28
+ update_rc "-f", @resource[:name], "remove"
29
+ update_rc @resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", "."
30
+ end
27
31
  end
28
32
 
29
33
  def enabled?
@@ -3,6 +3,11 @@ require 'puppet/rails/inventory_fact'
3
3
  class Puppet::Rails::InventoryNode < ::ActiveRecord::Base
4
4
  has_many :facts, :class_name => "Puppet::Rails::InventoryFact", :foreign_key => :node_id, :dependent => :delete_all
5
5
 
6
+ if Puppet::Util.activerecord_version >= 3.0
7
+ # Prevents "DEPRECATION WARNING: Base.named_scope has been deprecated, please use Base.scope instead"
8
+ ActiveRecord::NamedScope::ClassMethods.module_eval { alias :named_scope :scope }
9
+ end
10
+
6
11
  named_scope :has_fact_with_value, lambda { |name,value|
7
12
  {
8
13
  :conditions => ["inventory_facts.name = ? AND inventory_facts.value = ?", name, value],
@@ -0,0 +1,123 @@
1
+ # This doesn't get stored in trac, since it changes every time.
2
+ providers = Puppet::Util::Reference.newreference :providers, :title => "Provider Suitability Report", :depth => 1, :dynamic => true, :doc => "Which providers are valid for this machine" do
3
+ types = []
4
+ newPuppet::Type.loadall
5
+ Puppet::Type.eachtype do |klass|
6
+ next unless klass.providers.length > 0
7
+ types << klass
8
+ end
9
+ types.sort! { |a,b| a.name.to_s <=> b.name.to_s }
10
+
11
+ command_line = Puppet::Util::CommandLine.new
12
+ types.reject! { |type| ! command_line.args.include?(type.name.to_s) } unless command_line.args.empty?
13
+
14
+ ret = "Details about this host:\n\n"
15
+
16
+ # Throw some facts in there, so we know where the report is from.
17
+ ["Ruby Version", "Puppet Version", "Operating System", "Operating System Release"].each do |label|
18
+ name = label.gsub(/\s+/, '')
19
+ value = Facter.value(name)
20
+ ret += option(label, value)
21
+ end
22
+ ret += "\n"
23
+
24
+ count = 1
25
+
26
+ # Produce output for each type.
27
+ types.each do |type|
28
+ features = type.features
29
+ ret += "\n" # add a trailing newline
30
+
31
+ # Now build up a table of provider suitability.
32
+ headers = %w{Provider Suitable?} + features.collect { |f| f.to_s }.sort
33
+
34
+ table_data = {}
35
+
36
+ functional = false
37
+ notes = []
38
+ begin
39
+ default = type.defaultprovider.name
40
+ rescue Puppet::DevError
41
+ default = "none"
42
+ end
43
+ type.providers.sort { |a,b| a.to_s <=> b.to_s }.each do |pname|
44
+ data = []
45
+ table_data[pname] = data
46
+ provider = type.provider(pname)
47
+
48
+ # Add the suitability note
49
+ if missing = provider.suitable?(false) and missing.empty?
50
+ data << "*X*"
51
+ suit = true
52
+ functional = true
53
+ else
54
+ data << "[#{count}]_" # A pointer to the appropriate footnote
55
+ suit = false
56
+ end
57
+
58
+ # Add a footnote with the details about why this provider is unsuitable, if that's the case
59
+ unless suit
60
+ details = ".. [#{count}]\n"
61
+ missing.each do |test, values|
62
+ case test
63
+ when :exists
64
+ details += " - Missing files #{values.join(", ")}\n"
65
+ when :variable
66
+ values.each do |name, facts|
67
+ if Puppet.settings.valid?(name)
68
+ details += " - Setting #{name} (currently #{Puppet.settings.value(name).inspect}) not in list #{facts.join(", ")}\n"
69
+ else
70
+ details += " - Fact #{name} (currently #{Facter.value(name).inspect}) not in list #{facts.join(", ")}\n"
71
+ end
72
+ end
73
+ when :true
74
+ details += " - Got #{values} true tests that should have been false\n"
75
+ when :false
76
+ details += " - Got #{values} false tests that should have been true\n"
77
+ when :feature
78
+ details += " - Missing features #{values.collect { |f| f.to_s }.join(",")}\n"
79
+ end
80
+ end
81
+ notes << details
82
+
83
+ count += 1
84
+ end
85
+
86
+ # Add a note for every feature
87
+ features.each do |feature|
88
+ if provider.features.include?(feature)
89
+ data << "*X*"
90
+ else
91
+ data << ""
92
+ end
93
+ end
94
+ end
95
+
96
+ ret += h(type.name.to_s + "_", 2)
97
+
98
+ ret += "[#{type.name}](#{"http://docs.puppetlabs.com/references/stable/type.html##{type.name}"})\n\n"
99
+ ret += option("Default provider", default)
100
+ ret += doctable(headers, table_data)
101
+
102
+ notes.each do |note|
103
+ ret += note + "\n"
104
+ end
105
+
106
+ ret += "\n"
107
+ end
108
+
109
+ ret += "\n"
110
+
111
+ ret
112
+ end
113
+ providers.header = "
114
+ Puppet resource types are usually backed by multiple implementations called `providers`,
115
+ which handle variance between platforms and tools.
116
+
117
+ Different providers are suitable or unsuitable on different platforms based on things
118
+ like the presence of a given tool.
119
+
120
+ Here are all of the provider-backed types and their different providers. Any unmentioned
121
+ types do not use providers yet.
122
+
123
+ "
@@ -162,17 +162,22 @@ class Puppet::Resource::TypeCollection
162
162
  parser.string = code
163
163
  else
164
164
  file = Puppet.settings.value(:manifest, environment.to_s)
165
- return unless File.exist?(file)
166
165
  parser.file = file
167
166
  end
168
167
  parser.parse
169
168
  rescue => detail
169
+ @parse_failed = true
170
+
170
171
  msg = "Could not parse for environment #{environment}: #{detail}"
171
172
  error = Puppet::Error.new(msg)
172
173
  error.set_backtrace(detail.backtrace)
173
174
  raise error
174
175
  end
175
176
 
177
+ def require_reparse?
178
+ @parse_failed || stale?
179
+ end
180
+
176
181
  def stale?
177
182
  @watched_files.values.detect { |file| file.changed? }
178
183
  end
@@ -329,7 +329,7 @@ class Puppet::SimpleGraph
329
329
  children = other.adjacent(container, :direction => :out)
330
330
 
331
331
  # MQR TODO: Luke suggests that it should be possible to refactor the system so that
332
- # container nodes are retained, thus obviating the need for the whit.
332
+ # container nodes are retained, thus obviating the need for the whit.
333
333
  children = [whit_class.new(:name => container.name, :catalog => other)] if children.empty?
334
334
 
335
335
  # First create new edges for each of the :in edges
@@ -47,7 +47,7 @@ class Puppet::Transaction
47
47
  def apply(resource, ancestor = nil)
48
48
  status = resource_harness.evaluate(resource)
49
49
  add_resource_status(status)
50
- event_manager.queue_events(ancestor || resource, status.events)
50
+ event_manager.queue_events(ancestor || resource, status.events) unless status.failed?
51
51
  rescue => detail
52
52
  resource.err "Could not evaluate: #{detail}"
53
53
  end
@@ -80,30 +80,49 @@ class Puppet::Transaction::Report
80
80
  host
81
81
  end
82
82
 
83
- # Provide a summary of this report.
83
+ # Provide a human readable textual summary of this report.
84
84
  def summary
85
+ report = raw_summary
86
+
85
87
  ret = ""
88
+ report.keys.sort { |a,b| a.to_s <=> b.to_s }.each do |key|
89
+ ret += "#{Puppet::Util::Metric.labelize(key)}:\n"
86
90
 
87
- @metrics.sort { |a,b| a[1].label <=> b[1].label }.each do |name, metric|
88
- ret += "#{metric.label}:\n"
89
- metric.values.sort { |a,b|
91
+ report[key].keys.sort { |a,b|
90
92
  # sort by label
91
- if a[0] == :total
93
+ if a == :total
92
94
  1
93
- elsif b[0] == :total
95
+ elsif b == :total
94
96
  -1
95
97
  else
96
- a[1] <=> b[1]
98
+ report[key][a].to_s <=> report[key][b].to_s
97
99
  end
98
- }.each do |name, label, value|
100
+ }.each do |label|
101
+ value = report[key][label]
99
102
  next if value == 0
100
103
  value = "%0.2f" % value if value.is_a?(Float)
101
- ret += " %15s %s\n" % [label + ":", value]
104
+ ret += " %15s %s\n" % [Puppet::Util::Metric.labelize(label) + ":", value]
102
105
  end
103
106
  end
104
107
  ret
105
108
  end
106
109
 
110
+ # Provide a raw hash summary of this report.
111
+ def raw_summary
112
+ report = {}
113
+
114
+ @metrics.each do |name, metric|
115
+ key = metric.name.to_s
116
+ report[key] = {}
117
+ metric.values.each do |name, label, value|
118
+ report[key][name.to_s] = value
119
+ end
120
+ report[key]["total"] = 0 unless key == "time" or report[key].include?("total")
121
+ end
122
+ (report["time"] ||= {})["last_run"] = Time.now.tv_sec
123
+ report
124
+ end
125
+
107
126
  # Based on the contents of this report's metrics, compute a single number
108
127
  # that represents the report. The resulting number is a bitmask where
109
128
  # individual bits represent the presence of different metrics.
@@ -142,7 +161,6 @@ class Puppet::Transaction::Report
142
161
  metrics["total"] = resource_statuses.length
143
162
 
144
163
  resource_statuses.each do |name, status|
145
-
146
164
  Puppet::Resource::Status::STATES.each do |state|
147
165
  metrics[state.to_s] += 1 if status.send(state)
148
166
  end
@@ -226,7 +226,9 @@ Puppet::Type.newtype(:cron) do
226
226
  end
227
227
 
228
228
  newproperty(:special) do
229
- desc "Special schedules"
229
+ desc "A special value such as 'reboot' or 'annually'.
230
+ Only available on supported systems such as Vixie Cron.
231
+ Overrides more specific time of day/week settings."
230
232
 
231
233
  def specials
232
234
  %w{reboot yearly annually monthly weekly daily midnight hourly}
@@ -23,17 +23,15 @@ module Puppet
23
23
  you are doing a lot of work with `exec`, please at least notify
24
24
  us at Puppet Labs what you are doing, and hopefully we can work with
25
25
  you to get a native resource type for the work you are doing.
26
-
27
- **Autorequires:** If Puppet is managing an exec's cwd or the executable file used in an exec's command, the exec resource will autorequire those files. If Puppet is managing the user that an exec should run as, the exec resource will autorequire that user."
28
26
 
29
- require 'open3'
27
+ **Autorequires:** If Puppet is managing an exec's cwd or the executable file used in an exec's command, the exec resource will autorequire those files. If Puppet is managing the user that an exec should run as, the exec resource will autorequire that user."
30
28
 
31
29
  # Create a new check mechanism. It's basically just a parameter that
32
30
  # provides one extra 'check' method.
33
- def self.newcheck(name, &block)
31
+ def self.newcheck(name, options = {}, &block)
34
32
  @checks ||= {}
35
33
 
36
- check = newparam(name, &block)
34
+ check = newparam(name, options, &block)
37
35
  @checks[name] = check
38
36
  end
39
37
 
@@ -65,9 +63,11 @@ module Puppet
65
63
 
66
64
  # First verify that all of our checks pass.
67
65
  def retrieve
68
- # Default to somethinng
69
-
70
- if @resource.check
66
+ # We need to return :notrun to trigger evaluation; when that isn't
67
+ # true, we *LIE* about what happened and return a "success" for the
68
+ # value, which causes us to be treated as in_sync?, which means we
69
+ # don't actually execute anything. I think. --daniel 2011-03-10
70
+ if @resource.check_all_attributes
71
71
  return :notrun
72
72
  else
73
73
  return self.should
@@ -89,7 +89,7 @@ module Puppet
89
89
  tries.times do |try|
90
90
  # Only add debug messages for tries > 1 to reduce log spam.
91
91
  debug("Exec try #{try+1}/#{tries}") if tries > 1
92
- @output, @status = @resource.run(self.resource[:command])
92
+ @output, @status = provider.run(self.resource[:command])
93
93
  break if self.should.include?(@status.exitstatus.to_s)
94
94
  if try_sleep > 0 and tries > 1
95
95
  debug("Sleeping for #{try_sleep} seconds between tries")
@@ -139,7 +139,7 @@ module Puppet
139
139
  newparam(:path) do
140
140
  desc "The search path used for command execution.
141
141
  Commands must be fully qualified if no path is specified. Paths
142
- can be specified as an array or as a colon-separated list."
142
+ can be specified as an array or as a colon separated list."
143
143
 
144
144
  # Support both arrays and colon-separated fields.
145
145
  def value=(*values)
@@ -176,21 +176,9 @@ module Puppet
176
176
  # Validation is handled by the SUIDManager class.
177
177
  end
178
178
 
179
- newparam(:cwd) do
179
+ newparam(:cwd, :parent => Puppet::Parameter::Path) do
180
180
  desc "The directory from which to run the command. If
181
181
  this directory does not exist, the command will fail."
182
-
183
- validate do |dir|
184
- unless dir =~ /^#{File::SEPARATOR}/
185
- self.fail("CWD must be a fully qualified path")
186
- end
187
- end
188
-
189
- munge do |dir|
190
- dir = dir[0] if dir.is_a?(Array)
191
-
192
- dir
193
- end
194
182
  end
195
183
 
196
184
  newparam(:logoutput) do
@@ -209,7 +197,7 @@ module Puppet
209
197
  for refreshing."
210
198
 
211
199
  validate do |command|
212
- @resource.validatecmd(command)
200
+ provider.validatecmd(command)
213
201
  end
214
202
  end
215
203
 
@@ -241,19 +229,17 @@ module Puppet
241
229
  newparam(:timeout) do
242
230
  desc "The maximum time the command should take. If the command takes
243
231
  longer than the timeout, the command is considered to have failed
244
- and will be stopped. Use any negative number to disable the timeout.
232
+ and will be stopped. Use 0 to disable the timeout.
245
233
  The time is specified in seconds."
246
234
 
247
235
  munge do |value|
248
236
  value = value.shift if value.is_a?(Array)
249
- if value.is_a?(String)
250
- unless value =~ /^[-\d.]+$/
251
- raise ArgumentError, "The timeout must be a number."
252
- end
253
- Float(value)
254
- else
255
- value
237
+ begin
238
+ value = Float(value)
239
+ rescue ArgumentError => e
240
+ raise ArgumentError, "The timeout must be a number."
256
241
  end
242
+ [value, 0.0].max
257
243
  end
258
244
 
259
245
  defaultto 300
@@ -333,7 +319,7 @@ module Puppet
333
319
  end
334
320
  end
335
321
 
336
- newcheck(:creates) do
322
+ newcheck(:creates, :parent => Puppet::Parameter::Path) do
337
323
  desc "A file that this command creates. If this
338
324
  parameter is provided, then the command will only be run
339
325
  if the specified file does not exist:
@@ -346,19 +332,7 @@ module Puppet
346
332
 
347
333
  "
348
334
 
349
- # FIXME if they try to set this and fail, then we should probably
350
- # fail the entire exec, right?
351
- validate do |files|
352
- files = [files] unless files.is_a? Array
353
-
354
- files.each do |file|
355
- self.fail("'creates' must be set to a fully qualified path") unless file
356
-
357
- unless file =~ %r{^#{File::SEPARATOR}}
358
- self.fail "'creates' files must be fully qualified."
359
- end
360
- end
361
- end
335
+ accept_arrays
362
336
 
363
337
  # If the file exists, return false (i.e., don't run the command),
364
338
  # else return true
@@ -386,15 +360,15 @@ module Puppet
386
360
  validate do |cmds|
387
361
  cmds = [cmds] unless cmds.is_a? Array
388
362
 
389
- cmds.each do |cmd|
390
- @resource.validatecmd(cmd)
363
+ cmds.each do |command|
364
+ provider.validatecmd(command)
391
365
  end
392
366
  end
393
367
 
394
368
  # Return true if the command does not return 0.
395
369
  def check(value)
396
370
  begin
397
- output, status = @resource.run(value, true)
371
+ output, status = provider.run(value, true)
398
372
  rescue Timeout::Error
399
373
  err "Check #{value.inspect} exceeded timeout"
400
374
  return false
@@ -428,15 +402,15 @@ module Puppet
428
402
  validate do |cmds|
429
403
  cmds = [cmds] unless cmds.is_a? Array
430
404
 
431
- cmds.each do |cmd|
432
- @resource.validatecmd(cmd)
405
+ cmds.each do |command|
406
+ provider.validatecmd(command)
433
407
  end
434
408
  end
435
409
 
436
410
  # Return true if the command returns 0.
437
411
  def check(value)
438
412
  begin
439
- output, status = @resource.run(value, true)
413
+ output, status = provider.run(value, true)
440
414
  rescue Timeout::Error
441
415
  err "Check #{value.inspect} exceeded timeout"
442
416
  return false
@@ -450,7 +424,7 @@ module Puppet
450
424
  @isomorphic = false
451
425
 
452
426
  validate do
453
- validatecmd(self[:command])
427
+ provider.validatecmd(self[:command])
454
428
  end
455
429
 
456
430
  # FIXME exec should autorequire any exec that 'creates' our cwd
@@ -503,7 +477,7 @@ module Puppet
503
477
  # Verify that we pass all of the checks. The argument determines whether
504
478
  # we skip the :refreshonly check, which is necessary because we now check
505
479
  # within refresh
506
- def check(refreshing = false)
480
+ def check_all_attributes(refreshing = false)
507
481
  self.class.checks.each { |check|
508
482
  next if refreshing and check == :refreshonly
509
483
  if @parameters.include?(check)
@@ -518,32 +492,6 @@ module Puppet
518
492
  true
519
493
  end
520
494
 
521
- # Verify that we have the executable
522
- def checkexe(cmd)
523
- exe = extractexe(cmd)
524
-
525
- if self[:path]
526
- if Puppet.features.posix? and !File.exists?(exe)
527
- withenv :PATH => self[:path].join(File::PATH_SEPARATOR) do
528
- exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'")
529
- end
530
- elsif Puppet.features.microsoft_windows? and !File.exists?(exe)
531
- self[:path].each do |path|
532
- [".exe", ".ps1", ".bat", ".com", ""].each do |extension|
533
- file = File.join(path, exe+extension)
534
- return if File.exists?(file)
535
- end
536
- end
537
- end
538
- end
539
-
540
- raise ArgumentError, "Could not find executable '#{exe}'" unless FileTest.exists?(exe)
541
- unless FileTest.executable?(exe)
542
- raise ArgumentError,
543
- "'#{exe}' is not executable"
544
- end
545
- end
546
-
547
495
  def output
548
496
  if self.property(:returns).nil?
549
497
  return nil
@@ -554,98 +502,13 @@ module Puppet
554
502
 
555
503
  # Run the command, or optionally run a separately-specified command.
556
504
  def refresh
557
- if self.check(true)
505
+ if self.check_all_attributes(true)
558
506
  if cmd = self[:refresh]
559
- self.run(cmd)
507
+ provider.run(cmd)
560
508
  else
561
509
  self.property(:returns).sync
562
510
  end
563
511
  end
564
512
  end
565
-
566
- # Run a command.
567
- def run(command, check = false)
568
- output = nil
569
- status = nil
570
-
571
- dir = nil
572
-
573
- checkexe(command)
574
-
575
- if dir = self[:cwd]
576
- unless File.directory?(dir)
577
- if check
578
- dir = nil
579
- else
580
- self.fail "Working directory '#{dir}' does not exist"
581
- end
582
- end
583
- end
584
-
585
- dir ||= Dir.pwd
586
-
587
- if check
588
- debug "Executing check '#{command}'"
589
- else
590
- debug "Executing '#{command}'"
591
- end
592
- begin
593
- # Do our chdir
594
- Dir.chdir(dir) do
595
- environment = {}
596
-
597
- environment[:PATH] = self[:path].join(":") if self[:path]
598
-
599
- if envlist = self[:environment]
600
- envlist = [envlist] unless envlist.is_a? Array
601
- envlist.each do |setting|
602
- if setting =~ /^(\w+)=((.|\n)+)$/
603
- name = $1
604
- value = $2
605
- if environment.include? name
606
- warning(
607
- "Overriding environment setting '#{name}' with '#{value}'"
608
- )
609
- end
610
- environment[name] = value
611
- else
612
- warning "Cannot understand environment setting #{setting.inspect}"
613
- end
614
- end
615
- end
616
-
617
- withenv environment do
618
- Timeout::timeout(self[:timeout]) do
619
- output, status = Puppet::Util::SUIDManager.run_and_capture(
620
- [command], self[:user], self[:group]
621
- )
622
- end
623
- # The shell returns 127 if the command is missing.
624
- if status.exitstatus == 127
625
- raise ArgumentError, output
626
- end
627
- end
628
- end
629
- rescue Errno::ENOENT => detail
630
- self.fail detail.to_s
631
- end
632
-
633
- return output, status
634
- end
635
-
636
- def validatecmd(cmd)
637
- exe = extractexe(cmd)
638
- # if we're not fully qualified, require a path
639
- 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?
640
- end
641
-
642
- def extractexe(cmd)
643
- # easy case: command was quoted
644
- if cmd =~ /^"([^"]+)"/
645
- $1
646
- else
647
- cmd.split(/ /)[0]
648
- end
649
- end
650
513
  end
651
514
  end