cfhighlander 0.2.1 → 0.3.0.alpha.1528936037
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +199 -82
- data/bin/cfhighlander +1 -1
- data/bin/{highlander.rb → cfhighlander.rb} +39 -23
- data/cfndsl_ext/sg.rb +29 -2
- data/hl_ext/mapping_helper.rb +6 -4
- data/lib/{highlander.compiler.rb → cfhighlander.compiler.rb} +26 -18
- data/lib/{highlander.dsl.base.rb → cfhighlander.dsl.base.rb} +3 -2
- data/lib/cfhighlander.dsl.params.rb +108 -0
- data/lib/cfhighlander.dsl.subcomponent.rb +274 -0
- data/lib/{highlander.dsl.rb → cfhighlander.dsl.template.rb} +111 -63
- data/lib/cfhighlander.factory.rb +45 -0
- data/lib/cfhighlander.factory.templatefinder.rb +248 -0
- data/lib/{highlander.helper.rb → cfhighlander.helper.rb} +2 -2
- data/lib/{highlander.mapproviders.rb → cfhighlander.mapproviders.rb} +1 -1
- data/lib/cfhighlander.model.component.rb +177 -0
- data/lib/cfhighlander.model.templatemeta.rb +25 -0
- data/lib/{highlander.publisher.rb → cfhighlander.publisher.rb} +3 -3
- data/lib/{highlander.validator.rb → cfhighlander.validator.rb} +1 -1
- data/lib/util/zip.util.rb +1 -1
- data/templates/cfndsl.component.template.erb +15 -14
- metadata +38 -15
- data/lib/highlander.dsl.component.rb +0 -243
- data/lib/highlander.dsl.params.rb +0 -113
- data/lib/highlander.factory.rb +0 -359
@@ -12,7 +12,7 @@ require 'highline/import'
|
|
12
12
|
require 'zip'
|
13
13
|
require_relative './util/zip.util'
|
14
14
|
|
15
|
-
module
|
15
|
+
module Cfhighlander
|
16
16
|
|
17
17
|
module Compiler
|
18
18
|
|
@@ -33,7 +33,7 @@ module Highlander
|
|
33
33
|
|
34
34
|
def initialize(component)
|
35
35
|
|
36
|
-
@workdir = ENV['
|
36
|
+
@workdir = ENV['CFHIGHLANDER_WORKDIR']
|
37
37
|
@component = component
|
38
38
|
@sub_components = []
|
39
39
|
@component_name = component.highlander_dsl.name.downcase
|
@@ -43,14 +43,16 @@ module Highlander
|
|
43
43
|
@lambdas_processed = false
|
44
44
|
@silent_mode = false
|
45
45
|
@lambda_src_paths = []
|
46
|
+
@config_yaml_path = nil
|
47
|
+
@cfn_model = nil
|
46
48
|
|
47
49
|
if @@global_extensions_paths.empty?
|
48
50
|
global_extensions_folder = "#{File.dirname(__FILE__)}/../cfndsl_ext"
|
49
51
|
Dir["#{global_extensions_folder}/*.rb"].each { |f| @@global_extensions_paths << f }
|
50
52
|
end
|
51
53
|
|
52
|
-
@component.highlander_dsl.
|
53
|
-
sub_component_compiler =
|
54
|
+
@component.highlander_dsl.subcomponents.each do |sub_component|
|
55
|
+
sub_component_compiler = Cfhighlander::Compiler::ComponentCompiler.new(sub_component.component_loaded)
|
54
56
|
sub_component_compiler.component_name = sub_component.name
|
55
57
|
@sub_components << sub_component_compiler
|
56
58
|
end
|
@@ -67,7 +69,7 @@ module Highlander
|
|
67
69
|
dsl = @component.highlander_dsl
|
68
70
|
component_cfndsl = @component.cfndsl_content
|
69
71
|
|
70
|
-
@component.highlander_dsl.
|
72
|
+
@component.highlander_dsl.subcomponents.each { |sc|
|
71
73
|
sc.distribution_format = out_format
|
72
74
|
}
|
73
75
|
|
@@ -100,33 +102,38 @@ module Highlander
|
|
100
102
|
|
101
103
|
end
|
102
104
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
105
|
+
def evaluateCloudFormation(format = 'yaml')
|
106
106
|
#compile cfndsl templates first
|
107
107
|
compileCfnDsl format unless @cfndsl_compiled
|
108
108
|
|
109
|
+
# write config
|
110
|
+
cfndsl_opts = []
|
111
|
+
cfndsl_opts.push([:yaml, @config_yaml_path])
|
112
|
+
|
113
|
+
# grab cfndsl model
|
114
|
+
model = CfnDsl.eval_file_with_extras(@cfndsl_compiled_path, cfndsl_opts, false)
|
115
|
+
@cfn_model = model
|
116
|
+
return model
|
117
|
+
end
|
118
|
+
|
119
|
+
def compileCloudFormation(format = 'yaml')
|
120
|
+
|
109
121
|
dsl = @component.highlander_dsl
|
110
|
-
component_cfndsl = @component.cfndsl_content
|
111
122
|
|
112
123
|
# create out dir if not there
|
113
124
|
@cfn_output_location = "#{@workdir}/out/#{format}"
|
114
125
|
output_dir = @cfn_output_location
|
115
126
|
FileUtils.mkdir_p(output_dir) unless Dir.exist?(output_dir)
|
116
127
|
|
117
|
-
# write config
|
118
|
-
config_yaml_path = writeConfig
|
119
|
-
|
120
128
|
|
121
129
|
# compile templates
|
122
|
-
output_path = "#{output_dir}/#{component_name}.compiled.#{format}"
|
130
|
+
output_path = "#{output_dir}/#{@component_name}.compiled.#{format}"
|
123
131
|
@cfn_template_paths << output_path
|
124
132
|
# configure cfndsl
|
125
|
-
|
126
|
-
cfndsl_opts.push([:yaml, config_yaml_path])
|
133
|
+
|
127
134
|
|
128
135
|
# grab cfndsl model
|
129
|
-
model =
|
136
|
+
model = evaluateCloudFormation
|
130
137
|
|
131
138
|
# write resulting cloud formation template
|
132
139
|
if format == 'json'
|
@@ -165,7 +172,8 @@ module Highlander
|
|
165
172
|
end
|
166
173
|
end
|
167
174
|
@config_written = true
|
168
|
-
config_yaml_path
|
175
|
+
@config_yaml_path = config_yaml_path
|
176
|
+
return @config_yaml_path
|
169
177
|
end
|
170
178
|
|
171
179
|
def processLambdas()
|
@@ -281,7 +289,7 @@ module Highlander
|
|
281
289
|
end
|
282
290
|
end
|
283
291
|
File.delete full_destination_path if File.exist? full_destination_path
|
284
|
-
zip_generator =
|
292
|
+
zip_generator = Cfhighlander::Util::ZipFileGenerator.new(lambda_source_dir, full_destination_path)
|
285
293
|
zip_generator.write
|
286
294
|
|
287
295
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
module
|
2
|
+
module Cfhighlander
|
3
3
|
|
4
4
|
module Dsl
|
5
5
|
class DslBase
|
@@ -15,10 +15,11 @@ module Highlander
|
|
15
15
|
raise StandardError, "#{self} no config!"
|
16
16
|
end
|
17
17
|
return @config["#{method}"] unless @config["#{method}"].nil?
|
18
|
-
raise StandardError, "#{self}Unknown method or variable #{method} in
|
18
|
+
raise StandardError, "#{self} Unknown method or variable #{method} in Cfhighlander template"
|
19
19
|
end
|
20
20
|
|
21
21
|
end
|
22
|
+
|
22
23
|
end
|
23
24
|
|
24
25
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require_relative './cfhighlander.dsl.base'
|
2
|
+
|
3
|
+
module Cfhighlander
|
4
|
+
|
5
|
+
module Dsl
|
6
|
+
|
7
|
+
# dsl statements
|
8
|
+
class Parameters < DslBase
|
9
|
+
|
10
|
+
attr_accessor :param_list
|
11
|
+
|
12
|
+
def initialize()
|
13
|
+
@param_list = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def addParam(param)
|
17
|
+
existing_param = @param_list.find {|p| p.name == param.name}
|
18
|
+
if not existing_param.nil?
|
19
|
+
puts "Parameter being overwritten. Updating parameter #{param.name} with new definition..."
|
20
|
+
@param_list[@param_list.index(existing_param)] = param
|
21
|
+
else
|
22
|
+
@param_list << param
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def StackParam(name, defaultValue = '', isGlobal: false, noEcho: false, type: 'String')
|
27
|
+
STDERR.puts "DEPRECATED: StackParam #{name} - Use ComponentParam instead"
|
28
|
+
ComponentParam(name, defaultValue, isGlobal: isGlobal, noEcho: noEcho, type: type)
|
29
|
+
end
|
30
|
+
|
31
|
+
def OutputParam(component:, name:, isGlobal: false, noEcho: false, type: 'String')
|
32
|
+
STDERR.puts ("DEPRECATED: OutputParam #{name} - Use ComponentParam instead. Outputut params are " +
|
33
|
+
"autorwired by name only, with component disregarded")
|
34
|
+
param = ComponentParam(name, '', isGlobal: isGlobal, noEcho: noEcho, type: type)
|
35
|
+
param.provided_value = "#{component}.#{name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def ComponentParam(name, defaultValue = '', isGlobal: false, noEcho: false, type: 'String', allowedValues: nil)
|
39
|
+
param = Parameter.new(name, type, defaultValue, noEcho, isGlobal, allowedValues)
|
40
|
+
param.config = @config
|
41
|
+
addParam param
|
42
|
+
return param
|
43
|
+
end
|
44
|
+
|
45
|
+
def MappingParam(name, defaultValue = '', &block)
|
46
|
+
param = MappingParam.new(name, 'String', defaultValue)
|
47
|
+
param.config = @config
|
48
|
+
param.instance_eval(&block)
|
49
|
+
addParam param
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# model classes
|
56
|
+
class Parameter < DslBase
|
57
|
+
attr_accessor :name,
|
58
|
+
:type,
|
59
|
+
:default_value,
|
60
|
+
:no_echo,
|
61
|
+
:is_global,
|
62
|
+
:provided_value,
|
63
|
+
:allowed_values
|
64
|
+
|
65
|
+
def initialize(name, type, defaultValue, noEcho = false, isGlobal = false, allowed_values = nil)
|
66
|
+
@no_echo = noEcho
|
67
|
+
@name = name
|
68
|
+
@type = type
|
69
|
+
@default_value = defaultValue
|
70
|
+
@is_global = isGlobal
|
71
|
+
@allowed_values = allowed_values
|
72
|
+
@provided_value = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
class MappingParam < Parameter
|
78
|
+
|
79
|
+
attr_accessor :mapName, :mapKey, :mapAttribute
|
80
|
+
|
81
|
+
def method_missing(method, *args)
|
82
|
+
smethod = "#{method}"
|
83
|
+
if smethod.start_with?('Map')
|
84
|
+
puts smethod
|
85
|
+
end
|
86
|
+
|
87
|
+
super.method_missing(method)
|
88
|
+
end
|
89
|
+
|
90
|
+
def key(map_key)
|
91
|
+
@mapKey = map_key
|
92
|
+
end
|
93
|
+
|
94
|
+
def attribute(key)
|
95
|
+
@mapAttribute = key
|
96
|
+
end
|
97
|
+
|
98
|
+
def map(mapName)
|
99
|
+
@mapName = mapName
|
100
|
+
end
|
101
|
+
|
102
|
+
def mapProvider
|
103
|
+
mappings_provider(@mapName)
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require_relative './cfhighlander.helper'
|
2
|
+
require_relative './cfhighlander.dsl.base'
|
3
|
+
require_relative './cfhighlander.factory'
|
4
|
+
require 'cfndsl'
|
5
|
+
|
6
|
+
module Cfhighlander
|
7
|
+
|
8
|
+
module Dsl
|
9
|
+
|
10
|
+
class SubcomponentParameter
|
11
|
+
attr_accessor :name, :cfndsl_value
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class Subcomponent < DslBase
|
20
|
+
|
21
|
+
attr_accessor :distribution_format,
|
22
|
+
:distribution_location,
|
23
|
+
:distribution_url,
|
24
|
+
:distribution_format,
|
25
|
+
:component_loaded,
|
26
|
+
:parameters,
|
27
|
+
:param_values,
|
28
|
+
:component_config_override,
|
29
|
+
:export_config
|
30
|
+
|
31
|
+
|
32
|
+
attr_reader :cfn_name,
|
33
|
+
:conditional,
|
34
|
+
:parent,
|
35
|
+
:name,
|
36
|
+
:template,
|
37
|
+
:template_version
|
38
|
+
|
39
|
+
def initialize(parent,
|
40
|
+
name,
|
41
|
+
template,
|
42
|
+
param_values,
|
43
|
+
component_sources = [],
|
44
|
+
config = {},
|
45
|
+
export_config = {},
|
46
|
+
conditional = false,
|
47
|
+
enabled = true,
|
48
|
+
distribution_format = 'yaml')
|
49
|
+
|
50
|
+
@parent = parent
|
51
|
+
@config = config
|
52
|
+
@export_config = export_config
|
53
|
+
@component_sources = component_sources
|
54
|
+
@conditional = conditional
|
55
|
+
|
56
|
+
template_name = template
|
57
|
+
template_version = 'latest'
|
58
|
+
if template.include?('@') and not (template.start_with? 'git')
|
59
|
+
template_name = template.split('@')[0]
|
60
|
+
template_version = template.split('@')[1]
|
61
|
+
end
|
62
|
+
|
63
|
+
@template = template_name
|
64
|
+
@template_version = template_version
|
65
|
+
@name = name
|
66
|
+
@cfn_name = @name.gsub('-','').gsub('_','').gsub(' ','')
|
67
|
+
@param_values = param_values
|
68
|
+
|
69
|
+
# distribution settings
|
70
|
+
@distribution_format = distribution_format
|
71
|
+
# by default components located at same location as master stack
|
72
|
+
@distribution_location = '.'
|
73
|
+
build_distribution_url
|
74
|
+
|
75
|
+
# load component
|
76
|
+
factory = Cfhighlander::Factory::ComponentFactory.new(@component_sources)
|
77
|
+
@component_loaded = factory.loadComponentFromTemplate(
|
78
|
+
@template,
|
79
|
+
@template_version,
|
80
|
+
@name
|
81
|
+
)
|
82
|
+
@component_loaded.config.extend @config
|
83
|
+
|
84
|
+
@parameters = []
|
85
|
+
|
86
|
+
# add condition to parent if conditonal component
|
87
|
+
if @conditional
|
88
|
+
condition_param_name = "Enable#{@cfn_name}"
|
89
|
+
@parent.Condition(condition_param_name, CfnDsl::Fn.new('Equals', [
|
90
|
+
CfnDsl::RefDefinition.new(condition_param_name),
|
91
|
+
'true'
|
92
|
+
]).to_json)
|
93
|
+
@parent.Parameters do
|
94
|
+
ComponentParam condition_param_name, enabled.to_s, allowedValues: %w(true false)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def version=(value)
|
100
|
+
@component_loaded.version = value
|
101
|
+
end
|
102
|
+
|
103
|
+
def distribute_bucket=(value)
|
104
|
+
@component_loaded.distribution_bucket = value
|
105
|
+
end
|
106
|
+
|
107
|
+
def distribute_prefix=(value)
|
108
|
+
@component_loaded.distribution_prefix = value
|
109
|
+
end
|
110
|
+
|
111
|
+
def distribution_format=(value)
|
112
|
+
@distribution_format = value
|
113
|
+
build_distribution_url
|
114
|
+
end
|
115
|
+
|
116
|
+
def build_distribution_url
|
117
|
+
@distribution_location = @parent.distribute_url unless @parent.distribute_url.nil?
|
118
|
+
@distribution_url = "#{@distribution_location}/#{@name}.compiled.#{@distribution_format}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def load(component_config_override = {})
|
122
|
+
# Highest priority is DSL defined configuration
|
123
|
+
component_config_override.extend @config
|
124
|
+
|
125
|
+
@component_config_override = component_config_override
|
126
|
+
|
127
|
+
@component_loaded.load @component_config_override
|
128
|
+
end
|
129
|
+
|
130
|
+
def parameter(name:, value:)
|
131
|
+
@param_values[name] = value
|
132
|
+
end
|
133
|
+
|
134
|
+
# Parameters should be lazy loaded, that is late-binding should happen once
|
135
|
+
# all parameters and mappings are known
|
136
|
+
def resolve_parameter_values(available_outputs)
|
137
|
+
component_dsl = @component_loaded.highlander_dsl
|
138
|
+
component_dsl.parameters.param_list.each do |component_param|
|
139
|
+
param = Cfhighlander::Dsl::SubcomponentParameter.new
|
140
|
+
param.name = component_param.name
|
141
|
+
param.cfndsl_value = SubcomponentParamValueResolver.resolveValue(
|
142
|
+
@parent,
|
143
|
+
self,
|
144
|
+
component_param,
|
145
|
+
available_outputs)
|
146
|
+
@parameters << param
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
class SubcomponentParamValueResolver
|
153
|
+
def self.resolveValue(component, sub_component, param, available_outputs)
|
154
|
+
|
155
|
+
print("INFO Resolving parameter #{component.name} -> #{sub_component.name}.#{param.name}: ")
|
156
|
+
|
157
|
+
# rule 0: this rule is here for legacy reasons and OutputParam. It should be deprecated
|
158
|
+
# once all hl-components- repos remove any references to OutputParam
|
159
|
+
if not param.provided_value.nil?
|
160
|
+
component_name = param.provided_value.split('.')[0]
|
161
|
+
output_name = param.provided_value.split('.')[1]
|
162
|
+
source_component = component.subcomponents.find {|c| c.name == component_name}
|
163
|
+
if source_component.nil?
|
164
|
+
source_component = component.subcomponents.find {|c| c.component_loaded.template.template_name == component_name}
|
165
|
+
end
|
166
|
+
return CfnDsl::Fn.new('GetAtt', [
|
167
|
+
source_component.name,
|
168
|
+
"Outputs.#{output_name}"
|
169
|
+
]).to_json
|
170
|
+
end
|
171
|
+
|
172
|
+
# rule 1: check if there are values defined on component itself
|
173
|
+
if sub_component.param_values.key?(param.name)
|
174
|
+
puts " parameter value provided "
|
175
|
+
|
176
|
+
param_value = sub_component.param_values[param.name]
|
177
|
+
if param_value.is_a? String and param_value.include? '.'
|
178
|
+
source_component_name = param_value.split('.')[0]
|
179
|
+
source_output = param_value.split('.')[1]
|
180
|
+
source_component = component.subcomponents.find {|sc| sc.name == source_component_name}
|
181
|
+
# if source component exists
|
182
|
+
if not source_component.nil?
|
183
|
+
if source_component_name == sub_component.name
|
184
|
+
STDERR.puts "WARNING: Parameter value on component #{source_component_name} references component itself: #{param_value}"
|
185
|
+
else
|
186
|
+
return CfnDsl::Fn.new('GetAtt', [
|
187
|
+
source_component_name,
|
188
|
+
"Outputs.#{source_output}"
|
189
|
+
]).to_json
|
190
|
+
end
|
191
|
+
else
|
192
|
+
return Cfhighlander::Helper.parameter_cfndsl_value(param_value)
|
193
|
+
end
|
194
|
+
else
|
195
|
+
return Cfhighlander::Helper.parameter_cfndsl_value(sub_component.param_values[param.name])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# rule 1.1 mapping parameters are handled differently.
|
200
|
+
# TODO wire mapping parameters outside of component
|
201
|
+
if param.class == Cfhighlander::Dsl::MappingParam
|
202
|
+
puts " mapping parameter"
|
203
|
+
return self.resolveMappingParamValue(component, sub_component, param)
|
204
|
+
end
|
205
|
+
|
206
|
+
# rule #2: match output values from other components
|
207
|
+
# by parameter name
|
208
|
+
if available_outputs.key? param.name
|
209
|
+
component_name = available_outputs[param.name].component.name
|
210
|
+
puts " resolved as output of #{component_name}"
|
211
|
+
return CfnDsl::Fn.new('GetAtt', [
|
212
|
+
component_name,
|
213
|
+
"Outputs.#{param.name}"
|
214
|
+
]).to_json
|
215
|
+
end
|
216
|
+
|
217
|
+
# by default bubble parameter and resolve as reference on upper level
|
218
|
+
propagated_param = param.clone
|
219
|
+
propagated_param.name = "#{sub_component.cfn_name}#{param.name}" unless param.is_global
|
220
|
+
component.parameters.addParam propagated_param
|
221
|
+
puts " no autowiring candidates, propagate parameter to parent"
|
222
|
+
return CfnDsl::RefDefinition.new(propagated_param.name).to_json
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.resolveMappingParamValue(component, sub_component, param)
|
227
|
+
|
228
|
+
# determine map name
|
229
|
+
|
230
|
+
provider = nil
|
231
|
+
|
232
|
+
mappings_name = param.mapName
|
233
|
+
actual_map_name = mappings_name
|
234
|
+
|
235
|
+
key_name = nil
|
236
|
+
|
237
|
+
# priority 0: stack-level parameter of map name
|
238
|
+
stack_param_mapname = component.parameters.param_list.find {|p| p.name == mappings_name}
|
239
|
+
unless stack_param_mapname.nil?
|
240
|
+
key_name = "Ref('#{mappings_name}')"
|
241
|
+
end
|
242
|
+
|
243
|
+
# priority 1 mapping provider keyName - used as lowest priority
|
244
|
+
if key_name.nil?
|
245
|
+
provider = mappings_provider(mappings_name)
|
246
|
+
if ((not provider.nil?) and (provider.respond_to?('getDefaultKey')))
|
247
|
+
key_name = provider.getDefaultKey
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# priority 2: dsl defined key name
|
252
|
+
if key_name.nil?
|
253
|
+
key_name = param.mapKey
|
254
|
+
# could still be nil after this line
|
255
|
+
end
|
256
|
+
|
257
|
+
value = mapping_value(component: component,
|
258
|
+
provider_name: mappings_name,
|
259
|
+
value_name: param.mapAttribute,
|
260
|
+
key_name: key_name
|
261
|
+
)
|
262
|
+
|
263
|
+
if value.nil?
|
264
|
+
return "'#{param.default_value}'" unless param.default_value.empty?
|
265
|
+
return "''"
|
266
|
+
end
|
267
|
+
|
268
|
+
return value
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
end
|