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
209
210
211
212
213
214
215
216
217
218
219
|
#! /usr/bin/env ruby
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)
File.stubs(:executable?).with(command).returns(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)
Puppet::Util::Execution.expects(:execute).with(filename, instance_of(Hash)).returns(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)
FileTest.stubs(:executable?).with(command).returns(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 "does not override the user when it is already the requested user" do
Etc.stubs(:getpwuid).returns(Struct::Passwd.new('testing'))
provider.resource[:user] = 'testing'
command = make_exe
Puppet::Util::Execution.expects(:execute).with(anything(), has_entry(:uid, nil)).returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(command)
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::Execution.expects(:execute).with("#{command} bar --sillyarg=true --blah", instance_of(Hash)).returns(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
Puppet::Util::Execution.expects(:execute).with(command, instance_of(Hash)).returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(command)
@logs.map {|l| "#{l.level}: #{l.message}" }.should == ["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'
Puppet::Util.expects(: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, status = provider.run(command % var)
output.strip.should == 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, status = provider.run(command % var)
output.strip.should == 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, status = provider.run(command % 'LANG')
output.strip.should == 'C'
output, status = provider.run(command % 'LC_ALL')
output.strip.should == '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
ENV[var].should == user_sentinel_env[var]
# run an "exec" via the provider and ensure that it unsets the vars
output, status = provider.run(command % var)
output.strip.should == ""
# ensure that after the exec, our temporary env is still intact
ENV[var].should == 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, status = provider.run(command % var)
# ensure that it matches our expected sentinel value
output.strip.should == sentinel_value
end
end
end
end
end
|