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
data/CHANGELOG CHANGED
@@ -1,3 +1,52 @@
1
+ 2.6.8
2
+ =====
3
+ c1edcb2 add test for ticket 7101
4
+ db26326 Move tests from puppet-acceptance repo
5
+ bee1ef7 Updated CHANGELOG for 2.6.8rc1
6
+ 8b7444d (#2331) Remove darwinports pkg provider, replace with rewritten macports provider
7
+ 65c4e14 Fixed #7082 - Added system support for groups
8
+ b7f4ff7 (#7018) Give more context on the service type's assumptions. Wording tweaks.
9
+ bb19dea (#7018) explain internals better in service provider documentation
10
+ 23c9663 maint: Fix sqlite3 require to really be optional
11
+ 4b73d41 maint: Fix sporadic sqlite error
12
+ 54b9f5d (#6818) Stop from getting Rails 3 named_scope deprecation warning
13
+ e493f8a (#6856) Copy dangling symlinks with 'links => manage' File resource.
14
+ 1e4968e (maint) Indentation fixes
15
+ 99d78f2 (#6490) Add plugin initialization callback system to core
16
+ 5d1cb02 Fix #4339 - Locally save the last report to $lastrunreport
17
+ 306aa30 Fix #4339 - Save a last run report summary to $statedir/last_run_summary.yaml
18
+ 9bb3018 Fixed #3127 - removed legacy debug code
19
+ d2bacd3 Fixed #3127 - Fixed gem selection regex
20
+ 1b66c28 (#5437) Invalidate cached TypeCollection when there was an error parsing
21
+ 0675c9a (#6937) Adjust formatting of recurse's desc
22
+ 2cdadf9 (#6937) Document the recurse parameter of File type.
23
+ 647a640 (#6893) Document the cron type in the case of specials.
24
+ 87ca313 (#5670) Don't trigger refresh from a failed resource
25
+ f5aabf5 (#5908) Add support for new update-rc.d disable API
26
+ 37f9ca0 (#6862) Add a default subject for the mail_patches rake task
27
+ 9a4de12 Fixed #6256 - Creation of rrd directory.
28
+ 7c60db5 (#5477) Allow watch_file to watch non-existent files, especially site.pp
29
+ 7761acb (#5221) Add test for fix to fileset with trailing separator
30
+ 357514c (#5221) Fix fileset path absoluteness checking with trailing slash
31
+ f8941b8 (#4769) Fix negative timeout support for newer rubies
32
+ a29c7fd Fixed #6562 - Minor kick documentation fix
33
+ df20513 (#6658) Propagate ENC connection errors to the agent
34
+ 08115c0 (#4884) Remove typo from spec test
35
+ f2c771b (#4884) Modify tests to pass on non-OS X systems
36
+ ec1aa19 (#4884) Revise new exec tests, add a few more
37
+ 196294a (4576) - if ENC declares invalid class, it is logged at warning.
38
+ 0d2d6f3 (#4884) Add an shell provider for execs
39
+ d2e911a (#4884) Fix Test::Unit exec tests
40
+ fa0cfc6 (#4884) Break the exec type out to have a posix provider
41
+ c86a980 (#4884) Add consistent path validation and behavior
42
+ 77fbf7f (#4884) Add expand_path to requiring the spec_helper
43
+ 7ec9057 (#4884) Autorequire shared behaviors and method to silence warnings
44
+ acc99ba (#4884) Fix whitespace
45
+ 6a4d291 (#4884) Get rid of open3 require since it wasn't being used
46
+ 3e7ebbb Fixed #6554 - Missing $haveftool if/else conditional in install.rb breaking Ruby 1.9
47
+ fddc165 (#5814) Improved cron type specs
48
+ f2dfee6 (#5814) cron_spec shouldn't depend on cron provider
49
+
1
50
  2.6.7
2
51
  =====
3
52
  17f673d Updated CHANGELOG for 2.6.7rc1
data/install.rb CHANGED
@@ -92,8 +92,12 @@ def do_configs(configs, target, strip = 'conf/')
92
92
  Dir.mkdir(target) unless File.directory? target
93
93
  configs.each do |cf|
94
94
  ocf = File.join(InstallOptions.config_dir, cf.gsub(/#{strip}/, ''))
95
- File.install(cf, ocf, 0644, true)
96
- end
95
+ if $haveftools
96
+ File.install(cf, ocf, 0644, true)
97
+ else
98
+ FileUtils.install(cf, ocf, {:mode => 0644, :verbose => true})
99
+ end
100
+ end
97
101
  end
98
102
 
99
103
  def do_bins(bins, target, strip = 's?bin/')
@@ -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.7'
27
+ PUPPETVERSION = '2.6.8'
28
28
 
29
29
  def Puppet.version
30
30
  PUPPETVERSION
@@ -1,4 +1,5 @@
1
1
  require 'optparse'
2
+ require 'puppet/util/plugins'
2
3
 
3
4
  # This class handles all the aspects of a Puppet application/executable
4
5
  # * setting up options
@@ -297,11 +298,11 @@ class Application
297
298
 
298
299
  # This is the main application entry point
299
300
  def run
300
- exit_on_fail("initialize") { preinit }
301
- exit_on_fail("parse options") { parse_options }
301
+ exit_on_fail("initialize") { hook('preinit') { preinit } }
302
+ exit_on_fail("parse options") { hook('parse_options') { parse_options } }
302
303
  exit_on_fail("parse configuration file") { Puppet.settings.parse } if should_parse_config?
303
- exit_on_fail("prepare for execution") { setup }
304
- exit_on_fail("run") { run_command }
304
+ exit_on_fail("prepare for execution") { hook('setup') { setup } }
305
+ exit_on_fail("run") { hook('run_command') { run_command } }
305
306
  end
306
307
 
307
308
  def main
@@ -407,11 +408,18 @@ class Application
407
408
  private
408
409
 
409
410
  def exit_on_fail(message, code = 1)
410
- yield
411
+ yield
411
412
  rescue RuntimeError, NotImplementedError => detail
412
- puts detail.backtrace if Puppet[:trace]
413
- $stderr.puts "Could not #{message}: #{detail}"
414
- exit(code)
413
+ puts detail.backtrace if Puppet[:trace]
414
+ $stderr.puts "Could not #{message}: #{detail}"
415
+ exit(code)
416
+ end
417
+
418
+ def hook(step,&block)
419
+ Puppet::Plugins.send("before_application_#{step}",:application_object => self)
420
+ x = yield
421
+ Puppet::Plugins.send("after_application_#{step}",:application_object => self, :return_value => x)
422
+ x
415
423
  end
416
424
  end
417
425
  end
@@ -229,6 +229,8 @@ class Puppet::Application::Agent < Puppet::Application
229
229
  Puppet::SSL::Host.ca_location = options[:fingerprint] ? :none : :remote
230
230
 
231
231
  Puppet::Transaction::Report.terminus_class = :rest
232
+ # we want the last report to be persisted locally
233
+ Puppet::Transaction::Report.cache_class = :yaml
232
234
 
233
235
  # Override the default; puppetd needs this, usually.
234
236
  # You can still override this on the command-line with, e.g., :compiler.
@@ -148,6 +148,9 @@ class Puppet::Application::Apply < Puppet::Application
148
148
  exit(1)
149
149
  end
150
150
 
151
+ # we want the last report to be persisted locally
152
+ Puppet::Transaction::Report.cache_class = :yaml
153
+
151
154
  if options[:debug]
152
155
  Puppet::Util::Log.level = :debug
153
156
  elsif options[:verbose]
@@ -136,7 +136,7 @@ class Puppet::Application::Master < Puppet::Application
136
136
 
137
137
  exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
138
138
 
139
- Puppet.settings.use :main, :master, :ssl
139
+ Puppet.settings.use :main, :master, :ssl, :metrics
140
140
 
141
141
  # Cache our nodes in yaml. Currently not configurable.
142
142
  Puppet::Node.cache_class = :yaml
@@ -166,19 +166,28 @@ class Puppet::Configurer
166
166
  execute_postrun_command
167
167
 
168
168
  Puppet::Util::Log.close(report)
169
-
170
169
  send_report(report, transaction)
171
170
  end
172
171
 
173
172
  def send_report(report, trans)
174
173
  report.finalize_report if trans
175
174
  puts report.summary if Puppet[:summarize]
175
+ save_last_run_summary(report)
176
176
  report.save if Puppet[:report]
177
177
  rescue => detail
178
178
  puts detail.backtrace if Puppet[:trace]
179
179
  Puppet.err "Could not send report: #{detail}"
180
180
  end
181
181
 
182
+ def save_last_run_summary(report)
183
+ Puppet::Util::FileLocking.writelock(Puppet[:lastrunfile], 0660) do |file|
184
+ file.print YAML.dump(report.raw_summary)
185
+ end
186
+ rescue => detail
187
+ puts detail.backtrace if Puppet[:trace]
188
+ Puppet.err "Could not save last run local report: #{detail}"
189
+ end
190
+
182
191
  private
183
192
 
184
193
  def self.timeout
@@ -474,6 +474,7 @@ module Puppet
474
474
 
475
475
  setdefaults(:metrics,
476
476
  :rrddir => {:default => "$vardir/rrd",
477
+ :mode => 0750,
477
478
  :owner => "service",
478
479
  :group => "service",
479
480
  :desc => "The directory where RRD database files are stored.
@@ -606,6 +607,14 @@ module Puppet
606
607
  :report => [false,
607
608
  "Whether to send reports after every transaction."
608
609
  ],
610
+ :lastrunfile => { :default => "$statedir/last_run_summary.yaml",
611
+ :mode => 0660,
612
+ :desc => "Where puppet agent stores the last run report summary in yaml format."
613
+ },
614
+ :lastrunreport => { :default => "$statedir/last_run_report.yaml",
615
+ :mode => 0660,
616
+ :desc => "Where puppet agent stores the last run report in yaml format."
617
+ },
609
618
  :graph => [false, "Whether to create dot graph files for the different
610
619
  configuration graphs. These dot files can be interpreted by tools
611
620
  like OmniGraffle or dot (which is part of ImageMagick)."],
@@ -59,6 +59,7 @@ class Puppet::FileServing::Fileset
59
59
  end
60
60
 
61
61
  def initialize(path, options = {})
62
+ path = path.chomp(File::SEPARATOR)
62
63
  raise ArgumentError.new("Fileset paths must be fully qualified") unless File.expand_path(path) == path
63
64
 
64
65
  @path = path
@@ -35,8 +35,7 @@ class Puppet::Indirector::Exec < Puppet::Indirector::Terminus
35
35
  begin
36
36
  output = execute(external_command)
37
37
  rescue Puppet::ExecutionFailure => detail
38
- Puppet.err "Failed to find #{name} via exec: #{detail}"
39
- return nil
38
+ raise Puppet::Error, "Failed to find #{name} via exec: #{detail}"
40
39
  end
41
40
 
42
41
  if output =~ /\A\s*\Z/ # all whitespace
@@ -0,0 +1,11 @@
1
+ require 'puppet/transaction/report'
2
+ require 'puppet/indirector/yaml'
3
+
4
+ class Puppet::Transaction::Report::Yaml < Puppet::Indirector::Yaml
5
+ desc "Store last report as a flat file, serialized using YAML."
6
+
7
+ # Force report to be saved there
8
+ def path(name,ext='.yaml')
9
+ Puppet[:lastrunreport]
10
+ end
11
+ end
@@ -79,7 +79,7 @@ class Puppet::Node::Environment
79
79
  # environment has changed do we delve deeper.
80
80
  Thread.current[:known_resource_types] = nil if (krt = Thread.current[:known_resource_types]) && krt.environment != self
81
81
  Thread.current[:known_resource_types] ||= synchronize {
82
- if @known_resource_types.nil? or @known_resource_types.stale?
82
+ if @known_resource_types.nil? or @known_resource_types.require_reparse?
83
83
  @known_resource_types = Puppet::Resource::TypeCollection.new(self)
84
84
  @known_resource_types.perform_initial_import
85
85
  end
@@ -300,3 +300,5 @@ class Puppet::Parameter
300
300
  name.to_s
301
301
  end
302
302
  end
303
+
304
+ require 'puppet/parameter/path'
@@ -0,0 +1,42 @@
1
+ require 'puppet/parameter'
2
+
3
+ class Puppet::Parameter::Path < Puppet::Parameter
4
+ def self.accept_arrays(bool = true)
5
+ @accept_arrays = !!bool
6
+ end
7
+ def self.arrays?
8
+ @accept_arrays
9
+ end
10
+
11
+ def validate_path(paths)
12
+ if paths.is_a?(Array) and ! self.class.arrays? then
13
+ fail "#{name} only accepts a single path, not an array of paths"
14
+ end
15
+
16
+ # We *always* support Unix path separators, as Win32 does now too.
17
+ absolute = "[/#{::Regexp.quote(::File::SEPARATOR)}]"
18
+ win32 = Puppet.features.microsoft_windows?
19
+
20
+ Array(paths).each do |path|
21
+ next if path =~ %r{^#{absolute}}
22
+ next if win32 and path =~ %r{^(?:[a-zA-Z]:)?#{absolute}}
23
+ fail("#{name} must be a fully qualified path")
24
+ end
25
+
26
+ paths
27
+ end
28
+
29
+ # This will be overridden if someone uses the validate option, which is why
30
+ # it just delegates to the other, useful, method.
31
+ def unsafe_validate(paths)
32
+ validate_path(paths)
33
+ end
34
+
35
+ # Likewise, this might be overridden, but by default...
36
+ def unsafe_munge(paths)
37
+ if paths.is_a?(Array) and ! self.class.arrays? then
38
+ fail "#{name} only accepts a single path, not an array of paths"
39
+ end
40
+ paths
41
+ end
42
+ end
@@ -162,7 +162,7 @@ class Puppet::Parser::Compiler
162
162
  resource.evaluate unless lazy_evaluate
163
163
  found << name
164
164
  else
165
- Puppet.info "Could not find class #{name} for #{node.name}"
165
+ Puppet.warning "Could not find class #{name} for #{node.name}"
166
166
  @catalog.tag(name)
167
167
  end
168
168
  end
@@ -312,7 +312,8 @@ class Puppet::Parser::Lexer
312
312
  def file=(file)
313
313
  @file = file
314
314
  @line = 1
315
- @scanner = StringScanner.new(File.read(file))
315
+ contents = File.exists?(file) ? File.read(file) : ""
316
+ @scanner = StringScanner.new(contents)
316
317
  end
317
318
 
318
319
  def shift_token
@@ -547,7 +548,7 @@ class Puppet::Parser::Lexer
547
548
  value,terminator = slurpstring('"$')
548
549
  token_queue << [TOKENS[token_type[terminator]],preamble+value]
549
550
  if terminator != '$' or @scanner.scan(/\{/)
550
- token_queue.shift
551
+ token_queue.shift
551
552
  elsif var_name = @scanner.scan(%r{(\w*::)*\w+|[0-9]})
552
553
  token_queue << [TOKENS[:VARIABLE],var_name]
553
554
  tokenize_interpolated_string(DQ_continuation_token_types)
@@ -88,7 +88,6 @@ class Puppet::Parser::Parser
88
88
  unless file =~ /\.pp$/
89
89
  file = file + ".pp"
90
90
  end
91
- raise Puppet::Error, "Could not find file #{file}" unless FileTest.exist?(file)
92
91
  end
93
92
  raise Puppet::AlreadyImportedError, "Import loop detected" if known_resource_types.watching_file?(file)
94
93
 
@@ -0,0 +1,112 @@
1
+ Puppet::Type.type(:exec).provide :posix do
2
+ include Puppet::Util::Execution
3
+
4
+ confine :feature => :posix
5
+ defaultfor :feature => :posix
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."
10
+
11
+ def run(command, check = false)
12
+ output = nil
13
+ status = nil
14
+ dir = nil
15
+
16
+ checkexe(command)
17
+
18
+ if dir = resource[:cwd]
19
+ unless File.directory?(dir)
20
+ if check
21
+ dir = nil
22
+ else
23
+ self.fail "Working directory '#{dir}' does not exist"
24
+ end
25
+ end
26
+ end
27
+
28
+ dir ||= Dir.pwd
29
+
30
+ debug "Executing#{check ? " check": ""} '#{command}'"
31
+ begin
32
+ # Do our chdir
33
+ Dir.chdir(dir) do
34
+ environment = {}
35
+
36
+ environment[:PATH] = resource[:path].join(":") if resource[:path]
37
+
38
+ if envlist = resource[:environment]
39
+ envlist = [envlist] unless envlist.is_a? Array
40
+ envlist.each do |setting|
41
+ if setting =~ /^(\w+)=((.|\n)+)$/
42
+ env_name = $1
43
+ value = $2
44
+ if environment.include?(env_name) || environment.include?(env_name.to_sym)
45
+ warning "Overriding environment setting '#{env_name}' with '#{value}'"
46
+ end
47
+ environment[env_name] = value
48
+ else
49
+ warning "Cannot understand environment setting #{setting.inspect}"
50
+ end
51
+ end
52
+ end
53
+
54
+ withenv environment do
55
+ Timeout::timeout(resource[:timeout]) do
56
+ output, status = Puppet::Util::SUIDManager.
57
+ run_and_capture([command], resource[:user], resource[:group])
58
+ end
59
+ # The shell returns 127 if the command is missing.
60
+ if status.exitstatus == 127
61
+ raise ArgumentError, output
62
+ end
63
+ end
64
+ end
65
+ rescue Errno::ENOENT => detail
66
+ self.fail detail.to_s
67
+ end
68
+
69
+ return output, status
70
+ end
71
+
72
+ # Verify that we have the executable
73
+ def checkexe(command)
74
+ exe = extractexe(command)
75
+
76
+ if resource[:path]
77
+ if Puppet.features.posix? and !File.exists?(exe)
78
+ withenv :PATH => resource[:path].join(File::PATH_SEPARATOR) do
79
+ exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'")
80
+ end
81
+ elsif Puppet.features.microsoft_windows? and !File.exists?(exe)
82
+ resource[:path].each do |path|
83
+ [".exe", ".ps1", ".bat", ".com", ""].each do |extension|
84
+ file = File.join(path, exe+extension)
85
+ return if File.exists?(file)
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ raise ArgumentError, "Could not find command '#{exe}'" unless File.exists?(exe)
92
+ unless File.executable?(exe)
93
+ raise ArgumentError,
94
+ "'#{exe}' is not executable"
95
+ end
96
+ end
97
+
98
+ def extractexe(command)
99
+ # easy case: command was quoted
100
+ if command =~ /^"([^"]+)"/
101
+ $1
102
+ else
103
+ command.split(/ /)[0]
104
+ end
105
+ end
106
+
107
+ def validatecmd(command)
108
+ exe = extractexe(command)
109
+ # if we're not fully qualified, require a path
110
+ self.fail "'#{command}' is not qualified and no path was specified. Please qualify the command or specify a path." if File.expand_path(exe) != exe and resource[:path].nil?
111
+ end
112
+ end