lib/bora/parameter_resolver.rb in bora-1.6.0 vs lib/bora/parameter_resolver.rb in bora-1.7.0
- old
+ new
@@ -3,11 +3,14 @@
class Bora
class ParameterResolver
UnresolvedSubstitutionError = Class.new(StandardError)
- PLACEHOLDER_REGEX = /\${[^}]+}/
+ # Regular expression that can match placeholders nested to two levels.
+ # For example it will match: "${foo-${bar}}".
+ # See https://stackoverflow.com/questions/17759004/how-to-match-string-within-parentheses-nested-in-java
+ PLACEHOLDER_REGEX = /\${([^{}]*|{[^{}]*})*}/
def initialize(stack)
@stack = stack
@loader = ParameterResolverLoader.new
@resolver_cache = {}
@@ -18,28 +21,33 @@
while unresolved_placeholders_still_remain
unresolved_placeholders_still_remain = false
placeholders_were_substituted = false
params.each do |k, v|
resolved_value = process_param_substitutions(v, params)
- unresolved_placeholders_still_remain ||= has_unresolved_placeholder?(resolved_value)
+ unresolved_placeholders_still_remain ||= unresolved_placeholder?(resolved_value)
placeholders_were_substituted ||= resolved_value != v
params[k] = resolved_value
end
if unresolved_placeholders_still_remain && !placeholders_were_substituted
raise UnresolvedSubstitutionError, "Parameter substitutions could not be resolved:\n#{unresolved_placeholders_as_string(params)}"
end
end
params
end
-
private
def process_param_substitutions(val, params)
result = val
if val.is_a? String
result = val.gsub(PLACEHOLDER_REGEX) do |placeholder|
+ # Handle nested substitutions, like "${foo-${bar}}
+ if unresolved_placeholder?(placeholder)
+ placeholder_contents = placeholder[2..-2]
+ placeholder = "${#{process_param_substitutions(placeholder_contents, params)}}"
+ end
+
process_placeholder(placeholder, params)
end
elsif val.is_a? Array
result = val.map { |i| process_param_substitutions(i, params) }
elsif val.is_a? Hash
@@ -51,43 +59,42 @@
def process_placeholder(placeholder, params)
uri = parse_uri(placeholder[2..-2])
if !uri.scheme
# This token refers to another parameter, rather than a resolver
value_to_substitute = params[uri.path]
- return !value_to_substitute || has_unresolved_placeholder?(value_to_substitute) ? placeholder : value_to_substitute
+ return !value_to_substitute || unresolved_placeholder?(value_to_substitute) ? placeholder : value_to_substitute
else
# This token needs to be resolved by a resolver
resolver_name = uri.scheme
resolver = @resolver_cache[resolver_name] || @loader.load_resolver(resolver_name).new(@stack)
return resolver.resolve(uri)
end
end
- def has_unresolved_placeholder?(val)
+ def unresolved_placeholder?(val)
result = false
if val.is_a? String
result = val =~ PLACEHOLDER_REGEX
elsif val.is_a? Array
- result = val.find { |i| has_unresolved_placeholder?(i) }
+ result = val.find { |i| unresolved_placeholder?(i) }
elsif val.is_a? Hash
- result = val.find { |_, v| has_unresolved_placeholder?(v) }
+ result = val.find { |_, v| unresolved_placeholder?(v) }
end
result
end
def parse_uri(s)
uri = URI(s)
# Support for legacy CFN substitutions without a scheme, eg: ${stack/outputs/foo}.
# Will be removed in next breaking version.
- if !uri.scheme && uri.path && uri.path.count("/") == 2
+ if !uri.scheme && uri.path && uri.path.count('/') == 2
uri = URI("cfn://#{s}")
end
uri
end
def unresolved_placeholders_as_string(params)
- params.select { |k, v| has_unresolved_placeholder?(v) }.to_a.map { |k, v| "#{k}: #{v}" }.join("\n")
+ params.select { |_k, v| unresolved_placeholder?(v) }.to_a.map { |k, v| "#{k}: #{v}" }.join("\n")
end
-
end
end