Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix save_to_file to be indentation independent #10283

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions app/services/foreman/renderer/scope/macros/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,21 @@ def pxe_kernel_options
example "save_to_file(shell_escape('/tmp/a file with spaces'), nil) # => 'cp /dev/null /tmp/a\ file\ with\ spaces'"
end
def save_to_file(filename, content, verbatim: false)
delimiter = 'EOF-' + Digest::SHA512.hexdigest(filename)[0..7]
if content.empty?
"cp /dev/null #{filename}"
elsif verbatim
content = Base64.encode64(content)
"cat << #{delimiter} | base64 -d > #{filename}\n#{content}#{delimiter}"
base64 = Base64.encode64(content)
content_assignments = base64.split("\n").map { |l| "echo #{Shellwords.shellescape(l)}" }.join("\n")
Comment on lines +123 to +124
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the escaping even needed? Base64 shouldn't contain any problematic characters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"(#{content_assignments}) | base64 -d > #{filename}"
else
content += "\n" unless content.end_with?("\n")
"cat << #{delimiter} > #{filename}\n#{content}#{delimiter}"
content_echos = content.split("\n").map do |content_line|
# to make sure there the substitutions are working, have the bash script
# handle all special characters except double quotes.
"echo \"#{content_line.gsub('"', '\"')}\" >> #{filename}"
end.join("\n")

# prefix the append commands with a cleanup command
"cat /dev/null > #{filename}\n#{content_echos}"
end
end

Expand Down
2 changes: 2 additions & 0 deletions test/static_fixtures/interpolated_text.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
foo: bar
baz: $INTERPOLATED
5 changes: 5 additions & 0 deletions test/static_fixtures/script.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh

echo "Calling Ansible AWX/Tower provisioning callback..."
/usr/bin/curl -v -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing some interpolated values. Can we somehow set up the environment so those are set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added another test with interpolated values.

echo "DONE"
5 changes: 4 additions & 1 deletion test/unit/foreman/renderer/renderers_shared_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ module RenderersSharedTests
test "should render a save_to_file macro" do
source = OpenStruct.new(content: '<%= save_to_file("/etc/puppet/puppet.conf", "[main]\nserver=example.com\n") %>')
assert_nothing_raised do
assert_equal("cat << EOF-728d4ec4 > /etc/puppet/puppet.conf\n[main]\nserver=example.com\nEOF-728d4ec4", renderer.render(source, @scope))
actual = renderer.render(source, @scope)
assert_match /[main]/, actual
assert_match /server=example.com/, actual
assert_match /\/etc\/puppet\/puppet.conf/, actual
end
end

Expand Down
72 changes: 64 additions & 8 deletions test/unit/foreman/renderer/scope/macros/base_test.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'test_helper'
require 'tempfile'

class BaseMacrosTest < ActiveSupport::TestCase
setup do
Expand Down Expand Up @@ -249,21 +250,76 @@ class BaseMacrosTest < ActiveSupport::TestCase
end

test "should add a missing newline" do
delimiter = 'EOF-e6fb375b'
command = @scope.save_to_file('/tmp/test', 'echo hello')
assert_equal command, "cat << #{delimiter} > /tmp/test\necho hello\n#{delimiter}"
tmp_file = Tempfile.new('save_to_file')
tmp_file.close

file_content = 'echo hello'

command = @scope.save_to_file(tmp_file.path, file_content)

system({'INTERPOLATED' => 'test_value_123'}, command)

actual = `cat #{tmp_file.path}`

assert_equal "#{file_content}\n", actual
ensure
tmp_file.unlink
end

test "should encode content as base64" do
delimiter = 'EOF-e6fb375b'
base64 = Base64.encode64('echo hello')
command = @scope.save_to_file('/tmp/test', 'echo hello', verbatim: true)
assert_equal command, "cat << #{delimiter} | base64 -d > /tmp/test\n#{base64}#{delimiter}"
test "should not interpolate content with verbatim on" do
tmp_file = Tempfile.new('save_to_file')
tmp_file.close

file_content = File.read(Rails.root.join('test', 'static_fixtures', 'interpolated_text.txt'))

command = @scope.save_to_file(tmp_file.path, file_content, verbatim: true)

system({'INTERPOLATED' => 'test_value_123'}, command)

actual = `cat #{tmp_file.path}`

assert_equal file_content, actual
ensure
tmp_file.unlink
end

test "should ignore escaping of filename by default" do
command = @scope.save_to_file('/tmp/ifcfg-$sanitized_real', nil)
assert_equal command, 'cp /dev/null /tmp/ifcfg-$sanitized_real'
end

test "Should create a correct script even when indented" do
tmp_file = Tempfile.new('save_to_file')
tmp_file.close

file_content = File.read(Rails.root.join('test', 'static_fixtures', 'script.txt'))

command = @scope.indent(2) { @scope.save_to_file(tmp_file.path, file_content) }

system(command)

actual = `cat #{tmp_file.path}`

assert_equal file_content, actual
ensure
tmp_file.unlink
end

test "Should interpolate ENV values to the script" do
tmp_file = Tempfile.new('save_to_file')
tmp_file.close

file_content = File.read(Rails.root.join('test', 'static_fixtures', 'interpolated_text.txt'))

command = @scope.indent(2) { @scope.save_to_file(tmp_file.path, file_content) }

system({'INTERPOLATED' => 'test_value_123'}, command)

actual = `cat #{tmp_file.path}`

assert_match /test_value_123/, actual
ensure
tmp_file.unlink
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,12 @@ echo "Performing initial puppet run for --tags no_such_tag"



cat << EOF-9f037ba1 > /root/ansible_provisioning_call.sh
#!/bin/sh

echo "Calling Ansible AWX/Tower provisioning callback..."
/usr/bin/curl -v -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
echo "DONE"
EOF-9f037ba1
cat /dev/null > /root/ansible_provisioning_call.sh
echo "#!/bin/sh" >> /root/ansible_provisioning_call.sh
echo "" >> /root/ansible_provisioning_call.sh
echo "echo \"Calling Ansible AWX/Tower provisioning callback...\"" >> /root/ansible_provisioning_call.sh
echo "/usr/bin/curl -v -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /root/ansible_provisioning_call.sh
echo "echo \"DONE\"" >> /root/ansible_provisioning_call.sh
chmod +x /root/ansible_provisioning_call.sh
/root/ansible_provisioning_call.sh

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,19 @@ allow-hotplug $real
iface $real inet dhcp
EOF

cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback
if [ -x /usr/bin/curl ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,19 @@ allow-hotplug $real
iface $real inet dhcp
EOF

cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback
if [ -x /usr/bin/curl ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,19 @@ echo "Performing initial puppet run for --tags no_such_tag"



cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,19 @@ echo "Performing initial puppet run for --tags no_such_tag"



cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,19 @@ echo "Performing initial puppet run for --tags no_such_tag"



cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,19 @@ echo "Performing initial puppet run for --tags no_such_tag"



cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,19 @@ echo "Performing initial puppet run for --tags no_such_tag"



cat << EOF-2929810d > /etc/systemd/system/ansible-callback.service
[Unit]
Description=Provisioning callback to Ansible Tower
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/curl -k -s --data "host_config_key=" https:///api/v2/job_templates//callback/
ExecStartPost=/usr/bin/systemctl disable ansible-callback

[Install]
WantedBy=multi-user.target
EOF-2929810d
cat /dev/null > /etc/systemd/system/ansible-callback.service
echo "[Unit]" >> /etc/systemd/system/ansible-callback.service
echo "Description=Provisioning callback to Ansible Tower" >> /etc/systemd/system/ansible-callback.service
echo "Wants=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "After=network-online.target" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Service]" >> /etc/systemd/system/ansible-callback.service
echo "Type=oneshot" >> /etc/systemd/system/ansible-callback.service
echo "ExecStart=/usr/bin/curl -k -s --data \"host_config_key=\" https:///api/v2/job_templates//callback/" >> /etc/systemd/system/ansible-callback.service
echo "ExecStartPost=/usr/bin/systemctl disable ansible-callback" >> /etc/systemd/system/ansible-callback.service
echo "" >> /etc/systemd/system/ansible-callback.service
echo "[Install]" >> /etc/systemd/system/ansible-callback.service
echo "WantedBy=multi-user.target" >> /etc/systemd/system/ansible-callback.service
# Runs during first boot, removes itself
systemctl enable ansible-callback

Expand Down
Loading
Loading