lono 1.1.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/CHANGELOG.md +8 -0
- data/README.md +150 -39
- data/bin/lono +2 -2
- data/circle.yml +4 -0
- data/lib/lono.rb +16 -7
- data/lib/lono/cfn.rb +64 -0
- data/lib/lono/cfn/aws_services.rb +37 -0
- data/lib/lono/cfn/base.rb +144 -0
- data/lib/lono/cfn/create.rb +34 -0
- data/lib/lono/cfn/delete.rb +26 -0
- data/lib/lono/cfn/diff.rb +43 -0
- data/lib/lono/cfn/help.rb +93 -0
- data/lib/lono/cfn/preview.rb +133 -0
- data/lib/lono/cfn/update.rb +62 -0
- data/lib/lono/cfn/util.rb +21 -0
- data/lib/lono/cli.rb +19 -10
- data/lib/lono/command.rb +25 -0
- data/lib/lono/help.rb +59 -0
- data/lib/lono/new.rb +3 -2
- data/lib/lono/param.rb +20 -0
- data/lib/lono/param/generator.rb +90 -0
- data/lib/lono/param/help.rb +15 -0
- data/lib/lono/project_checker.rb +44 -0
- data/lib/lono/template.rb +22 -248
- data/lib/lono/template/bashify.rb +39 -0
- data/lib/lono/template/dsl.rb +139 -0
- data/lib/lono/template/help.rb +25 -0
- data/lib/lono/template/template.rb +251 -0
- data/lib/lono/version.rb +1 -1
- data/lib/{starter_project_yaml → starter_projects/json_project}/Gemfile +0 -1
- data/lib/{starter_project_json → starter_projects/json_project}/Guardfile +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/config/lono.rb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/config/lono/api.rb +0 -0
- data/lib/starter_projects/json_project/params/api-web-prod.txt +20 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/db.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/partial/host_record.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/partial/server.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/app.sh.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/db.sh.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/db2.sh.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/user_data/ruby_script.rb.erb +0 -0
- data/lib/{starter_project_json → starter_projects/json_project}/templates/web.json.erb +0 -0
- data/lib/{starter_project_json → starter_projects/yaml_project}/Gemfile +0 -1
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/Guardfile +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/config/lono.rb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/config/lono/api.rb +0 -0
- data/lib/starter_projects/yaml_project/params/api-web-prod.txt +20 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/db.yml.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/host_record.yml.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/server.yml.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/partial/user_data/bootstrap.sh.erb +0 -0
- data/lib/{starter_project_yaml → starter_projects/yaml_project}/templates/web.yml.erb +0 -0
- data/lono.gemspec +15 -10
- data/spec/fixtures/my_project/config/lono.rb +1 -0
- data/spec/fixtures/my_project/params/my-stack.txt +3 -0
- data/spec/fixtures/my_project/templates/.gitkeep +0 -0
- data/spec/fixtures/my_project/templates/my-stack.yml.erb +0 -0
- data/spec/lib/lono/cfn_spec.rb +35 -0
- data/spec/lib/lono/new_spec.rb +3 -3
- data/spec/lib/lono/param_spec.rb +15 -0
- data/spec/lib/lono/{dsl_spec.rb → template/dsl_spec.rb} +9 -9
- data/spec/lib/lono/template/template_spec.rb +104 -0
- data/spec/lib/lono/template_spec.rb +22 -37
- data/spec/lib/lono_spec.rb +6 -83
- data/vendor/plissken/Gemfile +14 -0
- data/vendor/plissken/LICENSE.txt +20 -0
- data/vendor/plissken/README.md +46 -0
- data/vendor/plissken/Rakefile +56 -0
- data/vendor/plissken/VERSION +1 -0
- data/vendor/plissken/lib/plissken.rb +1 -0
- data/vendor/plissken/lib/plissken/ext/hash/to_snake_keys.rb +45 -0
- data/vendor/plissken/plissken.gemspec +61 -0
- data/vendor/plissken/spec/lib/to_snake_keys_spec.rb +177 -0
- data/vendor/plissken/spec/spec_helper.rb +90 -0
- data/vendor/plissken/test/helper.rb +20 -0
- data/vendor/plissken/test/plissken/ext/hash/to_snake_keys_test.rb +184 -0
- data/vendor/plissken/test/test_plissken.rb +2 -0
- metadata +115 -39
- data/lib/lono/bashify.rb +0 -41
- data/lib/lono/cli/help.rb +0 -37
- data/lib/lono/dsl.rb +0 -132
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
class Lono::Template::Bashify
|
4
|
+
def initialize(options={})
|
5
|
+
@options = options
|
6
|
+
@path = options[:path]
|
7
|
+
end
|
8
|
+
|
9
|
+
def user_data_paths(data,path="")
|
10
|
+
paths = []
|
11
|
+
paths << path
|
12
|
+
data.each do |key,value|
|
13
|
+
if value.is_a?(Hash)
|
14
|
+
paths += user_data_paths(value,"#{path}/#{key}")
|
15
|
+
else
|
16
|
+
paths += ["#{path}/#{key}"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
paths.select {|p| p =~ /UserData/ && p =~ /Fn::Join/ }
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
raw = open(@path).read
|
24
|
+
json = JSON.load(raw)
|
25
|
+
paths = user_data_paths(json)
|
26
|
+
if paths.empty?
|
27
|
+
puts "No UserData script found"
|
28
|
+
return
|
29
|
+
end
|
30
|
+
paths.each do |path|
|
31
|
+
puts "UserData script for #{path}:"
|
32
|
+
key = path.sub('/','').split("/").map {|x| "['#{x}']"}.join('')
|
33
|
+
user_data = eval("json#{key}")
|
34
|
+
delimiter = user_data[0]
|
35
|
+
script = user_data[1]
|
36
|
+
puts script.join(delimiter)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
class Lono::Template::DSL
|
2
|
+
def initialize(options={})
|
3
|
+
@options = options
|
4
|
+
@project_root = @options[:project_root] || '.'
|
5
|
+
@path = "#{@project_root}/config/lono.rb"
|
6
|
+
Lono::ProjectChecker.check(@project_root)
|
7
|
+
@templates = []
|
8
|
+
@results = {}
|
9
|
+
@detected_format = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(options={})
|
13
|
+
evaluate_templates
|
14
|
+
build_templates
|
15
|
+
write_output
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate_templates
|
19
|
+
instance_eval(File.read(@path), @path)
|
20
|
+
load_subfolder
|
21
|
+
@detected_format = detect_format
|
22
|
+
end
|
23
|
+
|
24
|
+
# Detects the format of the templates. Simply checks the extension of all the
|
25
|
+
# templates files.
|
26
|
+
# All the templates must be of the same format, either all json or all yaml.
|
27
|
+
def detect_format
|
28
|
+
# @templates contains Array of Hashes. Example:
|
29
|
+
# [{name: ""blog-web-prod.json", block: ...},
|
30
|
+
# {name: ""api-web-prod.json", block: ...}]
|
31
|
+
formats = @templates.map{ |t| File.extname(t[:name]) }.uniq
|
32
|
+
if formats.size > 1
|
33
|
+
puts "ERROR: Detected multiple formats: #{formats.join(", ")}".colorize(:red)
|
34
|
+
puts "All the source values in the template blocks in the config folder must have the same format extension."
|
35
|
+
exit 1
|
36
|
+
else
|
37
|
+
found_format = formats.first
|
38
|
+
if found_format
|
39
|
+
detected_format = found_format.sub(/^\./,'')
|
40
|
+
detected_format = "yaml" if detected_format == "yml"
|
41
|
+
else # empty templates, no templates defined yet
|
42
|
+
detected_format = "yaml" # defaults to yaml
|
43
|
+
end
|
44
|
+
end
|
45
|
+
detected_format
|
46
|
+
end
|
47
|
+
|
48
|
+
# load any templates defined in project/config/lono/*
|
49
|
+
def load_subfolder
|
50
|
+
Dir.glob("#{File.dirname(@path)}/lono/**/*").select{ |e| File.file? e }.each do |path|
|
51
|
+
instance_eval(File.read(path), path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def template(name, &block)
|
56
|
+
@templates << {name: name, block: block}
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_templates
|
60
|
+
@templates.each do |t|
|
61
|
+
@results[t[:name]] = Lono::Template::Template.new(t[:name], t[:block], @options).build
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def write_output
|
66
|
+
output_path = "#{@project_root}/output"
|
67
|
+
FileUtils.rm_rf(output_path) if @options[:clean]
|
68
|
+
FileUtils.mkdir(output_path) unless File.exist?(output_path)
|
69
|
+
puts "Generating CloudFormation templates:" unless @options[:quiet]
|
70
|
+
@results.each do |name,text|
|
71
|
+
path = "#{output_path}/#{name}".sub(/^\.\//,'')
|
72
|
+
puts " #{path}" unless @options[:quiet]
|
73
|
+
ensure_parent_dir(path)
|
74
|
+
validate(text, path)
|
75
|
+
File.open(path, 'w') do |f|
|
76
|
+
f.write(output_format(text))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# TODO: set @detected_format upon DSL.new
|
82
|
+
def validate(text, path)
|
83
|
+
if @detected_format == "json"
|
84
|
+
validate_json(text, path)
|
85
|
+
else
|
86
|
+
validate_yaml(text, path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_yaml(yaml, path)
|
91
|
+
begin
|
92
|
+
YAML.load(yaml)
|
93
|
+
rescue Psych::SyntaxError => e
|
94
|
+
puts "Invalid yaml. Output written to #{path} for debugging".colorize(:red)
|
95
|
+
puts "ERROR: #{e.message}".colorize(:red)
|
96
|
+
File.open(path, 'w') {|f| f.write(yaml) }
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def validate_json(json, path)
|
102
|
+
begin
|
103
|
+
JSON.parse(json)
|
104
|
+
rescue JSON::ParserError => e
|
105
|
+
puts "Invalid json. Output written to #{path} for debugging".colorize(:red)
|
106
|
+
puts "ERROR: #{e.message}".colorize(:red)
|
107
|
+
File.open(path, 'w') {|f| f.write(json) }
|
108
|
+
exit 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def output_format(text)
|
113
|
+
@options[:pretty] ? prettify(text) : text
|
114
|
+
end
|
115
|
+
|
116
|
+
# Input text is either yaml or json.
|
117
|
+
# Do not prettify yaml format because it removes the !Ref like CloudFormation notation
|
118
|
+
def prettify(text)
|
119
|
+
@detected_format == "json" ? JSON.pretty_generate(JSON.parse(text)) : yaml_format(text)
|
120
|
+
end
|
121
|
+
|
122
|
+
def yaml_format(text)
|
123
|
+
comment =<<~EOS
|
124
|
+
# This file was generated with lono. Do not edit directly, the changes will be lost.
|
125
|
+
# More info: https://github.com/tongueroo/lono
|
126
|
+
EOS
|
127
|
+
"#{comment}#{remove_blank_lines(text)}"
|
128
|
+
end
|
129
|
+
|
130
|
+
# ERB templates leaves blank lines around, remove those lines
|
131
|
+
def remove_blank_lines(text)
|
132
|
+
text.split("\n").reject { |l| l.strip == '' }.join("\n") + "\n"
|
133
|
+
end
|
134
|
+
|
135
|
+
def ensure_parent_dir(path)
|
136
|
+
dir = File.dirname(path)
|
137
|
+
FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lono::Template::Help
|
2
|
+
def generate
|
3
|
+
<<-EOL
|
4
|
+
Examples:
|
5
|
+
|
6
|
+
$ lono template generate
|
7
|
+
|
8
|
+
$ lono template g -c # shortcut
|
9
|
+
|
10
|
+
Builds the CloudFormation templates files based on lono project and writes them to the output folder on the filesystem.
|
11
|
+
EOL
|
12
|
+
end
|
13
|
+
|
14
|
+
def bashify
|
15
|
+
<<-EOL
|
16
|
+
Examples:
|
17
|
+
|
18
|
+
$ lono template bashify /path/to/cloudformation-template.json
|
19
|
+
|
20
|
+
$ lono template bashify https://s3.amazonaws.com/cloudformation-templates-us-east-1/EC2WebSiteSample.template
|
21
|
+
EOL
|
22
|
+
end
|
23
|
+
|
24
|
+
extend self
|
25
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'json'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class Lono::Template::Template
|
6
|
+
include ERB::Util
|
7
|
+
|
8
|
+
attr_reader :name
|
9
|
+
def initialize(name, block, options={})
|
10
|
+
@name = name
|
11
|
+
@block = block
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def build
|
16
|
+
instance_eval(&@block)
|
17
|
+
template = IO.read(@source)
|
18
|
+
erb_result(@source, template)
|
19
|
+
end
|
20
|
+
|
21
|
+
def source(path)
|
22
|
+
@source = path[0..0] == '/' ? path : "#{@options[:project_root]}/templates/#{path}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def variables(vars={})
|
26
|
+
vars.each do |var,value|
|
27
|
+
instance_variable_set("@#{var}", value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def partial(path,vars={}, options={})
|
32
|
+
path = "#{@options[:project_root]}/templates/partial/#{path}"
|
33
|
+
template = IO.read(path)
|
34
|
+
variables(vars)
|
35
|
+
result = erb_result(path, template)
|
36
|
+
result = indent(result, options[:indent]) if options[:indent]
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
# add indentation
|
41
|
+
def indent(result, indentation_amount)
|
42
|
+
result.split("\n").map do |line|
|
43
|
+
" " * indentation_amount + line
|
44
|
+
end.join("\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
def erb_result(path, template)
|
48
|
+
begin
|
49
|
+
ERB.new(template, nil, "-").result(binding)
|
50
|
+
rescue Exception => e
|
51
|
+
puts e
|
52
|
+
|
53
|
+
# how to know where ERB stopped? - https://www.ruby-forum.com/topic/182051
|
54
|
+
# syntax errors have the (erb):xxx info in e.message
|
55
|
+
# undefined variables have (erb):xxx info in e.backtrac
|
56
|
+
error_info = e.message.split("\n").grep(/\(erb\)/)[0]
|
57
|
+
error_info ||= e.backtrace.grep(/\(erb\)/)[0]
|
58
|
+
raise unless error_info # unable to find the (erb):xxx: error line
|
59
|
+
line = error_info.split(':')[1].to_i
|
60
|
+
puts "Error evaluating ERB template on line #{line.to_s.colorize(:red)} of: #{path.sub(/^\.\//, '')}"
|
61
|
+
|
62
|
+
template_lines = template.split("\n")
|
63
|
+
context = 5 # lines of context
|
64
|
+
top, bottom = [line-context-1, 0].max, line+context-1
|
65
|
+
spacing = template_lines.size.to_s.size
|
66
|
+
template_lines[top..bottom].each_with_index do |line_content, index|
|
67
|
+
line_number = top+index+1
|
68
|
+
if line_number == line
|
69
|
+
printf("%#{spacing}d %s\n".colorize(:red), line_number, line_content)
|
70
|
+
else
|
71
|
+
printf("%#{spacing}d %s\n", line_number, line_content)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
exit 1 unless ENV['TEST']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def user_data(path, vars={})
|
79
|
+
path = "#{@options[:project_root]}/templates/user_data/#{path}"
|
80
|
+
template = IO.read(path)
|
81
|
+
variables(vars)
|
82
|
+
result = erb_result(path, template)
|
83
|
+
output = []
|
84
|
+
result.split("\n").each do |line|
|
85
|
+
output += transform(line)
|
86
|
+
end
|
87
|
+
json = output.to_json
|
88
|
+
json[0] = '' # remove first char: [
|
89
|
+
json.chop! # remove last char: ]
|
90
|
+
end
|
91
|
+
|
92
|
+
def ref(name)
|
93
|
+
%Q|{"Ref"=>"#{name}"}|
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_in_map(*args)
|
97
|
+
%Q|{"Fn::FindInMap" => [ #{transform_array(args)} ]}|
|
98
|
+
end
|
99
|
+
|
100
|
+
def base64(value)
|
101
|
+
%Q|{"Fn::Base64"=>"#{value}"}|
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_att(*args)
|
105
|
+
%Q|{"Fn::GetAtt" => [ #{transform_array(args)} ]}|
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_azs(region="AWS::Region")
|
109
|
+
%Q|{"Fn::GetAZs"=>"#{region}"}|
|
110
|
+
end
|
111
|
+
|
112
|
+
def join(delimiter, values)
|
113
|
+
%Q|{"Fn::Join" => ["#{delimiter}", [ #{transform_array(values)} ]]}|
|
114
|
+
end
|
115
|
+
|
116
|
+
def select(index, list)
|
117
|
+
%Q|{"Fn::Select" => ["#{index}", [ #{transform_array(list)} ]]}|
|
118
|
+
end
|
119
|
+
|
120
|
+
def transform_array(arr)
|
121
|
+
arr.map! {|x| x =~ /=>/ ? x : x.inspect }
|
122
|
+
arr.join(',')
|
123
|
+
end
|
124
|
+
|
125
|
+
# transform each line of bash script to array with cloudformation template objects
|
126
|
+
def transform(data)
|
127
|
+
data = evaluate(data)
|
128
|
+
if data[-1].is_a?(String)
|
129
|
+
data[0..-2] + ["#{data[-1]}\n"]
|
130
|
+
else
|
131
|
+
data + ["\n"]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Input:
|
136
|
+
# String
|
137
|
+
# Output:
|
138
|
+
# Array of parse positions
|
139
|
+
#
|
140
|
+
# The positions of tokens taking into account when brackets start and close,
|
141
|
+
# handles nested brackets.
|
142
|
+
def bracket_positions(line)
|
143
|
+
positions,pair,count = [],[],0
|
144
|
+
|
145
|
+
line.split('').each_with_index do |char,i|
|
146
|
+
pair << i if pair.empty?
|
147
|
+
|
148
|
+
first_pair_char = line[pair[0]]
|
149
|
+
if first_pair_char == '{' # object logic
|
150
|
+
if char == '{'
|
151
|
+
count += 1
|
152
|
+
end
|
153
|
+
|
154
|
+
if char == '}'
|
155
|
+
count -= 1
|
156
|
+
if count == 0
|
157
|
+
pair << i
|
158
|
+
positions << pair
|
159
|
+
pair = []
|
160
|
+
end
|
161
|
+
end
|
162
|
+
else # string logic
|
163
|
+
lookahead = line[i+1]
|
164
|
+
if lookahead == '{'
|
165
|
+
pair << i
|
166
|
+
positions << pair
|
167
|
+
pair = []
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end # end of loop
|
171
|
+
|
172
|
+
# for string logic when lookahead does not contain a object token
|
173
|
+
# need to clear out what's left to match the final pair
|
174
|
+
if !pair.empty?
|
175
|
+
pair << line.size - 1
|
176
|
+
positions << pair
|
177
|
+
end
|
178
|
+
|
179
|
+
positions
|
180
|
+
end
|
181
|
+
|
182
|
+
# Input:
|
183
|
+
# Array - bracket_positions
|
184
|
+
# Ouput:
|
185
|
+
# Array - positions that can be use to determine what to parse
|
186
|
+
def parse_positions(line)
|
187
|
+
positions = bracket_positions(line)
|
188
|
+
positions.flatten
|
189
|
+
end
|
190
|
+
|
191
|
+
# Input
|
192
|
+
# String line of code to decompose into chunks, some can be transformed into objects
|
193
|
+
# Output
|
194
|
+
# Array of strings, some can be transformed into objects
|
195
|
+
#
|
196
|
+
# Example:
|
197
|
+
# line = 'a{b}c{d{d}d}e' # nested brackets
|
198
|
+
# template.decompose(line).should == ['a','{b}','c','{d{d}d}','e']
|
199
|
+
def decompose(line)
|
200
|
+
positions = parse_positions(line)
|
201
|
+
return [line] if positions.empty?
|
202
|
+
|
203
|
+
result = []
|
204
|
+
str = ''
|
205
|
+
until positions.empty?
|
206
|
+
left = positions.shift
|
207
|
+
right = positions.shift
|
208
|
+
token = line[left..right]
|
209
|
+
# if cfn object, add to the result set but after clearing out
|
210
|
+
# the temp str that is being built up when the token is just a string
|
211
|
+
if cfn_object?(token)
|
212
|
+
unless str.empty? # first token might be a object
|
213
|
+
result << str
|
214
|
+
str = ''
|
215
|
+
end
|
216
|
+
result << token
|
217
|
+
else
|
218
|
+
str << token # keeps building up the string
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# at the of the loop there's a leftover string, unless the last token
|
223
|
+
# is an object
|
224
|
+
result << str unless str.empty?
|
225
|
+
|
226
|
+
result
|
227
|
+
end
|
228
|
+
|
229
|
+
def cfn_object?(s)
|
230
|
+
exact = %w[Ref]
|
231
|
+
pattern = %w[Fn::]
|
232
|
+
exact_match = !!exact.detect {|word| s.include?(word)}
|
233
|
+
pattern_match = !!pattern.detect {|p| s =~ Regexp.new(p)}
|
234
|
+
(exact_match || pattern_match) && s =~ /^{/ && s =~ /=>/
|
235
|
+
end
|
236
|
+
|
237
|
+
def recompose(decomposition)
|
238
|
+
decomposition.map { |s| cfn_object?(s) ? eval(s) : s }
|
239
|
+
end
|
240
|
+
|
241
|
+
def evaluate(line)
|
242
|
+
recompose(decompose(line))
|
243
|
+
end
|
244
|
+
|
245
|
+
# For simple just parameters files that can also be generated with lono, the CFN
|
246
|
+
# Fn::Base64 function is not available and as lono is not being used in the context
|
247
|
+
# of CloudFormation. So this can be used in it's place.
|
248
|
+
def encode_base64(text)
|
249
|
+
Base64.strict_encode64(text).strip
|
250
|
+
end
|
251
|
+
end
|