Skip to content

Commit 1e71172

Browse files
authored
Merge pull request #7840 from binford2k/find_template_function
(PUP-10139) Add find_template function
2 parents 9630bd7 + f070b8e commit 1e71172

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

lib/puppet/functions/find_template.rb

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Finds an existing template from a module and returns its path.
2+
#
3+
# This function accepts an argument that is a String as a `<MODULE NAME>/<TEMPLATE>`
4+
# reference, which searches for `<TEMPLATE>` relative to a module's `templates`
5+
# directory on the master. (For example, the reference `mymod/secret.conf.epp`
6+
# will search for the file `<MODULES DIRECTORY>/mymod/templates/secret.conf.epp`.)
7+
#
8+
# The primary use case is for agent-side template rendering with late-bound variables
9+
# resolved, such as from secret stores inaccessible to the master, such as
10+
#
11+
# ```
12+
# $variables = {
13+
# 'password' => Deferred('vault_lookup::lookup',
14+
# ['secret/mymod', 'https://vault.example.com:8200']),
15+
# }
16+
#
17+
# # compile the template source into the catalog
18+
# file { '/etc/secrets.conf':
19+
# ensure => file,
20+
# content => Deferred('inline_epp',
21+
# [find_template('mymod/secret.conf.epp').file, $variables]),
22+
# }
23+
# ```
24+
#
25+
#
26+
#
27+
# This function can also accept:
28+
#
29+
# * An absolute String path, which checks for the existence of a template from anywhere on disk.
30+
# * Multiple String arguments, which returns the path of the **first** template
31+
# found, skipping nonexistent files.
32+
# * An array of string paths, which returns the path of the **first** template
33+
# found from the given paths in the array, skipping nonexistent files.
34+
#
35+
# The function returns `undef` if none of the given paths were found.
36+
#
37+
# @since 6.x
38+
#
39+
Puppet::Functions.create_function(:find_template, Puppet::Functions::InternalFunction) do
40+
dispatch :find_template do
41+
scope_param
42+
repeated_param 'String', :paths
43+
end
44+
45+
dispatch :find_template_array do
46+
scope_param
47+
repeated_param 'Array[String]', :paths_array
48+
end
49+
50+
def find_template_array(scope, array)
51+
find_template(scope, *array)
52+
end
53+
54+
def find_template(scope, *args)
55+
args.each do |file|
56+
found = Puppet::Parser::Files.find_template(file, scope.compiler.environment)
57+
if found && Puppet::FileSystem.exist?(found)
58+
return found
59+
end
60+
end
61+
nil
62+
end
63+
end
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
require 'spec_helper'
2+
require 'puppet_spec/compiler'
3+
require 'matchers/resource'
4+
require 'puppet_spec/files'
5+
6+
describe 'the find_template function' do
7+
include PuppetSpec::Compiler
8+
include Matchers::Resource
9+
include PuppetSpec::Files
10+
11+
def with_file_content(content)
12+
path = tmpfile('find-file-function')
13+
file = File.new(path, 'wb')
14+
file.sync = true
15+
file.print content
16+
yield path
17+
end
18+
19+
it 'finds an existing absolute file when given arguments individually' do
20+
with_file_content('one') do |one|
21+
with_file_content('two') do |two|
22+
expect(compile_to_catalog("notify { find_template('#{one}', '#{two}'):}")).to have_resource("Notify[#{one}]")
23+
end
24+
end
25+
end
26+
27+
it 'skips non existing files' do
28+
with_file_content('one') do |one|
29+
with_file_content('two') do |two|
30+
expect(compile_to_catalog("notify { find_template('#{one}/nope', '#{two}'):}")).to have_resource("Notify[#{two}]")
31+
end
32+
end
33+
end
34+
35+
it 'accepts arguments given as an array' do
36+
with_file_content('one') do |one|
37+
with_file_content('two') do |two|
38+
expect(compile_to_catalog("notify { find_template(['#{one}', '#{two}']):}")).to have_resource("Notify[#{one}]")
39+
end
40+
end
41+
end
42+
43+
it 'finds an existing file in a module' do
44+
with_file_content('file content') do |name|
45+
mod = double('module')
46+
allow(mod).to receive(:template).with('myfile').and_return(name)
47+
Puppet[:code] = "notify { find_template('mymod/myfile'):}"
48+
node = Puppet::Node.new('localhost')
49+
compiler = Puppet::Parser::Compiler.new(node)
50+
allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)
51+
52+
expect(compiler.compile().filter { |r| r.virtual? }).to have_resource("Notify[#{name}]")
53+
end
54+
end
55+
56+
it 'returns undef when none of the paths were found' do
57+
mod = double('module')
58+
allow(mod).to receive(:template).with('myfile').and_return(nil)
59+
Puppet[:code] = "notify { String(type(find_template('mymod/myfile', 'nomod/nofile'))):}"
60+
node = Puppet::Node.new('localhost')
61+
compiler = Puppet::Parser::Compiler.new(node)
62+
# For a module that does not have the file
63+
allow(compiler.environment).to receive(:module).with('mymod').and_return(mod)
64+
# For a module that does not exist
65+
allow(compiler.environment).to receive(:module).with('nomod').and_return(nil)
66+
67+
expect(compiler.compile().filter { |r| r.virtual? }).to have_resource("Notify[Undef]")
68+
end
69+
end

0 commit comments

Comments
 (0)