cfhighlander 0.2.0.alpha.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +411 -0
- data/bin/cfhighlander +3 -0
- data/bin/highlander.rb +158 -0
- data/cfndsl_ext/config/managed_policies.yaml +172 -0
- data/cfndsl_ext/iam_helper.rb +55 -0
- data/cfndsl_ext/lambda_helper.rb +86 -0
- data/cfndsl_ext/sg.rb +26 -0
- data/hl_ext/aws_helper.rb +13 -0
- data/hl_ext/common_helper.rb +21 -0
- data/hl_ext/mapping_helper.rb +99 -0
- data/hl_ext/vpc.rb +12 -0
- data/lib/highlander.compiler.rb +306 -0
- data/lib/highlander.dsl.base.rb +25 -0
- data/lib/highlander.dsl.component.rb +243 -0
- data/lib/highlander.dsl.params.rb +113 -0
- data/lib/highlander.dsl.rb +428 -0
- data/lib/highlander.factory.rb +359 -0
- data/lib/highlander.helper.rb +23 -0
- data/lib/highlander.mapproviders.rb +31 -0
- data/lib/highlander.publisher.rb +71 -0
- data/lib/highlander.validator.rb +72 -0
- data/lib/util/zip.util.rb +57 -0
- data/templates/cfndsl.component.template.erb +45 -0
- metadata +248 -0
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'cfndsl'
|
2
|
+
require 'erb'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'cfndsl/globals'
|
5
|
+
require 'cfndsl/version'
|
6
|
+
require 'json'
|
7
|
+
require 'yaml'
|
8
|
+
require 'open-uri'
|
9
|
+
require 'net/http'
|
10
|
+
require 'net/https'
|
11
|
+
require 'highline/import'
|
12
|
+
require 'zip'
|
13
|
+
require_relative './util/zip.util'
|
14
|
+
|
15
|
+
module Highlander
|
16
|
+
|
17
|
+
module Compiler
|
18
|
+
|
19
|
+
class ComponentCompiler
|
20
|
+
|
21
|
+
@@global_extensions_paths = []
|
22
|
+
|
23
|
+
attr_accessor :workdir,
|
24
|
+
:component,
|
25
|
+
:compiled_subcomponents,
|
26
|
+
:component_name,
|
27
|
+
:config_output_location,
|
28
|
+
:dsl_output_location,
|
29
|
+
:cfn_output_location,
|
30
|
+
:cfn_template_paths,
|
31
|
+
:silent_mode,
|
32
|
+
:lambda_src_paths
|
33
|
+
|
34
|
+
def initialize(component)
|
35
|
+
|
36
|
+
@workdir = ENV['HIGHLANDER_WORKDIR']
|
37
|
+
@component = component
|
38
|
+
@sub_components = []
|
39
|
+
@component_name = component.highlander_dsl.name.downcase
|
40
|
+
@cfndsl_compiled = false
|
41
|
+
@config_compiled = false
|
42
|
+
@cfn_template_paths = []
|
43
|
+
@lambdas_processed = false
|
44
|
+
@silent_mode = false
|
45
|
+
@lambda_src_paths = []
|
46
|
+
|
47
|
+
if @@global_extensions_paths.empty?
|
48
|
+
global_extensions_folder = "#{File.dirname(__FILE__)}/../cfndsl_ext"
|
49
|
+
Dir["#{global_extensions_folder}/*.rb"].each { |f| @@global_extensions_paths << f }
|
50
|
+
end
|
51
|
+
|
52
|
+
@component.highlander_dsl.components.each do |sub_component|
|
53
|
+
sub_component_compiler = Highlander::Compiler::ComponentCompiler.new(sub_component.component_loaded)
|
54
|
+
sub_component_compiler.component_name = sub_component.name
|
55
|
+
@sub_components << sub_component_compiler
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def silent_mode=(value)
|
60
|
+
@silent_mode = value
|
61
|
+
@sub_components.each { |scc| scc.silent_mode=value }
|
62
|
+
end
|
63
|
+
|
64
|
+
def compileCfnDsl(out_format)
|
65
|
+
processLambdas unless @lambdas_processed
|
66
|
+
writeConfig unless @config_written
|
67
|
+
dsl = @component.highlander_dsl
|
68
|
+
component_cfndsl = @component.cfndsl_content
|
69
|
+
|
70
|
+
@component.highlander_dsl.components.each { |sc|
|
71
|
+
sc.distribution_format = out_format
|
72
|
+
}
|
73
|
+
|
74
|
+
# indent component cfndsl
|
75
|
+
component_cfndsl.gsub!("\n", "\n\t")
|
76
|
+
component_cfndsl.gsub!("\r\n", "\r\n\t")
|
77
|
+
# render cfndsl
|
78
|
+
renderer = ERB.new(File.read("#{__dir__}/../templates/cfndsl.component.template.erb"))
|
79
|
+
cfn_template = renderer.result(OpenStruct.new({
|
80
|
+
'dsl' => dsl,
|
81
|
+
'component_cfndsl' => component_cfndsl,
|
82
|
+
'component_requires' => (@@global_extensions_paths + @component.cfndsl_ext_files)
|
83
|
+
}).instance_eval { binding })
|
84
|
+
|
85
|
+
# write to output file
|
86
|
+
output_dir = "#{@workdir}/out/cfndsl"
|
87
|
+
@dsl_output_location = output_dir
|
88
|
+
output_path = "#{output_dir}/#{@component_name}.compiled.cfndsl.rb"
|
89
|
+
FileUtils.mkdir_p(output_dir) unless Dir.exist?(output_dir)
|
90
|
+
File.write(output_path, cfn_template)
|
91
|
+
puts "cfndsl template for #{dsl.name} written to #{output_path}"
|
92
|
+
@cfndsl_compiled_path = output_path
|
93
|
+
|
94
|
+
@sub_components.each { |subcomponent_compiler|
|
95
|
+
puts "Rendering sub-component cfndsl: #{subcomponent_compiler.component_name}"
|
96
|
+
subcomponent_compiler.compileCfnDsl out_format
|
97
|
+
}
|
98
|
+
|
99
|
+
@cfndsl_compiled = true
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def compileCloudFormation(format = 'yaml')
|
104
|
+
|
105
|
+
|
106
|
+
#compile cfndsl templates first
|
107
|
+
compileCfnDsl format unless @cfndsl_compiled
|
108
|
+
|
109
|
+
dsl = @component.highlander_dsl
|
110
|
+
component_cfndsl = @component.cfndsl_content
|
111
|
+
|
112
|
+
# create out dir if not there
|
113
|
+
@cfn_output_location = "#{@workdir}/out/#{format}"
|
114
|
+
output_dir = @cfn_output_location
|
115
|
+
FileUtils.mkdir_p(output_dir) unless Dir.exist?(output_dir)
|
116
|
+
|
117
|
+
# write config
|
118
|
+
config_yaml_path = writeConfig
|
119
|
+
|
120
|
+
|
121
|
+
# compile templates
|
122
|
+
output_path = "#{output_dir}/#{component_name}.compiled.#{format}"
|
123
|
+
@cfn_template_paths << output_path
|
124
|
+
# configure cfndsl
|
125
|
+
cfndsl_opts = []
|
126
|
+
cfndsl_opts.push([:yaml, config_yaml_path])
|
127
|
+
|
128
|
+
# grab cfndsl model
|
129
|
+
model = CfnDsl.eval_file_with_extras(@cfndsl_compiled_path, cfndsl_opts, false)
|
130
|
+
|
131
|
+
# write resulting cloud formation template
|
132
|
+
if format == 'json'
|
133
|
+
output_content = JSON.pretty_generate(model)
|
134
|
+
elsif format == 'yaml'
|
135
|
+
output_content = JSON.parse(model.to_json).to_yaml
|
136
|
+
else
|
137
|
+
raise StandardError, "#{format} not supported for cfn generation"
|
138
|
+
end
|
139
|
+
|
140
|
+
File.write(output_path, output_content)
|
141
|
+
# `cfndsl #{@cfndsl_compiled_path} -p -f #{format} -o #{output_path} --disable-binding`
|
142
|
+
puts "CloudFormation #{format.upcase} template for #{dsl.name} written to #{output_path}"
|
143
|
+
|
144
|
+
# compile sub-component templates
|
145
|
+
@sub_components.each do |sub_component|
|
146
|
+
sub_component.compileCloudFormation format
|
147
|
+
@cfn_template_paths += sub_component.cfn_template_paths
|
148
|
+
@lambda_src_paths += sub_component.lambda_src_paths
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
def writeConfig(write_subcomponents_config = false)
|
154
|
+
@config_output_location = "#{@workdir}/out/config"
|
155
|
+
config_yaml_path = "#{@config_output_location}/#{@component_name}.config.yaml"
|
156
|
+
FileUtils.mkdir_p(@config_output_location) unless Dir.exist?(@config_output_location)
|
157
|
+
|
158
|
+
File.write(config_yaml_path, @component.config.to_yaml)
|
159
|
+
puts "Config for #{@component.highlander_dsl.name} written to #{config_yaml_path}"
|
160
|
+
|
161
|
+
if write_subcomponents_config
|
162
|
+
# compile sub-component templates
|
163
|
+
@sub_components.each do |sub_component|
|
164
|
+
sub_component.writeConfig write_subcomponents_config
|
165
|
+
end
|
166
|
+
end
|
167
|
+
@config_written = true
|
168
|
+
config_yaml_path
|
169
|
+
end
|
170
|
+
|
171
|
+
def processLambdas()
|
172
|
+
|
173
|
+
@component.highlander_dsl.lambda_functions_keys.each do |lfk|
|
174
|
+
resolver = LambdaResolver.new(@component,
|
175
|
+
lfk,
|
176
|
+
@workdir,
|
177
|
+
(not @silent_mode)
|
178
|
+
)
|
179
|
+
@lambda_src_paths += resolver.generateSourceArchives
|
180
|
+
resolver.mergeComponentConfig
|
181
|
+
end
|
182
|
+
|
183
|
+
@lambdas_processed = true
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
class LambdaResolver
|
191
|
+
|
192
|
+
def initialize(component, lambda_key, workdir, confirm_code_execution = true)
|
193
|
+
@component = component
|
194
|
+
@lambda_config = @component.config[lambda_key]
|
195
|
+
@component_dir = @component.component_dir
|
196
|
+
@workdir = workdir
|
197
|
+
@metadata = {
|
198
|
+
'path' => {},
|
199
|
+
'sha256' => {},
|
200
|
+
'version' => {}
|
201
|
+
}
|
202
|
+
@confirm_code_execution = confirm_code_execution
|
203
|
+
end
|
204
|
+
|
205
|
+
def generateSourceArchives
|
206
|
+
|
207
|
+
# Clear previous packages
|
208
|
+
FileUtils.rmtree "#{@workdir}/output/lambdas"
|
209
|
+
|
210
|
+
archive_paths = []
|
211
|
+
|
212
|
+
# Cached downloads map
|
213
|
+
cached_downloads = {}
|
214
|
+
@lambda_config['functions'].each do |name, lambda_config|
|
215
|
+
# create folder
|
216
|
+
out_folder = "#{@workdir}/out/lambdas/"
|
217
|
+
timestamp = Time.now.utc.to_i.to_s
|
218
|
+
file_name = "#{name}.#{@component.name}.#{@component.version}.#{timestamp}.zip"
|
219
|
+
@metadata['path'][name] = file_name
|
220
|
+
full_destination_path = "#{out_folder}#{file_name}"
|
221
|
+
archive_paths << full_destination_path
|
222
|
+
FileUtils.mkdir_p out_folder
|
223
|
+
# clear destination if already there
|
224
|
+
FileUtils.remove full_destination_path if File.exist? full_destination_path
|
225
|
+
|
226
|
+
# download file if code remote archive
|
227
|
+
puts "Packaging AWS Lambda function #{name}...\n"
|
228
|
+
if lambda_config['code'].include? 'http'
|
229
|
+
md5 = Digest::MD5.new
|
230
|
+
md5.update lambda_config['code']
|
231
|
+
hash = md5.hexdigest
|
232
|
+
cached_location = "#{@workdir}/.cache/lambdas/#{hash}"
|
233
|
+
if cached_downloads.key? lambda_config['code']
|
234
|
+
puts "Using already downloaded archive #{lambda_config['code']}"
|
235
|
+
FileUtils.copy(cached_downloads[lambda_config['code']], full_destination_path)
|
236
|
+
elsif File.file? cached_location
|
237
|
+
puts "Using cached download - '#{cached_location}'"
|
238
|
+
FileUtils.copy(cached_location, "#{out_folder}/src.zip")
|
239
|
+
else
|
240
|
+
puts "Downloading file #{lambda_config['code']} ..."
|
241
|
+
download = open(lambda_config['code'])
|
242
|
+
IO.copy_stream(download, "#{out_folder}/src.zip")
|
243
|
+
FileUtils.mkdir_p('.cache/lambdas')
|
244
|
+
FileUtils.copy("#{out_folder}/src.zip", cached_location)
|
245
|
+
puts "Download complete, caching in #{cached_location}"
|
246
|
+
cached_downloads[lambda_config['code']] = cached_location
|
247
|
+
end
|
248
|
+
else
|
249
|
+
# zip local code
|
250
|
+
zip_options = ''
|
251
|
+
full_path = "#{@component_dir}/lambdas/#{lambda_config['code']}"
|
252
|
+
|
253
|
+
# lambda source can be either path to file or directory within that file
|
254
|
+
# optionally, lambda source code
|
255
|
+
lambda_source_dir = File.dirname(full_path)
|
256
|
+
lambda_source_file = File.basename(full_path)
|
257
|
+
lambda_source_file = '.' if Pathname.new(full_path).directory?
|
258
|
+
lambda_source_dir = full_path if Pathname.new(full_path).directory?
|
259
|
+
|
260
|
+
# executing package command can generate files. We DO NOT want this file in source directory,
|
261
|
+
# but rather in intermediate directory
|
262
|
+
tmp_source_dir = "#{@workdir}/out/lambdas/tmp/#{name}"
|
263
|
+
FileUtils.mkpath(File.dirname(tmp_source_dir))
|
264
|
+
FileUtils.copy_entry(lambda_source_dir, tmp_source_dir)
|
265
|
+
lambda_source_dir = tmp_source_dir
|
266
|
+
|
267
|
+
# Lambda function source code allows pre-processing (e.g. install code dependencies)
|
268
|
+
unless lambda_config['package_cmd'].nil?
|
269
|
+
puts "Following code will be executed to generate lambda function #{name}:\n\n#{lambda_config['package_cmd']}\n\n"
|
270
|
+
|
271
|
+
if @confirm_code_execution
|
272
|
+
exit -7 unless HighLine.agree('Proceed (y/n)?')
|
273
|
+
end
|
274
|
+
|
275
|
+
package_cmd = "cd #{lambda_source_dir} && #{lambda_config['package_cmd']}"
|
276
|
+
puts 'Processing package command...'
|
277
|
+
package_result = system(package_cmd)
|
278
|
+
unless package_result
|
279
|
+
puts "Error packaging lambda function, following command failed\n\n#{package_cmd}\n\n"
|
280
|
+
exit -4
|
281
|
+
end
|
282
|
+
end
|
283
|
+
File.delete full_destination_path if File.exist? full_destination_path
|
284
|
+
zip_generator = Highlander::Util::ZipFileGenerator.new(lambda_source_dir, full_destination_path)
|
285
|
+
zip_generator.write
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
sha256 = Digest::SHA256.file full_destination_path
|
290
|
+
sha256 = sha256.base64digest
|
291
|
+
puts "Created zip package #{full_destination_path} for lambda #{name} with digest #{sha256}"
|
292
|
+
@metadata['sha256'][name] = sha256
|
293
|
+
@metadata['version'][name] = timestamp
|
294
|
+
end
|
295
|
+
|
296
|
+
return archive_paths
|
297
|
+
end
|
298
|
+
|
299
|
+
def mergeComponentConfig
|
300
|
+
@component.config['lambda_metadata'] = @metadata
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Highlander
|
3
|
+
|
4
|
+
module Dsl
|
5
|
+
class DslBase
|
6
|
+
|
7
|
+
attr_accessor :config
|
8
|
+
|
9
|
+
def initialize(parent)
|
10
|
+
@config = parent.config unless parent.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method, *args)
|
14
|
+
if @config.nil?
|
15
|
+
raise StandardError, "#{self} no config!"
|
16
|
+
end
|
17
|
+
return @config["#{method}"] unless @config["#{method}"].nil?
|
18
|
+
raise StandardError, "#{self}Unknown method or variable #{method} in Highlander template"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,243 @@
|
|
1
|
+
require_relative './highlander.helper'
|
2
|
+
require_relative './highlander.dsl.base'
|
3
|
+
require_relative './highlander.factory'
|
4
|
+
|
5
|
+
module Highlander
|
6
|
+
|
7
|
+
module Dsl
|
8
|
+
|
9
|
+
class SubcomponentParameter
|
10
|
+
attr_accessor :name, :cfndsl_value
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
class Component < DslBase
|
20
|
+
|
21
|
+
attr_accessor :name,
|
22
|
+
:template,
|
23
|
+
:template_version,
|
24
|
+
:distribution_format,
|
25
|
+
:distribution_location,
|
26
|
+
:distribution_url,
|
27
|
+
:distribution_format,
|
28
|
+
:component_loaded,
|
29
|
+
:parameters,
|
30
|
+
:param_values,
|
31
|
+
:parent,
|
32
|
+
:component_config_override,
|
33
|
+
:export_config
|
34
|
+
|
35
|
+
def initialize(parent,
|
36
|
+
name,
|
37
|
+
template,
|
38
|
+
param_values,
|
39
|
+
component_sources = [],
|
40
|
+
config = {},
|
41
|
+
export_config = {},
|
42
|
+
distribution_format = 'yaml')
|
43
|
+
|
44
|
+
@parent = parent
|
45
|
+
@config = config
|
46
|
+
@export_config = export_config
|
47
|
+
@component_sources = component_sources
|
48
|
+
|
49
|
+
template_name = template
|
50
|
+
template_version = 'latest'
|
51
|
+
if template.include?('@') and not (template.start_with? 'git')
|
52
|
+
template_name = template.split('@')[0]
|
53
|
+
template_version = template.split('@')[1]
|
54
|
+
end
|
55
|
+
|
56
|
+
@template = template_name
|
57
|
+
@template_version = template_version
|
58
|
+
@name = name
|
59
|
+
@param_values = param_values
|
60
|
+
|
61
|
+
# distribution settings
|
62
|
+
@distribution_format = distribution_format
|
63
|
+
# by default components located at same location as master stack
|
64
|
+
@distribution_location = '.'
|
65
|
+
build_distribution_url
|
66
|
+
|
67
|
+
# load component
|
68
|
+
factory = Highlander::Factory::ComponentFactory.new(@component_sources)
|
69
|
+
@component_loaded = factory.findComponent(
|
70
|
+
@template,
|
71
|
+
@template_version
|
72
|
+
)
|
73
|
+
@component_loaded.config.extend @config
|
74
|
+
|
75
|
+
@parameters = []
|
76
|
+
# load_parameters
|
77
|
+
end
|
78
|
+
|
79
|
+
def version=(value)
|
80
|
+
@component_loaded.version = value
|
81
|
+
end
|
82
|
+
|
83
|
+
def distribute_bucket=(value)
|
84
|
+
@component_loaded.distribution_bucket = value
|
85
|
+
end
|
86
|
+
|
87
|
+
def distribute_prefix=(value)
|
88
|
+
@component_loaded.distribution_prefix = value
|
89
|
+
end
|
90
|
+
|
91
|
+
def distribution_format=(value)
|
92
|
+
@distribution_format = value
|
93
|
+
build_distribution_url
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_distribution_url
|
97
|
+
@distribution_location = @parent.distribute_url unless @parent.distribute_url.nil?
|
98
|
+
@distribution_url = "#{@distribution_location}/#{@name}.compiled.#{@distribution_format}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def load(component_config_override = {})
|
102
|
+
# check for component config on parent
|
103
|
+
parent = @parent
|
104
|
+
|
105
|
+
# Highest priority is DSL defined configuration
|
106
|
+
component_config_override.extend @config
|
107
|
+
|
108
|
+
@component_config_override = component_config_override
|
109
|
+
|
110
|
+
@component_loaded.load @component_config_override
|
111
|
+
end
|
112
|
+
|
113
|
+
# Parameters should be lazy loaded, that is late-binding should happen once
|
114
|
+
# all parameters and mappings are known
|
115
|
+
def load_parameters
|
116
|
+
component_dsl = @component_loaded.highlander_dsl
|
117
|
+
component_dsl.parameters.param_list.each do |component_param|
|
118
|
+
param = Highlander::Dsl::SubcomponentParameter.new
|
119
|
+
param.name = component_param.name
|
120
|
+
param.cfndsl_value = SubcomponentParamValueResolver.resolveValue(
|
121
|
+
@parent,
|
122
|
+
self,
|
123
|
+
component_param)
|
124
|
+
@parameters << param
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
class SubcomponentParamValueResolver
|
131
|
+
def self.resolveValue(component, sub_component, param)
|
132
|
+
|
133
|
+
puts("Resolving parameter #{component.name} -> #{sub_component.name}.#{param.name}")
|
134
|
+
|
135
|
+
# check if there are values defined on component itself
|
136
|
+
if sub_component.param_values.key?(param.name)
|
137
|
+
return Highlander::Helper.parameter_cfndsl_value(sub_component.param_values[param.name])
|
138
|
+
end
|
139
|
+
|
140
|
+
if param.class == Highlander::Dsl::StackParam
|
141
|
+
return self.resolveStackParamValue(component, sub_component, param)
|
142
|
+
elsif param.class == Highlander::Dsl::ComponentParam
|
143
|
+
return self.resolveComponentParamValue(component, sub_component, param)
|
144
|
+
elsif param.class == Highlander::Dsl::MappingParam
|
145
|
+
return self.resolveMappingParamValue(component, sub_component, param)
|
146
|
+
elsif param.class == Highlander::Dsl::OutputParam
|
147
|
+
return self.resolveOutputParamValue(component, sub_component, param)
|
148
|
+
else
|
149
|
+
raise "#{param.class} not resolvable to parameter value"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.resolveStackParamValue(component, sub_component, param)
|
154
|
+
param_name = param.is_global ? param.name : "#{sub_component.name}#{param.name}"
|
155
|
+
return "Ref('#{param_name}')"
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.resolveComponentParamValue(component, sub_component, param)
|
159
|
+
# check component config for param value
|
160
|
+
# TODO
|
161
|
+
# check stack config for param value
|
162
|
+
# TODO
|
163
|
+
# return default value
|
164
|
+
return "'#{param.default_value}'"
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.resolveMappingParamValue(component, sub_component, param)
|
168
|
+
|
169
|
+
# determine map name
|
170
|
+
|
171
|
+
provider = nil
|
172
|
+
|
173
|
+
mappings_name = param.mapName
|
174
|
+
actual_map_name = mappings_name
|
175
|
+
|
176
|
+
key_name = nil
|
177
|
+
|
178
|
+
# priority 0: stack-level parameter of map name
|
179
|
+
stack_param_mapname = component.parameters.param_list.find { |p| p.name == mappings_name }
|
180
|
+
unless stack_param_mapname.nil?
|
181
|
+
key_name = "Ref('#{mappings_name}')"
|
182
|
+
end
|
183
|
+
|
184
|
+
# priority 1 mapping provider keyName - used as lowest priority
|
185
|
+
if key_name.nil?
|
186
|
+
provider = mappings_provider(mappings_name)
|
187
|
+
if ((not provider.nil?) and (provider.respond_to?('getDefaultKey')))
|
188
|
+
key_name = provider.getDefaultKey
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# priority 2: dsl defined key name
|
193
|
+
if key_name.nil?
|
194
|
+
key_name = param.mapKey
|
195
|
+
# could still be nil after this line
|
196
|
+
end
|
197
|
+
|
198
|
+
value = mapping_value(component: component,
|
199
|
+
provider_name: mappings_name,
|
200
|
+
value_name: param.mapAttribute,
|
201
|
+
key_name: key_name
|
202
|
+
)
|
203
|
+
|
204
|
+
if value.nil?
|
205
|
+
return "'#{param.default_value}'" unless param.default_value.empty?
|
206
|
+
return "''"
|
207
|
+
end
|
208
|
+
|
209
|
+
return value
|
210
|
+
|
211
|
+
|
212
|
+
return value
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.resolveOutputParamValue(component, sub_component, param)
|
216
|
+
component_name = param.component
|
217
|
+
resource_name = nil
|
218
|
+
if not sub_component.export_config.nil?
|
219
|
+
if sub_component.export_config.key? component_name
|
220
|
+
resource_name = sub_component.export_config[component_name]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
if resource_name.nil?
|
225
|
+
# find by component
|
226
|
+
resource = component.components.find { |c| c.name == component_name }
|
227
|
+
resource_name = resource.name unless resource.nil?
|
228
|
+
if resource_name.nil?
|
229
|
+
resource = component.components.find { |c| c.template == component_name }
|
230
|
+
resource_name = resource.name unless resource.nil?
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
if resource_name.nil?
|
235
|
+
raise "#{sub_component.name}.Params.#{param.name}: Failed to resolve OutputParam '#{param.name}' with source '#{component_name}'. Component not found!"
|
236
|
+
end
|
237
|
+
|
238
|
+
return "FnGetAtt('#{resource_name}','Outputs.#{param.name}')"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require_relative './highlander.dsl.base'
|
2
|
+
|
3
|
+
module Highlander
|
4
|
+
|
5
|
+
module Dsl
|
6
|
+
class Parameters < DslBase
|
7
|
+
|
8
|
+
attr_accessor :param_list
|
9
|
+
|
10
|
+
def initialize()
|
11
|
+
@param_list = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def addParam(param)
|
15
|
+
existing_param = @param_list.find { |p| p.name == param.name }
|
16
|
+
if not existing_param.nil?
|
17
|
+
puts "Parameter being overwritten. Updating parameter #{param.name} with new definition..."
|
18
|
+
@param_list[@param_list.index(existing_param)] = param
|
19
|
+
else
|
20
|
+
@param_list << param
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def StackParam(name, defaultValue='', isGlobal: false, noEcho: false)
|
25
|
+
param = StackParam.new(name, 'String', defaultValue)
|
26
|
+
param.is_global = isGlobal
|
27
|
+
param.config = @config
|
28
|
+
param.no_echo = noEcho
|
29
|
+
addParam param
|
30
|
+
end
|
31
|
+
|
32
|
+
def ComponentParam(name, defaultValue='')
|
33
|
+
param = ComponentParam.new(name, 'String', defaultValue)
|
34
|
+
param.config = @config
|
35
|
+
addParam param
|
36
|
+
end
|
37
|
+
|
38
|
+
def MappingParam(name, defaultValue='', &block)
|
39
|
+
param = MappingParam.new(name, 'String', defaultValue)
|
40
|
+
param.config = @config
|
41
|
+
param.instance_eval(&block)
|
42
|
+
addParam param
|
43
|
+
end
|
44
|
+
|
45
|
+
def OutputParam(component:, name:, default: '')
|
46
|
+
param = OutputParam.new(component, name, default)
|
47
|
+
param.config = @config
|
48
|
+
addParam param
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Parameter < DslBase
|
53
|
+
attr_accessor :name, :type, :default_value, :no_echo
|
54
|
+
|
55
|
+
def initialize(name, type, defaultValue, noEcho = false)
|
56
|
+
@no_echo = noEcho
|
57
|
+
@name = name
|
58
|
+
@type = type
|
59
|
+
@default_value = defaultValue
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class StackParam < Parameter
|
64
|
+
attr_accessor :is_global
|
65
|
+
end
|
66
|
+
|
67
|
+
class ComponentParam < Parameter
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class OutputParam < Parameter
|
72
|
+
attr_accessor :component
|
73
|
+
|
74
|
+
def initialize(component, name, default)
|
75
|
+
@component = component
|
76
|
+
@name = name
|
77
|
+
@default_value = default
|
78
|
+
@type = 'String'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class MappingParam < Parameter
|
83
|
+
|
84
|
+
attr_accessor :mapName, :mapKey, :mapAttribute
|
85
|
+
|
86
|
+
def method_missing(method, *args)
|
87
|
+
smethod = "#{method}"
|
88
|
+
if smethod.start_with?('Map')
|
89
|
+
puts smethod
|
90
|
+
end
|
91
|
+
|
92
|
+
super.method_missing(method)
|
93
|
+
end
|
94
|
+
|
95
|
+
def key(map_key)
|
96
|
+
@mapKey = map_key
|
97
|
+
end
|
98
|
+
|
99
|
+
def attribute(key)
|
100
|
+
@mapAttribute = key
|
101
|
+
end
|
102
|
+
|
103
|
+
def map(mapName)
|
104
|
+
@mapName = mapName
|
105
|
+
end
|
106
|
+
|
107
|
+
def mapProvider
|
108
|
+
mappings_provider(@mapName)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|