cfhighlander 0.2.1.alpha.43 → 0.2.1

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.
@@ -2,26 +2,26 @@
2
2
 
3
3
  extensions_folder = "#{File.dirname(__FILE__)}/../hl_ext"
4
4
 
5
- Dir["#{extensions_folder}/*.rb"].each {|f|
5
+ Dir["#{extensions_folder}/*.rb"].each { |f|
6
6
  require f
7
7
  }
8
8
 
9
9
  # require libraries
10
10
 
11
- require_relative './cfhighlander.dsl.base'
12
- require_relative './cfhighlander.dsl.params'
13
- require_relative './cfhighlander.dsl.subcomponent'
11
+ require_relative './highlander.dsl.base'
12
+ require_relative './highlander.dsl.params'
13
+ require_relative './highlander.dsl.component'
14
14
 
15
- module Cfhighlander
15
+ module Highlander
16
16
 
17
17
  module Dsl
18
18
 
19
- class HighlanderTemplate < DslBase
19
+ class Template < DslBase
20
20
 
21
21
  attr_accessor :mappings,
22
22
  :parameters,
23
23
  :name,
24
- :subcomponents,
24
+ :components,
25
25
  :version,
26
26
  :distribute_url,
27
27
  :distribution_bucket,
@@ -32,7 +32,7 @@ module Cfhighlander
32
32
 
33
33
  def initialize
34
34
  @mappings = []
35
- @subcomponents = []
35
+ @components = []
36
36
  @config = { 'mappings' => {}, 'component_version' => 'latest' }
37
37
  @component_configs = {}
38
38
  @version = 'latest'
@@ -54,12 +54,8 @@ module Cfhighlander
54
54
  end
55
55
 
56
56
  def Name(name)
57
- # nested components always have their name dictated by parent
58
- # component, defaulting to template name
59
- if (not @config.key? 'nested_component')
60
- @name = name
61
- @config['component_name'] = name
62
- end
57
+ @name = name
58
+ @config['component_name'] = name
63
59
  end
64
60
 
65
61
  def Description(description)
@@ -74,7 +70,7 @@ module Cfhighlander
74
70
 
75
71
  def DynamicMappings(providerName)
76
72
  maps = mappings_provider_maps(providerName, self.config)
77
- maps.each {|name, map| addMapping(name, map)} unless maps.nil?
73
+ maps.each { |name, map| addMapping(name, map) } unless maps.nil?
78
74
  end
79
75
 
80
76
  def DependsOn(template)
@@ -82,23 +78,11 @@ module Cfhighlander
82
78
  end
83
79
 
84
80
 
85
- def Component(template_arg = nil, template: nil,
86
- name: template,
87
- param_values: {},
88
- config: {},
89
- export_config: {}, &block)
90
- puts "INFO: Requested subcomponent #{name} with template #{template}"
91
- if ((not template_arg.nil?) and template.nil?)
92
- template = template_arg
93
- elsif (template_arg.nil? and template.nil?)
94
- raise 'Component must be defined with template name at minimum,' +
95
- ' passed as first argument, or template: named arguent'
96
- end
97
-
98
- name = template if name.nil?
81
+ def Component(name:, template:, param_values: {}, config: {}, export_config: {}, &block)
82
+ puts "Initialize #{name} with template #{template}"
99
83
 
100
84
  # load component
101
- component = Cfhighlander::Dsl::Subcomponent.new(self,
85
+ component = Highlander::Dsl::Component.new(self,
102
86
  name,
103
87
  template,
104
88
  param_values,
@@ -106,12 +90,11 @@ module Cfhighlander
106
90
  config,
107
91
  export_config
108
92
  )
109
- component.instance_eval(&block) unless block.nil?
110
93
  component.distribute_bucket = @distribution_bucket unless @distribution_bucket.nil?
111
94
  component.distribute_prefix = @distribution_prefix unless @distribution_prefix.nil?
112
95
  component.version = @version unless @version.nil?
113
96
  @component_configs[name] = config
114
- @subcomponents << component
97
+ @components << component
115
98
  end
116
99
 
117
100
  def ComponentVersion(version)
@@ -164,8 +147,8 @@ module Cfhighlander
164
147
  def loadComponents()
165
148
 
166
149
  # empty config overrides to start with
167
- @config_overrides = Hash[@subcomponents.collect {|c| [c.name, { 'nested_component' => true }]}]
168
- @named_components = Hash[@subcomponents.collect {|c| [c.name, c]}]
150
+ @config_overrides = Hash[@components.collect { |c| [c.name, {}] }]
151
+ @named_components = Hash[@components.collect { |c| [c.name, c] }]
169
152
 
170
153
  # populate overrides with master config defined overrides
171
154
  load_configfile_component_config
@@ -187,7 +170,7 @@ module Cfhighlander
187
170
 
188
171
 
189
172
  # load components and extract parent stack parameters and mappings
190
- @subcomponents.each do |component|
173
+ @components.each { |component|
191
174
 
192
175
  component.load @config_overrides[component.name]
193
176
  # add all of it's stack parameters unless same template has been already processed
@@ -197,8 +180,21 @@ module Cfhighlander
197
180
  .parameters
198
181
  .param_list.each do |param|
199
182
 
183
+ # add stack parameters
184
+ if param.class == Highlander::Dsl::StackParam
185
+ # sub-component stack param becomes top-level component param
186
+ param_name = param.is_global ? param.name : "#{component.name}#{param.name}"
187
+ stack_param = Highlander::Dsl::ComponentParam.new(
188
+ param_name,
189
+ param.type,
190
+ param.default_value,
191
+ param.no_echo
192
+ )
193
+ @parameters.addParam stack_param
194
+ end unless component.param_values.key? param.name
195
+
200
196
  # for map parameters add maps
201
- if param.class == Cfhighlander::Dsl::MappingParam
197
+ if param.class == Highlander::Dsl::MappingParam
202
198
  if not param.mapProvider.nil?
203
199
  maps = param.mapProvider.getMaps(component.component_loaded.config)
204
200
  maps.each do |name, map|
@@ -215,24 +211,12 @@ module Cfhighlander
215
211
 
216
212
  end
217
213
 
218
- component.component_loaded.eval_cfndsl()
219
- end
220
-
221
- # in 2nd pass, load parameters
222
- # 2nd pass is required as all of cfndsl models need to be populated
223
- available_outputs = {}
224
- @subcomponents.each do |c|
225
- c.component_loaded.outputs.each do |out|
226
- available_outputs[out.name] = out
227
- end
228
- end
229
-
230
- @subcomponents.each do |component|
231
- component.resolve_parameter_values available_outputs
232
- end
214
+ # late bind parameter values, once mappings and top level params are extracted
215
+ component.load_parameters
216
+ }
233
217
 
234
218
  @dependson_components_templates.each do |template|
235
- component = Cfhighlander::Dsl::Subcomponent.new(self,
219
+ component = Highlander::Dsl::Component.new(self,
236
220
  template,
237
221
  template,
238
222
  {},
@@ -244,7 +228,7 @@ module Cfhighlander
244
228
  end
245
229
 
246
230
  def load_extension_exports
247
- @subcomponents.each do |c|
231
+ @components.each do |c|
248
232
  component = c.component_loaded
249
233
  config = component.config
250
234
  if ((config.key? 'lib_export') and (config['lib_export'].key? 'global'))
@@ -253,7 +237,7 @@ module Cfhighlander
253
237
  if global_export_config.key? 'cfndsl'
254
238
  global_export_config['cfndsl'].each do |exported_extension|
255
239
  extension_file_path = "#{component.component_dir}/ext/cfndsl/#{exported_extension}.rb"
256
- @subcomponents.each do |cr|
240
+ @components.each do |cr|
257
241
  cr.component_loaded.cfndsl_ext_files << extension_file_path unless cr == c
258
242
  end
259
243
  end
@@ -264,20 +248,16 @@ module Cfhighlander
264
248
  end
265
249
 
266
250
  def apply_config_overrides
267
- @config_overrides.each {|component_name, component_override|
251
+ @config_overrides.each { |component_name, component_override|
268
252
  @named_components[component_name].component_loaded.config.extend(component_override)
269
253
  }
270
254
  end
271
255
 
272
256
  def load_configfile_component_config
273
257
  if (@config.key? 'components')
274
- @config['components'].each {|component_name, component_config|
258
+ @config['components'].each { |component_name, component_config|
275
259
  if component_config.key?('config')
276
- if @config_overrides.key? component_name
277
- @config_overrides[component_name].extend(component_config['config'])
278
- else
279
- STDERR.puts("WARN: Overriding config for non-existing component #{component_name}")
280
- end
260
+ @config_overrides[component_name].extend(component_config['config'])
281
261
  end
282
262
  }
283
263
  end
@@ -286,28 +266,28 @@ module Cfhighlander
286
266
  def apply_config_exports
287
267
  # first export from master to all children
288
268
  if ((@config.key? 'config_export') and (@config['config_export']['global']))
289
- @config['config_export']['global'].each {|global_export_key|
269
+ @config['config_export']['global'].each { |global_export_key|
290
270
  if @config.key? global_export_key
291
- @config_overrides.each {|cname, co|
271
+ @config_overrides.each { |cname, co|
292
272
  co[global_export_key] = @config[global_export_key]
293
273
  }
294
274
  end
295
275
  }
296
276
  end
297
277
 
298
- @subcomponents.each {|component|
278
+ @components.each { |component|
299
279
  cl = component.component_loaded
300
280
  if ((not cl.config.nil?) and (cl.config.key? 'config_export'))
301
281
 
302
282
  # global config
303
283
  if cl.config['config_export'].key? 'global'
304
- cl.config['config_export']['global'].each {|global_export_key|
284
+ cl.config['config_export']['global'].each { |global_export_key|
305
285
 
306
286
  # global config is exported to parent and every component
307
287
  if cl.config.key? global_export_key
308
288
 
309
289
  # cname is for component name, co for component override
310
- @config_overrides.each {|cname, co|
290
+ @config_overrides.each { |cname, co|
311
291
 
312
292
  # if templates are different e.g don't export from vpc to vpc
313
293
  config_receiver_component = @named_components[cname]
@@ -315,11 +295,11 @@ module Cfhighlander
315
295
  if (not config_receiver_component.export_config.nil?) and (config_receiver_component.export_config.key? component.template)
316
296
  allow_from_component_name = config_receiver_component.export_config[component.template]
317
297
  if allow_from_component_name == component.name
318
- puts("INFO: Exporting key #{global_export_key} from component #{component.name} to #{cname}")
298
+ puts("Exporting key #{global_export_key} from component #{component.name} to #{cname}")
319
299
  co[global_export_key] = cl.config[global_export_key]
320
300
  end
321
301
  else
322
- puts("INFO: Exporting key #{global_export_key} from component #{component.name} to #{cname}")
302
+ puts("Exporting key #{global_export_key} from component #{component.name} to #{cname}")
323
303
  co[global_export_key] = cl.config[global_export_key]
324
304
  end
325
305
  end
@@ -333,7 +313,7 @@ module Cfhighlander
333
313
  end
334
314
 
335
315
  if cl.config['config_export'].key? 'component'
336
- cl.config['config_export']['component'].each {|component_name, export_keys|
316
+ cl.config['config_export']['component'].each { |component_name, export_keys|
337
317
  # check if there is configuration of export from this component
338
318
  # and if there is export configuration for given component name
339
319
 
@@ -342,7 +322,7 @@ module Cfhighlander
342
322
  if @config_overrides.key? component.export_config[component_name]
343
323
  # override the config
344
324
  real_component_name = component.export_config[component_name]
345
- export_keys.each {|export_component_key|
325
+ export_keys.each { |export_component_key|
346
326
  puts("Exporting config for key=#{export_component_key} from #{component.name} to #{real_component_name}")
347
327
  if not @config_overrides[real_component_name].key? export_component_key
348
328
  @config_overrides[real_component_name][export_component_key] = {}
@@ -353,7 +333,7 @@ module Cfhighlander
353
333
  STDERR.puts("Trying to export configuration for non-existant component #{component.export_config[component_name]}")
354
334
  end
355
335
  elsif @config_overrides.key? component_name
356
- export_keys.each {|export_component_key|
336
+ export_keys.each { |export_component_key|
357
337
  puts("Exporting config for key=#{export_component_key} from #{component.name} to #{component_name}")
358
338
  if not @config_overrides[component_name].key? export_component_key
359
339
  @config_overrides[component_name][export_component_key] = {}
@@ -373,7 +353,7 @@ module Cfhighlander
373
353
  end
374
354
 
375
355
  def load_explicit_component_config
376
- @component_configs.each {|component_name, component_config|
356
+ @component_configs.each { |component_name, component_config|
377
357
  @config_overrides[component_name].extend(component_config)
378
358
  }
379
359
  end
@@ -388,15 +368,15 @@ module Cfhighlander
388
368
  build_distribution_url
389
369
  end
390
370
 
391
- # def name=(value)
392
- # self.Name(value)
393
- # end
371
+ def name=(value)
372
+ self.Name(value)
373
+ end
394
374
 
395
375
  def build_distribution_url
396
376
  if not (@distribution_bucket.nil? or @distribution_prefix.nil?)
397
377
  @distribute_url = "https://#{@distribution_bucket}.s3.amazonaws.com/#{@distribution_prefix}"
398
378
  @distribute_url = "#{@distribute_url}/#{@version}" unless @version.nil?
399
- @subcomponents.each {|component|
379
+ @components.each { |component|
400
380
  component.distribute_bucket = @distribution_bucket unless @distribution_bucket.nil?
401
381
  component.distribute_prefix = @distribution_prefix unless @distribution_prefix.nil?
402
382
  component.version = @version unless @version.nil?
@@ -411,12 +391,13 @@ module Cfhighlander
411
391
 
412
392
  end
413
393
 
414
- def CfhighlanderTemplate(&block)
415
- instance = Cfhighlander::Dsl::HighlanderTemplate.new
394
+ def HighlanderComponent(&block)
395
+ instance = Highlander::Dsl::Template.new
416
396
 
417
397
  puts "Processing higlander component #{@name}\n\tLocation:#{@highlander_dsl_path}" +
418
398
  "\n\tConfig:#{@config}"
419
399
 
400
+ component_config = @config
420
401
 
421
402
  instance.config = @config
422
403
 
@@ -426,7 +407,6 @@ def CfhighlanderTemplate(&block)
426
407
 
427
408
  unless @version.nil?
428
409
  instance.version = @version
429
- instance.config['component_version'] = @version
430
410
  end
431
411
 
432
412
  unless @distribution_bucket.nil?
@@ -436,22 +416,13 @@ def CfhighlanderTemplate(&block)
436
416
  instance.DistributionPrefix(@distribution_prefix)
437
417
  end
438
418
 
439
- instance.name = @template.template_name
440
419
  instance.instance_eval(&block)
441
-
420
+ if instance.name.nil?
421
+ instance.name = @name
422
+ end
442
423
 
443
424
  # load sub-components
444
425
  instance.loadComponents
445
426
 
446
427
  return instance
447
428
  end
448
-
449
- def CfhighlanderComponent(&block)
450
- STDERR.puts("DEPRECATED: #{@template.template_name}: use CfhighlanderTemplate instead of CfhighlanderComponent")
451
- CfhighlanderTemplate(&block)
452
- end
453
-
454
- def HighlanderComponent(&block)
455
- STDERR.puts("DEPRECATED: #{@template.template_name}: use CfhighlanderTemplate instead of HighlanderComponent")
456
- CfhighlanderTemplate(&block)
457
- end
@@ -0,0 +1,359 @@
1
+ require_relative './highlander.dsl'
2
+ require 'fileutils'
3
+ require 'git'
4
+
5
+ LOCAL_HIGHLANDER_CACHE_LOCATION = "#{ENV['HOME']}/.highlander/components"
6
+
7
+ module Highlander
8
+
9
+ module Factory
10
+
11
+ class Component
12
+
13
+ attr_accessor :component_dir,
14
+ :config,
15
+ :highlander_dsl_path,
16
+ :cfndsl_path,
17
+ :highlander_dsl,
18
+ :cfndsl_content,
19
+ :mappings,
20
+ :name,
21
+ :version,
22
+ :distribution_bucket,
23
+ :distribution_prefix,
24
+ :component_files,
25
+ :cfndsl_ext_files,
26
+ :lambda_src_files
27
+
28
+ def initialize(component_name, component_dir)
29
+ @name = component_name
30
+ @component_dir = component_dir
31
+ @mappings = {}
32
+ @version = nil
33
+ @distribution_bucket = nil
34
+ @distribution_prefix = nil
35
+ @component_files = []
36
+ @cfndsl_ext_files = []
37
+ @lambda_src_files = []
38
+ end
39
+
40
+ def load_config()
41
+ @config = {} if @config.nil?
42
+ Dir["#{@component_dir}/*.config.yaml"].each do |config_file|
43
+ partial_config = YAML.load(File.read(config_file))
44
+ unless partial_config.nil?
45
+ @config.extend(partial_config)
46
+ @component_files << config_file
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ def loadDepandantExt()
53
+ @highlander_dsl.dependson_components.each do |requirement|
54
+ requirement.component_loaded.cfndsl_ext_files.each do |file|
55
+ cfndsl_ext_files << file
56
+ end
57
+ end
58
+ end
59
+
60
+ # @param [Hash] config_override
61
+ def load(config_override = nil)
62
+ if @component_dir.start_with? 'http'
63
+ raise StandardError, 'http(s) sources not supported yet'
64
+ end
65
+
66
+
67
+ @highlander_dsl_path = "#{@component_dir}/#{@name}.highlander.rb"
68
+ @cfndsl_path = "#{@component_dir}/#{@name}.cfndsl.rb"
69
+ candidate_config_path = "#{@component_dir}/#{@name}.config.yaml"
70
+ candidate_mappings_path = "#{@component_dir}/*.mappings.yaml"
71
+ candidate_dynamic_mappings_path = "#{@component_dir}/#{@name}.mappings.rb"
72
+
73
+ @cfndsl_ext_files += Dir["#{@component_dir}/ext/cfndsl/*.rb"]
74
+ @lambda_src_files += Dir["#{@component_dir}/lambdas/**/*"].find_all {|p| not File.directory? p}
75
+ @component_files += @cfndsl_ext_files
76
+ @component_files += @lambda_src_files
77
+
78
+ @config = {} if @config.nil?
79
+
80
+ @config.extend config_override unless config_override.nil?
81
+ @config['component_version'] = @version unless @version.nil?
82
+ # allow override of components
83
+ # config_override.each do |key, value|
84
+ # @config[key] = value
85
+ # end unless config_override.nil?
86
+
87
+ Dir[candidate_mappings_path].each do |mapping_file|
88
+ mappings = YAML.load(File.read(mapping_file))
89
+ @component_files << mapping_file
90
+ mappings.each do |name, map|
91
+ @mappings[name] = map
92
+ end unless mappings.nil?
93
+ end
94
+
95
+ if File.exist? candidate_dynamic_mappings_path
96
+ require candidate_dynamic_mappings_path
97
+ @component_files << candidate_dynamic_mappings_path
98
+ end
99
+
100
+ # 1st pass - parse the file
101
+ @component_files << @highlander_dsl_path
102
+ @highlander_dsl = eval(File.read(@highlander_dsl_path), binding)
103
+
104
+ # set version if not defined
105
+ @highlander_dsl.ComponentVersion(@version) unless @version.nil?
106
+
107
+
108
+ # Handle name and description defaults if they are not specified
109
+ # in template itself
110
+ if @highlander_dsl.name.nil?
111
+ @highlander_dsl.name = @name
112
+ end
113
+
114
+ if @highlander_dsl.description.nil?
115
+ @highlander_dsl.Description("#{@highlander_dsl.name} - #{@highlander_dsl.version}")
116
+ end
117
+
118
+ # set (override) distribution options
119
+ @highlander_dsl.DistributionBucket(@distribution_bucket) unless @distribution_bucket.nil?
120
+ @highlander_dsl.DistributionPrefix(@distribution_prefix) unless @distribution_prefix.nil?
121
+
122
+ if File.exist? @cfndsl_path
123
+ @component_files << @cfndsl_path
124
+ @cfndsl_content = File.read(@cfndsl_path)
125
+ @cfndsl_content.strip!
126
+ # if there is CloudFormation do [content] end extract only contents
127
+ ### Regex \s is whitespace
128
+ match_data = /^CloudFormation do\s(.*)end\s?$/m.match(@cfndsl_content)
129
+ if not match_data.nil?
130
+ @cfndsl_content = match_data[1]
131
+ end
132
+
133
+ else
134
+ @cfndsl_content = ''
135
+ end
136
+
137
+ loadDepandantExt()
138
+ end
139
+ end
140
+
141
+ class ComponentFactory
142
+
143
+ attr_accessor :component_sources
144
+
145
+
146
+ def initialize(component_sources = [])
147
+ ## First look in local $PWD/components folder
148
+ ## Then search for cached $HOME/.highlander/components
149
+ ## Then search in sources given by dsl
150
+ default_locations = [
151
+ LOCAL_HIGHLANDER_CACHE_LOCATION,
152
+ File.expand_path('components'),
153
+ File.expand_path('.')
154
+ ]
155
+ default_locations.each do |predefined_path|
156
+ component_sources.unshift(predefined_path)
157
+ end
158
+
159
+ @component_sources = component_sources
160
+ end
161
+
162
+ def findComponentDefault(component_name, component_version)
163
+ default_lookup_url = 'https://github.com/theonestack'
164
+ default_lookup_url = ENV['HIGHLANDER_DEFAULT_COMPONENT_GIT_LOOKUP'] if ENV.key? 'HIGHLANDER_DEFAULT_COMPONENT_GIT_LOOKUP'
165
+
166
+ git_url = "#{default_lookup_url}/hl-component-#{component_name}"
167
+
168
+ if component_version.nil? or component_version.empty? or component_version == 'latest'
169
+ branch = 'master'
170
+ else
171
+ branch = component_version
172
+ end
173
+ local_path = "#{LOCAL_HIGHLANDER_CACHE_LOCATION}/#{component_name}/#{component_version}"
174
+ return findComponentGit(local_path, component_name, component_version, git_url, branch)
175
+
176
+ end
177
+
178
+ def findComponentGit(local_path, component_name, component_version, git_url, branch)
179
+ begin
180
+ local_path = "#{local_path}/" unless local_path.end_with? '/'
181
+ # if this is snapshot, clean local cache
182
+ if branch.end_with? '.snapshot'
183
+ branch = branch.gsub('.snapshot', '')
184
+ FileUtils.rmtree local_path if File.exist? local_path and File.directory? local_path
185
+ end
186
+
187
+ # if local cache exists, return from cache
188
+ if not Dir.glob("#{local_path}*.highlander.rb").empty?
189
+ # if cache exists, just return from cache
190
+ component_name = Dir.glob("#{local_path}*.highlander.rb")[0].gsub(local_path, '').gsub('.highlander.rb','')
191
+ return component_name, local_path
192
+ end
193
+
194
+ # shallow clone
195
+ puts "Trying to load #{component_name}/#{component_version} from #{git_url}##{branch} ... "
196
+ clone_opts = { depth: 1 }
197
+ clone_opts[:branch] = branch if not (branch.nil? or branch.empty?)
198
+ Git.clone git_url, local_path, clone_opts
199
+ puts "\t .. cached in #{local_path}\n"
200
+ # return from cache once it's cloned
201
+ return findComponentGit(local_path, component_name, component_version, git_url, branch)
202
+ rescue Exception => e
203
+ STDERR.puts "Failed to resolve component #{component_name}@#{component_version} from #{git_url}"
204
+ STDERR.puts e
205
+ return nil
206
+ end
207
+ end
208
+
209
+ def findComponentS3(s3_location, component_name, component_version)
210
+ parts = s3_location.split('/')
211
+ bucket = parts[2]
212
+ prefix = parts[3]
213
+ s3_key = "#{prefix}/#{component_name}/#{component_version}/#{component_name}.highlander.rb"
214
+ s3_prefix = "#{prefix}/#{component_name}/#{component_version}/"
215
+ local_destination = "#{LOCAL_HIGHLANDER_CACHE_LOCATION}/#{component_name}/#{component_version}"
216
+ begin
217
+ s3 = Aws::S3::Client.new({ region: s3_bucket_region(bucket) })
218
+ FileUtils.mkdir_p local_destination unless Dir.exist? local_destination
219
+
220
+ hl_content = s3.get_object({ bucket: bucket,
221
+ key: s3_key,
222
+ response_target: "#{local_destination}/#{component_name}.highlander.rb"
223
+ })
224
+ # if code execution got so far we consider file exists and download it locally
225
+ component_files = s3.list_objects_v2({ bucket: bucket, prefix: s3_prefix })
226
+ component_files.contents.each {|s3_object|
227
+ file_name = s3_object.key.gsub(s3_prefix, '')
228
+ destination_file = "#{local_destination}/#{file_name}"
229
+ destination_dir = File.dirname(destination_file)
230
+ print "Caching #{file_name} of #{component_name}@#{component_version} in #{destination_dir} ... "
231
+
232
+ FileUtils.mkpath(destination_dir) unless File.exists?(destination_dir)
233
+ s3.get_object({ bucket: bucket, key: s3_object.key, response_target: destination_file })
234
+ print " [OK] \n"
235
+ }
236
+ return local_destination
237
+ rescue => e
238
+ # this handles both nonexisting key and bucket
239
+ puts("#{component_name} not found in s3://#{bucket}/#{prefix}")
240
+ STDERR.puts(e.to_s) unless e.message.include? 'does not exist'
241
+ return nil
242
+ end
243
+ end
244
+
245
+ def findComponentGitTemplate(component_name, component_version)
246
+ if component_name.include? '#'
247
+ parts = component_name.split('#')
248
+ component_name = parts[0]
249
+ component_version = parts[1]
250
+ end
251
+
252
+ # avoid any nres
253
+ component_version = '' if component_version.nil?
254
+
255
+ # if empty or latest branch is empty
256
+ if component_version.empty? or component_version == 'latest' or component_version == 'latest.snapshot'
257
+ branch = ''
258
+ else
259
+ # otherwise component version is actual branch
260
+ branch = component_version
261
+ end
262
+
263
+
264
+ git_url = nil
265
+ if component_name.start_with? 'git:'
266
+ git_url = component_name.gsub('git:', '')
267
+ elsif component_name.start_with? 'github:'
268
+ git_url = "https://github.com/#{component_name.gsub('github:', '')}"
269
+ elsif component_name.start_with? 'github.com:'
270
+ git_url = "https://github.com/#{component_name.gsub('github.com:', '')}"
271
+ end
272
+
273
+ local_path = "#{LOCAL_HIGHLANDER_CACHE_LOCATION}/#{component_name}/#{component_version}"
274
+
275
+ if not git_url.nil?
276
+ component_name, location = findComponentGit(local_path, component_name, component_version, git_url, branch)
277
+ if location.nil?
278
+ raise "Could not resolve component #{component_name}@#{component_version}"
279
+ else
280
+ return component_name, location
281
+ end
282
+ end
283
+
284
+ return nil
285
+ end
286
+
287
+ # Find component and given list of sources
288
+ # @return [Highlander::Factory::Component]
289
+ def findComponent(component_name, component_version = nil)
290
+
291
+ component_version_s = component_version.nil? ? 'latest' : component_version
292
+ component_version = nil if component_version == 'latest'
293
+
294
+ if component_name.include? '@' and (not component_name.start_with? 'git')
295
+ parts = component_name.split('@')
296
+ component_name = parts[0]
297
+ component_version = parts[1]
298
+ end
299
+
300
+ # if component specified as git location
301
+ new_name, candidate_git = findComponentGitTemplate(component_name, component_version_s)
302
+ return buildComponent(new_name, candidate_git) unless candidate_git.nil?
303
+
304
+ # if not git but has .snapshot lookup in default
305
+ if (not component_version.nil?) and component_version.end_with? '.snapshot'
306
+ new_name, default_candidate = findComponentDefault(component_name, component_version_s)
307
+ return buildComponent(new_name, default_candidate) unless default_candidate.nil?
308
+ end
309
+
310
+ # try in all of the component source
311
+ @component_sources.each do |source|
312
+ component_full_name = "#{component_name}@#{component_version.nil? ? 'latest' : component_version}"
313
+ # TODO handle http(s) sources and their download to local
314
+ if source.start_with?('http')
315
+ raise StandardError, 'http(s) sources not supported yet'
316
+ elsif source.start_with?('s3://')
317
+ # s3 candidate
318
+
319
+ s3_candidate = findComponentS3(source, component_name, component_version_s)
320
+ if not s3_candidate.nil?
321
+ # at this point all component files are download to local file system and consumed from there
322
+ return buildComponent(component_name, s3_candidate)
323
+ end
324
+
325
+ else
326
+ # file system candidate
327
+ candidate = "#{source}/#{component_name}"
328
+ candidate = "#{candidate}/#{component_version}" unless component_version.nil?
329
+ candidate_hl_path = "#{candidate}/#{component_name}.highlander.rb"
330
+ candidate2_hl_path = "#{source}/#{component_name}.highlander.rb"
331
+ puts "Trying to load #{component_full_name} from #{candidate} ... "
332
+ if File.exist?(candidate_hl_path)
333
+ return buildComponent(component_name, candidate)
334
+ end
335
+ puts "Trying to load #{component_full_name} from #{source} ... "
336
+ if File.exist?(candidate2_hl_path)
337
+ return buildComponent(component_name, source)
338
+ end unless component_version_s != 'latest'
339
+ end
340
+ end
341
+
342
+ # try default component source on github
343
+ component_name, default_candidate = findComponentDefault(component_name, component_version_s)
344
+ return buildComponent(component_name, default_candidate) unless default_candidate.nil?
345
+
346
+ raise StandardError, "highlander template #{component_name}@#{component_version_s} not located" +
347
+ " in sources #{@component_sources}"
348
+ end
349
+
350
+ def buildComponent(component_name, component_dir)
351
+ component = Component.new(component_name, component_dir)
352
+ component.load_config
353
+ return component
354
+ end
355
+
356
+ end
357
+ end
358
+
359
+ end