puppet 0.13.1 → 0.13.2

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 (44) hide show
  1. data/CHANGELOG +4 -0
  2. data/Rakefile +2 -2
  3. data/bin/puppet +29 -0
  4. data/bin/puppetd +35 -1
  5. data/conf/redhat/puppet.spec +1 -1
  6. data/lib/puppet.rb +1 -1
  7. data/lib/puppet/client/master.rb +34 -3
  8. data/lib/puppet/filetype.rb +6 -2
  9. data/lib/puppet/networkclient.rb +4 -1
  10. data/lib/puppet/parameter.rb +35 -38
  11. data/lib/puppet/parser/ast/node.rb +1 -0
  12. data/lib/puppet/parser/interpreter.rb +129 -2
  13. data/lib/puppet/parser/scope.rb +44 -6
  14. data/lib/puppet/type.rb +47 -37
  15. data/lib/puppet/type/cron.rb +25 -10
  16. data/lib/puppet/type/exec.rb +165 -71
  17. data/lib/puppet/type/nameservice.rb +2 -17
  18. data/lib/puppet/type/package.rb +31 -7
  19. data/lib/puppet/type/package/sun.rb +11 -6
  20. data/lib/puppet/type/parsedtype.rb +94 -60
  21. data/lib/puppet/type/parsedtype/host.rb +5 -12
  22. data/lib/puppet/type/parsedtype/port.rb +53 -32
  23. data/lib/puppet/type/parsedtype/sshkey.rb +8 -4
  24. data/lib/puppet/type/pfile.rb +6 -4
  25. data/lib/puppet/type/pfile/ensure.rb +1 -6
  26. data/lib/puppet/type/state.rb +34 -74
  27. data/lib/puppet/type/symlink.rb +30 -19
  28. data/lib/puppet/type/user.rb +63 -11
  29. data/lib/puppet/util.rb +54 -60
  30. data/test/client/master.rb +72 -0
  31. data/test/language/interpreter.rb +94 -0
  32. data/test/other/log.rb +8 -1
  33. data/test/puppet/utiltest.rb +101 -1
  34. data/test/test +12 -5
  35. data/test/types/cron.rb +21 -1
  36. data/test/types/exec.rb +46 -2
  37. data/test/types/group.rb +15 -3
  38. data/test/types/host.rb +43 -4
  39. data/test/types/port.rb +67 -6
  40. data/test/types/sshkey.rb +45 -4
  41. data/test/types/symlink.rb +4 -4
  42. data/test/types/type.rb +41 -3
  43. data/test/types/user.rb +23 -2
  44. metadata +3 -2
@@ -169,9 +169,16 @@ module Puppet
169
169
  # Evaluate a specific node's code. This method will normally be called
170
170
  # on the top-level scope, but it actually evaluates the node at the
171
171
  # appropriate scope.
172
- def evalnode(names, facts)
173
- scope = code = nil
172
+ def evalnode(names, facts, classes = nil, parent = nil)
173
+ # First make sure there aren't any other node scopes lying around
174
+ self.nodeclean
174
175
 
176
+ # If they've passed classes in, then just generate from there.
177
+ if classes
178
+ return self.gennode(names, facts, classes, parent)
179
+ end
180
+
181
+ scope = code = nil
175
182
  # Find a node that matches one of our names
176
183
  names.each { |node|
177
184
  if hash = @nodetable[node]
@@ -187,9 +194,6 @@ module Puppet
187
194
  names.join(" or ")
188
195
  end
189
196
 
190
- # First make sure there aren't any other node scopes lying around
191
- self.nodeclean
192
-
193
197
  # We need to do a little skullduggery here. We want a
194
198
  # temporary scope, because we don't want this scope to
195
199
  # show up permanently in the scope tree -- otherwise we could
@@ -221,6 +225,40 @@ module Puppet
221
225
  return objects
222
226
  end
223
227
 
228
+ # Pull in all of the appropriate classes and evaluate them. It'd
229
+ # be nice if this didn't know quite so much about how AST::Node
230
+ # operated internally.
231
+ def gennode(names, facts, classes, parent)
232
+ name = names.shift
233
+ arghash = {
234
+ :name => name,
235
+ :code => AST::ASTArray.new(:pin => "[]")
236
+ }
237
+
238
+ if parent
239
+ arghash[:parentclass] = parent
240
+ end
241
+
242
+ # Create the node
243
+ node = AST::Node.new(arghash)
244
+ node.keyword = "node"
245
+ node.name = name
246
+
247
+ # Now evaluate it, which evaluates the parent but doesn't really
248
+ # do anything else but does return the nodescope
249
+ scope = node.safeevaluate(self)
250
+
251
+ # And now evaluate each set klass within the nodescope.
252
+ classes.each { |klass|
253
+ if code = scope.lookuptype(klass)
254
+ Puppet.warning "evaluating %s" % klass
255
+ code.safeevaluate(scope, {}, klass, klass)
256
+ end
257
+ }
258
+
259
+ return scope.to_trans
260
+ end
261
+
224
262
  # Retrieve a specific node. This is used in ast.rb to find a
225
263
  # parent node and in findnode to retrieve and evaluate a node.
226
264
  def node(name)
@@ -773,4 +811,4 @@ module Puppet
773
811
  end
774
812
  end
775
813
 
776
- # $Id: scope.rb 898 2006-02-13 17:13:09Z luke $
814
+ # $Id: scope.rb 909 2006-02-14 06:21:04Z luke $
data/lib/puppet/type.rb CHANGED
@@ -452,6 +452,8 @@ class Type < Puppet::Element
452
452
 
453
453
  @@metaparamhash ||= {}
454
454
  @@metaparams.each { |p| @@metaparamhash[name] = p }
455
+
456
+ return param
455
457
  end
456
458
 
457
459
  def self.eachmetaparam
@@ -486,6 +488,8 @@ class Type < Puppet::Element
486
488
  if param.isnamevar?
487
489
  @namevar = param.name
488
490
  end
491
+
492
+ return param
489
493
  end
490
494
 
491
495
  # Create a new state.
@@ -896,13 +900,17 @@ class Type < Puppet::Element
896
900
  # in order resolve other questions, such as finding a package
897
901
  # in a list
898
902
  def managed?
899
- if defined? @managed
903
+ # Once an object is managed, it always stays managed; but an object
904
+ # that is listed as unmanaged might become managed later in the process,
905
+ # so we have to check that every time
906
+ if defined? @managed and @managed
900
907
  return @managed
901
908
  else
902
909
  @managed = false
903
910
  states.each { |state|
904
911
  if state.should and ! state.class.unmanaged
905
912
  @managed = true
913
+ break
906
914
  end
907
915
  }
908
916
  return @managed
@@ -1103,10 +1111,16 @@ class Type < Puppet::Element
1103
1111
 
1104
1112
  return retobj
1105
1113
  else
1114
+ # If only one of the objects is being managed, then merge them
1115
+ if retobj.managed?
1116
+ raise Puppet::Error, "%s '%s' is already being managed" %
1117
+ [self.name, name]
1118
+ else
1119
+ retobj.merge(hash)
1120
+ return retobj
1121
+ end
1106
1122
  # We will probably want to support merging of some kind in
1107
1123
  # the future, but for now, just throw an error.
1108
- raise Puppet::Error, "%s '%s' is already being managed" %
1109
- [self.name, name]
1110
1124
  #retobj.merge(hash)
1111
1125
 
1112
1126
  #return retobj
@@ -1337,6 +1351,13 @@ class Type < Puppet::Element
1337
1351
  if self.respond_to?(:validate)
1338
1352
  self.validate
1339
1353
  end
1354
+
1355
+ # Ensure defaults to present for managed objects, but not otherwise.
1356
+ # Because of this complication, we can't use normal defaulting mechanisms
1357
+ # if ! @states.include?(:ensure) and self.managed? and
1358
+ # self.class.validstate?(:ensure)
1359
+ # self[:ensure] = :present
1360
+ # end
1340
1361
  end
1341
1362
 
1342
1363
  # Figure out of there are any objects we can automatically add as
@@ -1354,12 +1375,19 @@ class Type < Puppet::Element
1354
1375
 
1355
1376
  # Collect the current prereqs
1356
1377
  list.each { |dep|
1357
- # Skip autorequires that we aren't managing
1358
- next unless obj = typeobj[dep]
1378
+ obj = nil
1379
+ # Support them passing objects directly, to save some effort.
1380
+ if dep.is_a? Puppet::Type
1381
+ type = dep.class.name
1382
+ obj = dep
1383
+ else
1384
+ # Skip autorequires that we aren't managing
1385
+ next unless obj = typeobj[dep]
1386
+ end
1359
1387
 
1360
1388
  # Skip autorequires that we already require
1361
1389
  next if self.requires?(obj)
1362
- self.info "Auto-requiring %s" % obj.name
1390
+ #self.info "Auto-requiring %s %s" % [obj.class.name, obj.name]
1363
1391
 
1364
1392
  self[:require] = [type, dep]
1365
1393
  }
@@ -1469,8 +1497,14 @@ class Type < Puppet::Element
1469
1497
  next if self.attrset?(type, klass.name)
1470
1498
 
1471
1499
  obj = self.newattr(type, klass)
1472
- #self.debug "defaulting %s to %s" % [obj.name, obj.default]
1473
- obj.value = obj.default
1500
+ value = obj.default
1501
+ unless value.nil?
1502
+ #self.debug "defaulting %s to %s" % [obj.name, obj.default]
1503
+ obj.value = value
1504
+ else
1505
+ #self.debug "No default for %s" % obj.name
1506
+ self.delete(obj.name)
1507
+ end
1474
1508
  }
1475
1509
 
1476
1510
  end
@@ -1493,7 +1527,7 @@ class Type < Puppet::Element
1493
1527
  value = [value]
1494
1528
  end
1495
1529
 
1496
- if oldvals = @states[param].shouldorig
1530
+ if @states.include?(param) and oldvals = @states[param].shouldorig
1497
1531
  unless oldvals.is_a?(Array)
1498
1532
  oldvals = [oldvals]
1499
1533
  end
@@ -1521,6 +1555,9 @@ class Type < Puppet::Element
1521
1555
  self[param] = value
1522
1556
  end
1523
1557
  }
1558
+
1559
+ # Set the defaults again, just in case.
1560
+ self.setdefaults
1524
1561
  end
1525
1562
 
1526
1563
  # derive the instance name based on class.namevar
@@ -1654,23 +1691,9 @@ class Type < Puppet::Element
1654
1691
  @evalcount = 0
1655
1692
  end
1656
1693
  @@retrieved[self] += 1
1657
- # if we're a metaclass and we've already evaluated once...
1658
- #if self.metaclass and @evalcount > 0
1659
- # return
1660
- #end
1661
1694
  @evalcount += 1
1662
1695
 
1663
- #changes = @children.collect { |child|
1664
- # child.evaluate
1665
- #}
1666
-
1667
1696
  changes = []
1668
- # collect all of the changes from children and states
1669
- #if self.class.depthfirst?
1670
- # changes << self.collect { |child|
1671
- # child.evaluate
1672
- # }
1673
- #end
1674
1697
 
1675
1698
  # this only operates on states, not states + children
1676
1699
  # it's important that we call retrieve() on the type instance,
@@ -1694,16 +1717,6 @@ class Type < Puppet::Element
1694
1717
  child.cache(:checked, now)
1695
1718
  ch
1696
1719
  }
1697
- #unless self.class.depthfirst?
1698
- # changes << self.collect { |child|
1699
- # child.evaluate
1700
- # }
1701
- #end
1702
- # collect changes and return them
1703
- # these changes could be from child objects or from contained states
1704
- #self.collect { |child|
1705
- # child.evaluate
1706
- #}
1707
1720
 
1708
1721
  if self.class.depthfirst?
1709
1722
  changes += statechanges()
@@ -1716,9 +1729,6 @@ class Type < Puppet::Element
1716
1729
  if changes.length > 0
1717
1730
  self.info "%s change(s)" %
1718
1731
  [changes.length]
1719
- #changes.each { |change|
1720
- # self.debug "change: %s" % change.state.name
1721
- #}
1722
1732
  end
1723
1733
  self.cache(:checked, now)
1724
1734
  return changes.flatten
@@ -2199,4 +2209,4 @@ require 'puppet/type/user'
2199
2209
  require 'puppet/type/tidy'
2200
2210
  require 'puppet/type/parsedtype'
2201
2211
 
2202
- # $Id: type.rb 900 2006-02-13 18:00:17Z luke $
2212
+ # $Id: type.rb 914 2006-02-15 21:04:14Z luke $
@@ -103,6 +103,11 @@ module Puppet
103
103
  # a boolean of whether to do alpha checking, and if so requires
104
104
  # the ary against which to do the checking.
105
105
  munge do |value|
106
+ # Support 'absent' as a value, so that they can remove
107
+ # a value
108
+ if value == "absent" or value == :absent
109
+ return :absent
110
+ end
106
111
  return value unless self.class.boundaries
107
112
  lower, upper = self.class.boundaries
108
113
  retval = nil
@@ -135,7 +140,10 @@ module Puppet
135
140
  provided to the command varies by local system rules, and it is
136
141
  best to always provide a fully qualified command. The user's
137
142
  profile is not sourced when the command is run, so if the
138
- user's environment is desired it should be sourced manually."
143
+ user's environment is desired it should be sourced manually.
144
+
145
+ All cron parameters support ``absent`` as a value; this will
146
+ remove any existing values for that field."
139
147
  end
140
148
 
141
149
  newstate(:minute, CronParam) do
@@ -178,12 +186,13 @@ module Puppet
178
186
 
179
187
  newparam(:name) do
180
188
  desc "The symbolic name of the cron job. This name
181
- is used for human reference only and is generated
182
- automatically for cron jobs found on the system. This generally
183
- won't matter, as Puppet will do its best to match existing
184
- cron jobs against specified jobs (and Puppet adds a tag to
185
- cron jobs it adds), but it is at least possible that converting
186
- from unmanaged jobs to managed jobs might require manual intervention."
189
+ is used for human reference only and is generated automatically
190
+ for cron jobs found on the system. This generally won't
191
+ matter, as Puppet will do its best to match existing cron jobs
192
+ against specified jobs (and Puppet adds a tag to cron jobs it
193
+ adds), but it is at least possible that converting from
194
+ unmanaged jobs to managed jobs might require manual
195
+ intervention."
187
196
 
188
197
  isnamevar
189
198
  end
@@ -487,7 +496,9 @@ module Puppet
487
496
 
488
497
  def exists?
489
498
  val = false
490
- if @states.include?(:command) and @states[:command].is != :absent
499
+ if @states.include?(:command) and
500
+ @states[:command].is != :absent and
501
+ ! @states[:command].is.nil?
491
502
  val = true
492
503
  end
493
504
  return val
@@ -523,10 +534,14 @@ module Puppet
523
534
 
524
535
  return "# Puppet Name: %s\n" % self.name +
525
536
  self.class.fields.collect { |f|
526
- hash[f] || "*"
537
+ if hash[f] and hash[f] != :absent
538
+ hash[f]
539
+ else
540
+ "*"
541
+ end
527
542
  }.join(" ")
528
543
  end
529
544
  end
530
545
  end
531
546
 
532
- # $Id: cron.rb 898 2006-02-13 17:13:09Z luke $
547
+ # $Id: cron.rb 910 2006-02-15 07:20:36Z luke $
@@ -17,14 +17,16 @@ module Puppet
17
17
 
18
18
  # defined in the production class
19
19
  exec { \"make\":
20
- cwd => \"/prod/build/dir\"
20
+ cwd => \"/prod/build/dir\",
21
+ path => \"/usr/bin:/usr/sbin:/bin\"
21
22
  }
22
23
 
23
24
  . etc. .
24
25
 
25
26
  # defined in the test class
26
27
  exec { \"make\":
27
- cwd => \"/test/build/dir\"
28
+ cwd => \"/test/build/dir\",
29
+ path => \"/usr/bin:/usr/sbin:/bin\"
28
30
  }
29
31
 
30
32
  Any other type would throw an error, complaining that you had
@@ -45,6 +47,19 @@ module Puppet
45
47
  require 'open3'
46
48
  require 'puppet/type/state'
47
49
 
50
+ # Create a new check mechanism. It's basically just a parameter that provides
51
+ # one extra 'check' method.
52
+ def self.newcheck(name, &block)
53
+ @checks ||= {}
54
+
55
+ check = newparam(name, &block)
56
+ @checks[name] = check
57
+ end
58
+
59
+ def self.checks
60
+ @checks.keys
61
+ end
62
+
48
63
  newstate(:returns) do |state|
49
64
  munge do |value|
50
65
  value.to_s
@@ -96,25 +111,14 @@ module Puppet
96
111
  end
97
112
  end
98
113
 
99
- # because this command always runs,
100
- # we're just using retrieve to verify that the command
101
- # exists and such
114
+ # First verify that all of our checks pass.
102
115
  def retrieve
103
- if file = @parent[:creates]
104
- if FileTest.exists?(file)
105
- @is = true
106
- @should = [true]
107
- return
108
- end
109
- end
116
+ # Default to somethinng
110
117
 
111
- if self.parent[:refreshonly]
112
- # if refreshonly is enabled, then set things so we
113
- # won't sync
114
- self.is = self.should
118
+ if @parent.check
119
+ self.is = :notrun
115
120
  else
116
- # else, just set it to something we know it won't be
117
- self.is = nil
121
+ self.is = self.should
118
122
  end
119
123
  end
120
124
 
@@ -129,53 +133,24 @@ module Puppet
129
133
  tmppath = ENV["PATH"]
130
134
 
131
135
  event = :executed_command
132
- begin
133
- # Do our chdir
134
- Dir.chdir(dir) {
135
- if self.parent[:path]
136
- ENV["PATH"] = self.parent[:path].join(":")
137
- end
138
136
 
139
- # The user and group default to nil, which 'asuser'
140
- # handlers correctly
141
- Puppet::Util.asuser(@parent[:user], @parent[:group]) {
142
- # capture both stdout and stderr
143
- if @parent[:user]
144
- unless defined? @@alreadywarned
145
- Puppet.warning(
146
- "Cannot capture STDERR when running as another user"
147
- )
148
- @@alreadywarned = true
149
- end
150
- @output = %x{#{self.parent[:command]}}
151
- else
152
- @output = %x{#{self.parent[:command]} 2>&1}
153
- end
154
- }
155
- status = $?
137
+ @output, status = @parent.run(self.parent[:command])
156
138
 
157
- loglevel = @parent[:loglevel]
158
- if status.exitstatus.to_s != self.should.to_s
159
- err("%s returned %s" %
160
- [self.parent[:command],status.exitstatus])
139
+ loglevel = @parent[:loglevel]
140
+ if status.exitstatus.to_s != self.should.to_s
141
+ err("%s returned %s instead of %s" %
142
+ [self.parent[:command], status.exitstatus, self.should.to_s])
161
143
 
162
- # if we've had a failure, up the log level
163
- loglevel = :err
164
- event = :failed_command
165
- end
166
-
167
- # and log
168
- @output.split(/\n/).each { |line|
169
- self.send(loglevel, line)
170
- }
171
- }
172
- rescue Errno::ENOENT => detail
173
- self.fail detail.to_s
174
- ensure
175
- # reset things to how we found them
176
- ENV["PATH"] = tmppath
144
+ # if we've had a failure, up the log level
145
+ loglevel = :err
146
+ event = :failed_command
177
147
  end
178
148
 
149
+ # and log
150
+ @output.split(/\n/).each { |line|
151
+ self.send(loglevel, line)
152
+ }
153
+
179
154
  return event
180
155
  end
181
156
  end
@@ -276,7 +251,7 @@ module Puppet
276
251
  end
277
252
  end
278
253
 
279
- newparam(:refreshonly) do
254
+ newcheck(:refreshonly) do
280
255
  desc "The command should only be run as a
281
256
  refresh mechanism for when a dependent object is changed. It only
282
257
  makes sense to use this option when this command depends on some
@@ -295,9 +270,15 @@ module Puppet
295
270
  }
296
271
 
297
272
  "
273
+
274
+ # We always fail this test, because we're only supposed to run
275
+ # on refresh.
276
+ def check
277
+ false
278
+ end
298
279
  end
299
280
 
300
- newparam(:creates) do
281
+ newcheck(:creates) do
301
282
  desc "A file that this command creates. If this
302
283
  parameter is provided, then the command will only be run
303
284
  if the specified file does not exist.
@@ -319,6 +300,59 @@ module Puppet
319
300
  self.fail "'creates' files must be fully qualified."
320
301
  end
321
302
  end
303
+
304
+ # If the file exists, return false (i.e., don't run the command),
305
+ # else return true
306
+ def check
307
+ return ! FileTest.exists?(self.value)
308
+ end
309
+ end
310
+
311
+ newcheck(:unless) do
312
+ desc "If this parameter is set, then this +exec+ will run unless
313
+ the command returns 0. For example::
314
+
315
+ exec { \"/bin/echo root >> /usr/lib/cron/cron.allow\":
316
+ path => \"/usr/bin:/usr/sbin:/bin\",
317
+ unless => \"grep root /usr/lib/cron/cron.allow 2>/dev/null\"
318
+ }
319
+
320
+ This would add +root+ to the cron.allow file (on Solaris) unless
321
+ +grep+ determines it's already there.
322
+
323
+ Note that this command follows the same rules as the main command,
324
+ which is to say that it must be fully qualified if the path is not set.
325
+ "
326
+
327
+ # Return true if the command does not return 0.
328
+ def check
329
+ output, status = @parent.run(self.value)
330
+
331
+ return status.exitstatus != 0
332
+ end
333
+ end
334
+
335
+ newcheck(:onlyif) do
336
+ desc "If this parameter is set, then this +exec+ will only run if
337
+ the command returns 0. For example::
338
+
339
+ exec { \"logrotate\":
340
+ path => \"/usr/bin:/usr/sbin:/bin\",
341
+ onlyif => \"test `du /var/log/messages | cut -f1` -gt 100000\"
342
+ }
343
+
344
+ This would run +logrotate+ only if that test returned true.
345
+
346
+ Note that this command follows the same rules as the main command,
347
+ which is to say that it must be fully qualified if the path is not set.
348
+ "
349
+
350
+ # Return true if the command returns 0.
351
+ def check
352
+ output, status = @parent.run(self.value)
353
+
354
+ return status.exitstatus == 0
355
+ end
322
356
  end
323
357
 
324
358
  # Exec names are not isomorphic with the objects.
@@ -341,19 +375,38 @@ module Puppet
341
375
  reqs << self[:cwd]
342
376
  end
343
377
 
344
- tmp = self[:command].dup
378
+ [:command, :onlyif, :unless].each { |param|
379
+ next unless tmp = self[param]
345
380
 
346
- # And search the command line for files, adding any we find. This
347
- # will also catch the command itself if it's fully qualified. It might
348
- # not be a bad idea to add unqualified files, but, well, that's a
349
- # bit more annoying to do.
350
- while tmp.sub!(%r{(#{File::SEPARATOR}\S+)}, '')
351
- reqs << $1
352
- end
381
+ # And search the command line for files, adding any we find. This
382
+ # will also catch the command itself if it's fully qualified. It might
383
+ # not be a bad idea to add unqualified files, but, well, that's a
384
+ # bit more annoying to do.
385
+ reqs += tmp.scan(%r{(#{File::SEPARATOR}\S+)})
386
+ }
387
+
388
+ # For some reason, the += isn't causing a flattening
389
+ reqs.flatten!
353
390
 
354
391
  reqs
355
392
  end
356
393
 
394
+ # Verify that we pass all of the checks.
395
+ def check
396
+ self.class.checks.each { |check|
397
+ if @parameters.include?(check)
398
+ if @parameters[check].check
399
+ self.debug "%s returned true" % check
400
+ else
401
+ self.debug "%s returned false" % check
402
+ return false
403
+ end
404
+ end
405
+ }
406
+
407
+ return true
408
+ end
409
+
357
410
  def output
358
411
  if self.state(:returns).nil?
359
412
  return nil
@@ -367,10 +420,51 @@ module Puppet
367
420
  self.state(:returns).sync
368
421
  end
369
422
 
423
+ # Run a command.
424
+ def run(command)
425
+ output = nil
426
+ status = nil
427
+ tmppath = ENV["PATH"]
428
+ dir = self[:cwd] || Dir.pwd
429
+ begin
430
+ # Do our chdir
431
+ Dir.chdir(dir) {
432
+ if self[:path]
433
+ ENV["PATH"] = self[:path].join(":")
434
+ end
435
+
436
+ # The user and group default to nil, which 'asuser'
437
+ # handlers correctly
438
+ Puppet::Util.asuser(self[:user], self[:group]) {
439
+ # capture both stdout and stderr
440
+ if self[:user]
441
+ unless defined? @@alreadywarned
442
+ Puppet.warning(
443
+ "Cannot capture STDERR when running as another user"
444
+ )
445
+ @@alreadywarned = true
446
+ end
447
+ output = %x{#{command}}
448
+ else
449
+ output = %x{#{command} 2>&1}
450
+ end
451
+ }
452
+ status = $?.dup
453
+ }
454
+ rescue Errno::ENOENT => detail
455
+ self.fail detail.to_s
456
+ ensure
457
+ # reset things to how we found them
458
+ ENV["PATH"] = tmppath
459
+ end
460
+
461
+ return output, status
462
+ end
463
+
370
464
  def to_s
371
465
  "exec(%s)" % self.name
372
466
  end
373
467
  end
374
468
  end
375
469
 
376
- # $Id: exec.rb 841 2006-01-18 17:24:15Z luke $
470
+ # $Id: exec.rb 907 2006-02-13 21:58:40Z luke $