diff options
-rw-r--r-- | lib/puppet/provider/exec/posix.rb | 32 | ||||
-rwxr-xr-x | spec/unit/provider/exec/posix_spec.rb | 209 |
2 files changed, 120 insertions, 121 deletions
diff --git a/lib/puppet/provider/exec/posix.rb b/lib/puppet/provider/exec/posix.rb index 157d0f28d..782f1eac6 100644 --- a/lib/puppet/provider/exec/posix.rb +++ b/lib/puppet/provider/exec/posix.rb @@ -76,26 +76,26 @@ Puppet::Type.type(:exec).provide :posix do def checkexe(command) exe = extractexe(command) - if resource[:path] - if Puppet.features.posix? and !File.exists?(exe) - withenv :PATH => resource[:path].join(File::PATH_SEPARATOR) do - exe = which(exe) || raise(ArgumentError,"Could not find command '#{exe}'") - end - elsif Puppet.features.microsoft_windows? and !File.exists?(exe) - resource[:path].each do |path| - [".exe", ".ps1", ".bat", ".com", ""].each do |extension| - file = File.join(path, exe+extension) - return if File.exists?(file) - end - end + if File.expand_path(exe) == exe + if !File.exists?(exe) + raise ArgumentError, "Could not find command '#{exe}'" + elsif !File.file?(exe) + raise ArgumentError, "'#{exe}' is a #{File.ftype(exe)}, not a file" + elsif !File.executable?(exe) + raise ArgumentError, "'#{exe}' is not executable" end + return end - raise ArgumentError, "Could not find command '#{exe}'" unless File.exists?(exe) - unless File.executable?(exe) - raise ArgumentError, - "'#{exe}' is not executable" + if resource[:path] + withenv :PATH => resource[:path].join(File::PATH_SEPARATOR) do + return if which(exe) + end end + + # 'which' will only return the command if it's executable, so we can't + # distinguish not found from not executable + raise ArgumentError, "Could not find command '#{exe}'" end def extractexe(command) diff --git a/spec/unit/provider/exec/posix_spec.rb b/spec/unit/provider/exec/posix_spec.rb index 50697d826..876b9724d 100755 --- a/spec/unit/provider/exec/posix_spec.rb +++ b/spec/unit/provider/exec/posix_spec.rb @@ -1,120 +1,119 @@ #!/usr/bin/env rspec require 'spec_helper' -provider_class = Puppet::Type.type(:exec).provider(:posix) +describe Puppet::Type.type(:exec).provider(:posix) do + include PuppetSpec::Files + + def make_exe + command = tmpfile('my_command') + FileUtils.touch(command) + File.chmod(0755, command) + command + end + + let(:resource) { Puppet::Type.type(:exec).new(:title => '/foo') } + let(:provider) { described_class.new(resource) } -describe provider_class do before :each do - @resource = Puppet::Resource.new(:exec, 'foo') - @provider = provider_class.new(@resource) + Puppet.features.stubs(:posix?).returns(true) + Puppet.features.stubs(:microsoft_windows?).returns(false) + end + + describe "#validatecmd" do + it "should fail if no path is specified and the command is not fully qualified" do + expect { provider.validatecmd("foo") }.to raise_error( + Puppet::Error, + "'foo' is not qualified and no path was specified. Please qualify the command or specify a path." + ) + end + + it "should pass if a path is given" do + provider.resource[:path] = ['/bogus/bin'] + provider.validatecmd("../foo") + end + + it "should pass if command is fully qualifed" do + provider.resource[:path] = ['/bogus/bin'] + provider.validatecmd("/bin/blah/foo") + end end - ["posix", "microsoft_windows"].each do |feature| - describe "when in #{feature} environment" do - before :each do - if feature == "microsoft_windows" - Puppet.features.stubs(:microsoft_windows?).returns(true) - Puppet.features.stubs(:posix?).returns(false) - else - Puppet.features.stubs(:posix?).returns(true) - Puppet.features.stubs(:microsoft_windows?).returns(false) - end + describe "#run" do + describe "when the command is an absolute path" do + let(:command) { tmpfile('foo') } + + it "should fail if the command doesn't exist" do + expect { provider.run(command) }.to raise_error(ArgumentError, "Could not find command '#{command}'") + end + + it "should fail if the command isn't a file" do + FileUtils.mkdir(command) + FileUtils.chmod(0755, command) + + expect { provider.run(command) }.to raise_error(ArgumentError, "'#{command}' is a directory, not a file") + end + + it "should fail if the command isn't executable" do + FileUtils.touch(command) + + expect { provider.run(command) }.to raise_error(ArgumentError, "'#{command}' is not executable") + end + end + + describe "when the command is a relative path" do + it "should execute the command if it finds it in the path and is executable" do + command = make_exe + provider.resource[:path] = [File.dirname(command)] + filename = File.basename(command) + + Puppet::Util.expects(:execute).with { |cmdline, arguments| (cmdline == [filename]) && (arguments.is_a? Hash) } + + provider.run(filename) end - describe "#validatecmd" do - it "should fail if no path is specified and the command is not fully qualified" do - lambda { @provider.validatecmd("foo") }.should raise_error( - Puppet::Error, - "'foo' is not qualified and no path was specified. Please qualify the command or specify a path." - ) - end - - it "should pass if a path is given" do - @provider.resource[:path] = ['/bogus/bin'] - @provider.validatecmd("../foo") - end - - it "should pass if command is fully qualifed" do - @provider.resource[:path] = ['/bogus/bin'] - @provider.validatecmd("/bin/blah/foo") - end + it "should fail if the command isn't in the path" do + resource[:path] = ["/fake/path"] + + expect { provider.run('foo') }.to raise_error(ArgumentError, "Could not find command 'foo'") end - describe "#run" do - it "should fail if no path is specified and command does not exist" do - lambda { @provider.run("foo") }.should raise_error(ArgumentError, "Could not find command 'foo'") - end - - it "should fail if the command isn't in the path" do - @provider.resource[:path] = ['/bogus/bin'] - lambda { @provider.run("foo") }.should raise_error(ArgumentError, "Could not find command 'foo'") - end - - it "should fail if the command isn't executable" do - @provider.resource[:path] = ['/bogus/bin'] - File.stubs(:exists?).with("foo").returns(true) - - lambda { @provider.run("foo") }.should raise_error(ArgumentError, "'foo' is not executable") - end - - it "should not be able to execute shell builtins" do - @provider.resource[:path] = ['/bin'] - lambda { @provider.run("cd ..") }.should raise_error(ArgumentError, "Could not find command 'cd'") - end - - it "should execute the command if the command given includes arguments or subcommands" do - @provider.resource[:path] = ['/bogus/bin'] - File.stubs(:exists?).returns(false) - File.stubs(:exists?).with("foo").returns(true) - File.stubs(:executable?).with("foo").returns(true) - - Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo bar --sillyarg=true --blah']) && (arguments.is_a? Hash) } - @provider.run("foo bar --sillyarg=true --blah") - end - - it "should fail if quoted command doesn't exist" do - @provider.resource[:path] = ['/bogus/bin'] - File.stubs(:exists?).returns(false) - File.stubs(:exists?).with("foo").returns(true) - File.stubs(:executable?).with("foo").returns(true) - - lambda { @provider.run('"foo bar --sillyarg=true --blah"') }.should raise_error(ArgumentError, "Could not find command 'foo bar --sillyarg=true --blah'") - end - - it "should execute the command if it finds it in the path and is executable" do - @provider.resource[:path] = ['/bogus/bin'] - File.stubs(:exists?).with("foo").returns(true) - File.stubs(:executable?).with("foo").returns(true) - Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo']) && (arguments.is_a? Hash) } - - @provider.run("foo") - end - - if feature == "microsoft_windows" - [".exe", ".ps1", ".bat", ".com", ""].each do |extension| - it "should check file extension #{extension} when it can't find the executable" do - @provider.resource[:path] = ['/bogus/bin'] - File.stubs(:exists?).returns(false) - File.stubs(:exists?).with("/bogus/bin/foo#{extension}").returns(true) - File.stubs(:executable?).with("foo").returns(true) - Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo']) && (arguments.is_a? Hash) } - - @provider.run("foo") - end - end - end - - it "should warn if you're overriding something in environment" do - @provider.resource[:environment] = ['WHATEVER=/something/else', 'WHATEVER=/foo'] - File.stubs(:exists?).returns(false) - File.stubs(:exists?).with("foo").returns(true) - File.stubs(:executable?).with("foo").returns(true) - - Puppet::Util.expects(:execute).with() { |command, arguments| (command == ['foo']) && (arguments.is_a? Hash) } - @provider.run("foo") - @logs.map {|l| "#{l.level}: #{l.message}" }.should == ["warning: Overriding environment setting 'WHATEVER' with '/foo'"] - end + it "should fail if the command is in the path but not executable" do + command = tmpfile('foo') + FileUtils.touch(command) + resource[:path] = [File.dirname(command)] + filename = File.basename(command) + + expect { provider.run(filename) }.to raise_error(ArgumentError, "Could not find command '#{filename}'") end end + + it "should not be able to execute shell builtins" do + provider.resource[:path] = ['/bin'] + expect { provider.run("cd ..") }.to raise_error(ArgumentError, "Could not find command 'cd'") + end + + it "should execute the command if the command given includes arguments or subcommands" do + provider.resource[:path] = ['/bogus/bin'] + command = make_exe + + Puppet::Util.expects(:execute).with { |cmdline, arguments| (cmdline == ["#{command} bar --sillyarg=true --blah"]) && (arguments.is_a? Hash) } + provider.run("#{command} bar --sillyarg=true --blah") + end + + it "should fail if quoted command doesn't exist" do + provider.resource[:path] = ['/bogus/bin'] + command = "/foo bar --sillyarg=true --blah" + + expect { provider.run(%Q["#{command}"]) }.to raise_error(ArgumentError, "Could not find command '#{command}'") + end + + it "should warn if you're overriding something in environment" do + provider.resource[:environment] = ['WHATEVER=/something/else', 'WHATEVER=/foo'] + command = make_exe + + Puppet::Util.expects(:execute).with { |cmdline, arguments| (cmdline== [command]) && (arguments.is_a? Hash) } + provider.run(command) + @logs.map {|l| "#{l.level}: #{l.message}" }.should == ["warning: Overriding environment setting 'WHATEVER' with '/foo'"] + end end end |