Skip to content

Commit ae51234

Browse files
committed
Add integration tests for jail transport
This require a jail named `bolt` where python3.9 is installed.
1 parent ce71d6c commit ae51234

File tree

6 files changed

+253
-5
lines changed

6 files changed

+253
-5
lines changed

lib/bolt/shell/bash.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ def execute(command, sudoable: false, **options)
356356
if defined? conn.add_env_vars
357357
conn.add_env_vars(options[:environment])
358358
else
359-
env_decl = options[:environment].map do |env, val|
359+
env_decl = '/usr/bin/env ' + options[:environment].map do |env, val|
360360
"#{env}=#{Shellwords.shellescape(val)}"
361361
end.join(' ')
362362
end

spec/fixtures/modules/results/tasks/init.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env bash
1+
#!/bin/sh
22

33
if [ ! -z "$PT_fail" ]; then
44
exit 1
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
#!/bin/bash
1+
#!/bin/sh
22

33
grep "message"

spec/integration/jail_spec.rb

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
require 'bolt_spec/conn'
5+
require 'bolt_spec/files'
6+
require 'bolt_spec/integration'
7+
require 'bolt_spec/project'
8+
9+
describe "when runnning over the jail transport", jail: true do
10+
include BoltSpec::Conn
11+
include BoltSpec::Files
12+
include BoltSpec::Integration
13+
include BoltSpec::Project
14+
15+
let(:whoami) { "whoami" }
16+
let(:modulepath) { fixtures_path('modules') }
17+
let(:stdin_task) { "sample::stdin" }
18+
let(:uri) { conn_uri('jail') }
19+
let(:user) { conn_info('jail')[:user] }
20+
let(:password) { conn_info('jail')[:password] }
21+
22+
after(:each) { Puppet.settings.send(:clear_everything_for_tests) }
23+
24+
context 'when using CLI options' do
25+
let(:config_flags) {
26+
%W[--targets #{uri} --no-host-key-check --format json --modulepath #{modulepath} --password #{password}]
27+
}
28+
29+
it 'runs a command' do
30+
result = run_one_node(%W[command run #{whoami}] + config_flags)
31+
expect(result['stdout'].strip).to eq('root')
32+
end
33+
34+
it 'reports errors when command fails' do
35+
result = run_failed_node(%w[command run boop] + config_flags)
36+
expect(result['_error']['kind']).to eq('puppetlabs.tasks/command-error')
37+
expect(result['_error']['msg']).to eq('The command failed with exit code 1')
38+
end
39+
40+
it 'runs a task', :reset_puppet_settings do
41+
result = run_one_node(%W[task run #{stdin_task} message=somemessage] + config_flags)
42+
expect(result['message'].strip).to eq("somemessage")
43+
end
44+
45+
it 'reports errors when task fails', :reset_puppet_settings do
46+
result = run_failed_node(%w[task run results fail=true] + config_flags)
47+
expect(result['_error']['kind']).to eq('puppetlabs.tasks/task-error')
48+
expect(result['_error']['msg']).to eq("The task failed with exit code 1 and no output")
49+
end
50+
51+
it 'passes noop to a task that supports noop', :reset_puppet_settings do
52+
result = run_one_node(%w[task run sample::noop message=somemessage --noop] + config_flags)
53+
expect(result['_output'].strip).to eq("somemessage with noop true")
54+
end
55+
56+
it 'passes noop to a plan that runs a task with noop', :reset_puppet_settings do
57+
result = run_cli_json(%w[plan run sample::noop] + config_flags)[0]['value']
58+
expect(result['_output'].strip).to eq("This works with noop true")
59+
end
60+
61+
it 'does not pass noop to a task by default', :reset_puppet_settings do
62+
result = run_one_node(%w[task run sample::noop message=somemessage] + config_flags)
63+
expect(result['_output'].strip).to eq("somemessage with noop")
64+
end
65+
66+
it 'escalates privileges when passed --run-as' do
67+
result = run_one_node(%W[command run #{whoami} --run-as root --sudo-password #{password}] + config_flags)
68+
expect(result['stdout'].strip).to eq("root")
69+
result = run_one_node(%W[command run #{whoami} --run-as #{user} --sudo-password #{password}] + config_flags)
70+
expect(result['stdout'].strip).to eq(user)
71+
end
72+
end
73+
74+
context 'when using a project', :reset_puppet_settings do
75+
let(:config) do
76+
{
77+
'format' => 'json',
78+
'future' => future_config,
79+
'modulepath' => modulepath
80+
}
81+
end
82+
83+
let(:future_config) { {} }
84+
85+
let(:default_inv) do
86+
{
87+
'config' => {
88+
'jail' => {
89+
}
90+
}
91+
}
92+
end
93+
94+
let(:inv) { default_inv }
95+
let(:uri) { (1..2).map { |i| "#{conn_uri('jail')}?id=#{i}" }.join(',') }
96+
let(:project) { @project }
97+
let(:config_flags) { %W[--targets #{uri} --project #{project.path}] }
98+
let(:single_target_conf) { %W[--targets #{conn_uri('jail')} --project #{project.path}] }
99+
let(:interpreter_task) { 'sample::interpreter' }
100+
let(:interpreter_script) { 'sample/scripts/script.py' }
101+
102+
let(:run_as_conf) do
103+
{
104+
'config' => {
105+
'jail' => {
106+
}
107+
}
108+
}
109+
end
110+
111+
let(:interpreter_ext) do
112+
{
113+
'config' => {
114+
'jail' => {
115+
'interpreters' => {
116+
'.py' => '/usr/local/bin/python3.9'
117+
}
118+
}
119+
}
120+
}
121+
end
122+
123+
let(:interpreter_no_ext) do
124+
{
125+
'config' => {
126+
'jail' => {
127+
'interpreters' => {
128+
'py' => '/usr/local/bin/python3.9'
129+
}
130+
}
131+
}
132+
}
133+
end
134+
135+
let(:interpreter_array) do
136+
{
137+
'config' => {
138+
'jail' => {
139+
'interpreters' => {
140+
'py' => ['/usr/local/bin/python3.9', '-d']
141+
}
142+
}
143+
}
144+
}
145+
end
146+
147+
around :each do |example|
148+
with_project(config: config, inventory: inv) do |project|
149+
@project = project
150+
example.run
151+
end
152+
end
153+
154+
shared_examples 'script interpreter' do
155+
it 'does not run script with specified interpreter' do
156+
result = run_cli_json(%W[script run #{interpreter_script}] + config_flags)['items'][0]
157+
expect(result['status']).to eq('failure')
158+
expect(result['value']['exit_code']).to eq(2)
159+
expect(result['value']['stderr']).to match(/word unexpected/)
160+
end
161+
162+
context 'with future.script_interpreter configured' do
163+
let(:future_config) do
164+
{
165+
'script_interpreter' => true
166+
}
167+
end
168+
169+
it 'runs script with specified interpreter' do
170+
result = run_cli_json(%W[script run #{interpreter_script}] + config_flags)['items'][0]
171+
expect(result['status']).to eq('success')
172+
expect(result['value']['exit_code']).to eq(0)
173+
expect(result['value']['stdout']).to match(/Hello, world!/)
174+
end
175+
end
176+
end
177+
178+
it 'runs multiple commands' do
179+
result = run_nodes(%W[command run #{whoami}] + config_flags)
180+
expect(result.map { |r| r['stdout'].strip }).to eq([user, user])
181+
end
182+
183+
it 'runs multiple tasks' do
184+
result = run_nodes(%W[task run #{stdin_task} message=short] + config_flags)
185+
expect(result.map { |r| r['message'].strip }).to eq(%w[short short])
186+
end
187+
188+
context 'with run-as configured' do
189+
let(:inv) { Bolt::Util.deep_merge(default_inv, run_as_conf) }
190+
191+
it 'runs multiple tasks as a specified user' do
192+
result = run_nodes(%W[command run #{whoami} --sudo-password #{password}] + config_flags)
193+
expect(result.map { |r| r['stdout'].strip }).to eq([user, user])
194+
end
195+
end
196+
197+
context 'with interpreters without dots configured' do
198+
let(:inv) { Bolt::Util.deep_merge(default_inv, interpreter_no_ext) }
199+
200+
include_examples 'script interpreter'
201+
202+
it 'runs task with specified interpreter key py' do
203+
result = run_nodes(%W[task run #{interpreter_task} message=short] + config_flags)
204+
expect(result.map { |r| r['env'].strip }).to eq(%w[short short])
205+
expect(result.map { |r| r['stdin'].strip }).to eq(%w[short short])
206+
end
207+
208+
it 'runs task with specified interpreter that with run-as set' do
209+
result = run_nodes(%W[task run #{interpreter_task} message=short
210+
--run-as root --sudo-password #{password}] + config_flags)
211+
expect(result.map { |r| r['env'].strip }).to eq(%w[short short])
212+
expect(result.map { |r| r['stdin'].strip }).to eq(%w[short short])
213+
end
214+
end
215+
216+
context 'with interpreters with dots configured' do
217+
let(:inv) { Bolt::Util.deep_merge(default_inv, interpreter_ext) }
218+
219+
include_examples 'script interpreter'
220+
221+
it 'runs task with interpreter key .py' do
222+
result = run_nodes(%W[task run #{interpreter_task} message=short] + config_flags)
223+
expect(result.map { |r| r['env'].strip }).to eq(%w[short short])
224+
expect(result.map { |r| r['stdin'].strip }).to eq(%w[short short])
225+
end
226+
end
227+
228+
context 'with interpreters as an array' do
229+
let(:inv) { Bolt::Util.deep_merge(default_inv, interpreter_array) }
230+
231+
include_examples 'script interpreter'
232+
233+
it 'runs task with interpreter value as array' do
234+
result = run_nodes(%W[task run #{interpreter_task} message=short] + config_flags)
235+
expect(result.map { |r| r['env'].strip }).to eq(%w[short short])
236+
expect(result.map { |r| r['stdin'].strip }).to eq(%w[short short])
237+
end
238+
end
239+
240+
it 'task fails when bad shebang is not overriden' do
241+
result = run_failed_node(%W[task run #{interpreter_task} message=short] + single_target_conf)
242+
expect(result['_error']['msg']).to match(/interpreter.py: not found/)
243+
end
244+
end
245+
end

spec/lib/bolt_spec/conn.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ def conn_info(transport)
2929
default_host = 'ubuntu_node'
3030
when 'lxd'
3131
default_host = 'testlxd'
32+
when 'jail'
33+
default_user = 'root'
34+
default_host = 'bolt'
3235
else
33-
raise Error, "The transport must be either 'ssh', 'winrm', 'docker', 'podman', or 'lxd'."
36+
raise Error, "The transport must be either 'ssh', 'winrm', 'docker', 'podman', 'lxd' or 'jail'."
3437
end
3538

3639
additional_config.merge(

spec/unit/shell/bash_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def echo_result
131131
end
132132

133133
it "sets environment variables if requested" do
134-
expect(connection).to receive(:execute).with('FOO=bar sh -c echo\\ \\$FOO')
134+
expect(connection).to receive(:execute).with('/usr/bin/env FOO=bar sh -c echo\\ \\$FOO')
135135

136136
shell.run_command('echo $FOO', env_vars: { 'FOO' => 'bar' })
137137
end

0 commit comments

Comments
 (0)