From b28bcb031346cfd2026361ec5ffb420c1dcf60d7 Mon Sep 17 00:00:00 2001 From: Nick Lewis Date: Mon, 15 Aug 2011 15:51:37 -0700 Subject: (#5495) Remove dead Windows-specific code from posix exec provider Because this provider only applies when the posix feature is present (and thus not the windows feature), it can never be used on Windows. Thus, the Windows-specific command handling is unnecessary and unused. Also added more specific error messages for the cases where a command doesn't exist, isn't a file, and isn't executable. These only apply when the command path is absolute (otherwise the message is simply command not found). Reviewed-By: Matt Robinson --- lib/puppet/provider/exec/posix.rb | 32 +++--- 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 -- cgit