1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
require 'spec_helper'
describe Puppet::Type.type(:exec).provider(:posix), :if => Puppet.features.posix? do
include PuppetSpec::Files
def make_exe
cmdpath = tmpdir('cmdpath')
exepath = tmpfile('my_command', cmdpath)
FileUtils.touch(exepath)
File.chmod(0755, exepath)
exepath
end
let(:resource) { Puppet::Type.type(:exec).new(:title => '/foo', :provider => :posix) }
let(:provider) { described_class.new(resource) }
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
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)
allow(File).to receive(:executable?).with(command).and_return(false)
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)
expect(Puppet::Util::Execution).to receive(:execute).with(filename, instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(filename)
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
it "should fail if the command is in the path but not executable" do
command = make_exe
File.chmod(0644, command)
allow(FileTest).to receive(:executable?).with(command).and_return(false)
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] = ['/bogus/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
expect(Puppet::Util::Execution).to receive(:execute).with("#{command} bar --sillyarg=true --blah", instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
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
expect(Puppet::Util::Execution).to receive(:execute).with(command, instance_of(Hash)).and_return(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(command)
expect(@logs.map {|l| "#{l.level}: #{l.message}" }).to eq(["warning: Overriding environment setting 'WHATEVER' with '/foo'"])
end
it "should set umask before execution if umask parameter is in use" do
provider.resource[:umask] = '0027'
expect(Puppet::Util).to receive(:withumask).with(0027)
provider.run(provider.resource[:command])
end
describe "posix locale settings" do
# a sentinel value that we can use to emulate what locale environment variables might be set to on an international
# system.
lang_sentinel_value = "en_US.UTF-8"
# a temporary hash that contains sentinel values for each of the locale environment variables that we override in
# "exec"
locale_sentinel_env = {}
Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| locale_sentinel_env[var] = lang_sentinel_value }
command = "/bin/echo $%s"
it "should not override user's locale during execution" do
# we'll do this once without any sentinel values, to give us a little more test coverage
orig_env = {}
Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| orig_env[var] = ENV[var] if ENV[var] }
orig_env.keys.each do |var|
output, _ = provider.run(command % var)
expect(output.strip).to eq(orig_env[var])
end
# now, once more... but with our sentinel values
Puppet::Util.withenv(locale_sentinel_env) do
Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var|
output, _ = provider.run(command % var)
expect(output.strip).to eq(locale_sentinel_env[var])
end
end
end
it "should respect locale overrides in user's 'environment' configuration" do
provider.resource[:environment] = ['LANG=C', 'LC_ALL=C']
output, _ = provider.run(command % 'LANG')
expect(output.strip).to eq('C')
output, _ = provider.run(command % 'LC_ALL')
expect(output.strip).to eq('C')
end
end
describe "posix user-related environment vars" do
# a temporary hash that contains sentinel values for each of the user-related environment variables that we
# are expected to unset during an "exec"
user_sentinel_env = {}
Puppet::Util::POSIX::USER_ENV_VARS.each { |var| user_sentinel_env[var] = "Abracadabra" }
command = "/bin/echo $%s"
it "should unset user-related environment vars during execution" do
# first we set up a temporary execution environment with sentinel values for the user-related environment vars
# that we care about.
Puppet::Util.withenv(user_sentinel_env) do
# with this environment, we loop over the vars in question
Puppet::Util::POSIX::USER_ENV_VARS.each do |var|
# ensure that our temporary environment is set up as we expect
expect(ENV[var]).to eq(user_sentinel_env[var])
# run an "exec" via the provider and ensure that it unsets the vars
output, _ = provider.run(command % var)
expect(output.strip).to eq("")
# ensure that after the exec, our temporary env is still intact
expect(ENV[var]).to eq(user_sentinel_env[var])
end
end
end
it "should respect overrides to user-related environment vars in caller's 'environment' configuration" do
sentinel_value = "Abracadabra"
# set the "environment" property of the resource, populating it with a hash containing sentinel values for
# each of the user-related posix environment variables
provider.resource[:environment] = Puppet::Util::POSIX::USER_ENV_VARS.collect { |var| "#{var}=#{sentinel_value}"}
# loop over the posix user-related environment variables
Puppet::Util::POSIX::USER_ENV_VARS.each do |var|
# run an 'exec' to get the value of each variable
output, _ = provider.run(command % var)
# ensure that it matches our expected sentinel value
expect(output.strip).to eq(sentinel_value)
end
end
end
end
end
|