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.
@@ -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