cfhighlander 0.2.1.alpha.43 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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