cfhighlander 0.2.1 → 0.3.0.alpha.1528936037

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,7 @@ require 'highline/import'
12
12
  require 'zip'
13
13
  require_relative './util/zip.util'
14
14
 
15
- module Highlander
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['HIGHLANDER_WORKDIR']
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.components.each do |sub_component|
53
- sub_component_compiler = Highlander::Compiler::ComponentCompiler.new(sub_component.component_loaded)
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.components.each { |sc|
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 compileCloudFormation(format = 'yaml')
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
- cfndsl_opts = []
126
- cfndsl_opts.push([:yaml, config_yaml_path])
133
+
127
134
 
128
135
  # grab cfndsl model
129
- model = CfnDsl.eval_file_with_extras(@cfndsl_compiled_path, cfndsl_opts, false)
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 = Highlander::Util::ZipFileGenerator.new(lambda_source_dir, full_destination_path)
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 Highlander
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 Highlander template"
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