Skip to content

Commit 3fd53ba

Browse files
committed
(PUP-9997) Pass cwd to execute method
Dir.chdir is problematic because it affects all threads in the current process and if puppet is started with a current working directory it doesn't have traverse/execute permission to, then it won't be able to restore the cwd at the end of the Dir.chdir block. Puppet supports three execution implementations: posix, windows and stub. The first two already support the `cwd` option. Puppetserver injects its stub implementation and it recently added support for `cwd`, see SERVER-3051.
1 parent 0894445 commit 3fd53ba

File tree

2 files changed

+66
-10
lines changed

2 files changed

+66
-10
lines changed

lib/puppet/parser/functions/generate.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
end
3232

3333
begin
34-
Dir.chdir(File.dirname(args[0])) { Puppet::Util::Execution.execute(args).to_str }
34+
dir = File.dirname(args[0])
35+
Puppet::Util::Execution.execute(args, failonfail: true, combine: true, cwd: dir).to_str
3536
rescue Puppet::ExecutionFailure => detail
3637
raise Puppet::ParseError, _("Failed to execute generator %{generator}: %{detail}") % { generator: args[0], detail: detail }, detail.backtrace
3738
end

spec/unit/parser/functions/generate_spec.rb

+64-9
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,41 @@
11
require 'spec_helper'
22

3+
def with_executor
4+
return yield unless Puppet::Util::Platform.jruby?
5+
6+
begin
7+
Puppet::Util::ExecutionStub.set do |command, options, stdin, stdout, stderr|
8+
require 'open3'
9+
# simulate what puppetserver does
10+
Dir.chdir(options[:cwd]) do
11+
out, err, _status = Open3.capture3(*command)
12+
stdout.write(out)
13+
stderr.write(err)
14+
# execution api expects stdout to be returned
15+
out
16+
end
17+
end
18+
yield
19+
ensure
20+
Puppet::Util::ExecutionStub.reset
21+
end
22+
end
23+
324
describe "the generate function" do
425
include PuppetSpec::Files
526

627
let :node do Puppet::Node.new('localhost') end
728
let :compiler do Puppet::Parser::Compiler.new(node) end
829
let :scope do Puppet::Parser::Scope.new(compiler) end
30+
let :cwd do tmpdir('generate') end
931

1032
it "should exist" do
1133
expect(Puppet::Parser::Functions.function("generate")).to eq("function_generate")
1234
end
1335

1436
it "accept a fully-qualified path as a command" do
1537
command = File.expand_path('/command/foo')
16-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
38+
expect(Puppet::Util::Execution).to receive(:execute).with([command], anything).and_return("yay")
1739
expect(scope.function_generate([command])).to eq("yay")
1840
end
1941

@@ -35,33 +57,66 @@
3557
end
3658

3759
it "should execute the generate script with the correct working directory" do
38-
command = File.expand_path("/command")
39-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
40-
expect(scope.function_generate([command])).to eq('yay')
60+
command = File.expand_path("/usr/local/command")
61+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: %r{/usr/local})).and_return("yay")
62+
scope.function_generate([command])
63+
end
64+
65+
it "should execute the generate script with failonfail" do
66+
command = File.expand_path("/usr/local/command")
67+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(failonfail: true)).and_return("yay")
68+
scope.function_generate([command])
69+
end
70+
71+
it "should execute the generate script with combine" do
72+
command = File.expand_path("/usr/local/command")
73+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(combine: true)).and_return("yay")
74+
scope.function_generate([command])
75+
end
76+
77+
it "executes a command in a working directory" do
78+
if Puppet::Util::Platform.windows?
79+
command = File.join(cwd, 'echo.bat')
80+
File.write(command, <<~END)
81+
@echo off
82+
echo %CD%
83+
END
84+
expect(scope.function_generate([command]).chomp).to match(cwd.gsub('/', '\\'))
85+
else
86+
with_executor do
87+
command = File.join(cwd, 'echo.sh')
88+
File.write(command, <<~END)
89+
#!/bin/sh
90+
echo $PWD
91+
END
92+
Puppet::FileSystem.chmod(0755, command)
93+
expect(scope.function_generate([command]).chomp).to eq(cwd)
94+
end
95+
end
4196
end
4297

4398
describe "on Windows", :if => Puppet::Util::Platform.windows? do
4499
it "should accept the tilde in the path" do
45100
command = "C:/DOCUME~1/ADMINI~1/foo.bat"
46-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
101+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: "C:/DOCUME~1/ADMINI~1")).and_return("yay")
47102
expect(scope.function_generate([command])).to eq('yay')
48103
end
49104

50105
it "should accept lower-case drive letters" do
51106
command = 'd:/command/foo'
52-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
107+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: "d:/command")).and_return("yay")
53108
expect(scope.function_generate([command])).to eq('yay')
54109
end
55110

56111
it "should accept upper-case drive letters" do
57112
command = 'D:/command/foo'
58-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
113+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: "D:/command")).and_return("yay")
59114
expect(scope.function_generate([command])).to eq('yay')
60115
end
61116

62117
it "should accept forward and backslashes in the path" do
63118
command = 'D:\command/foo\bar'
64-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
119+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: 'D:\command/foo')).and_return("yay")
65120
expect(scope.function_generate([command])).to eq('yay')
66121
end
67122

@@ -81,7 +136,7 @@
81136

82137
it "should accept plus and dash" do
83138
command = "/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-/foo"
84-
expect(Dir).to receive(:chdir).with(File.dirname(command)).and_return("yay")
139+
expect(Puppet::Util::Execution).to receive(:execute).with([command], hash_including(cwd: '/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-')).and_return("yay")
85140
expect(scope.function_generate([command])).to eq('yay')
86141
end
87142
end

0 commit comments

Comments
 (0)