Class: Cfhighlander::Compiler::LambdaResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/cfhighlander.compiler.rb

Instance Method Summary collapse

Constructor Details

#initialize(component, lambda_key, workdir, confirm_code_execution = true, mock_resolve = false) ⇒ LambdaResolver

Returns a new instance of LambdaResolver.



239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/cfhighlander.compiler.rb', line 239

def initialize(component, lambda_key, workdir, confirm_code_execution = true,
    mock_resolve=false)
  @component = component
  @lambda_config = @component.config[lambda_key]
  @component_dir = @component.component_dir
  @workdir = workdir
  @metadata = {
      'path' => {},
      'sha256' => {},
      'version' => {}
  }
  @confirm_code_execution = confirm_code_execution
  @mock_resolve = mock_resolve
end

Instance Method Details

#generateMockArchivesObject



404
405
406
407
408
409
410
# File 'lib/cfhighlander.compiler.rb', line 404

def generateMockArchives
  @lambda_config['functions'].each do |name, _|
    @metadata['sha256'][name] = 'mockSHA'
    @metadata['version'][name] = 'mockVersion'
  end
  return []
end

#generateSourceArchivesObject



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/cfhighlander.compiler.rb', line 254

def generateSourceArchives
  return generateMockArchives if @mock_resolve
  archive_paths = []

  # Cached downloads map
  cached_downloads = {}
  @lambda_config['functions'].each do |name, lambda_config|
    # create folder
    out_folder = "#{@workdir}/out/lambdas/"
    timestamp = Time.now.utc.to_i.to_s
    file_name = "#{name}.#{@component.name}.#{@component.version}.#{timestamp}.zip"
    @metadata['path'][name] = file_name
    full_destination_path = "#{out_folder}#{file_name}"
    info_path = "#{out_folder}#{file_name}.info.yaml"
    archive_paths << full_destination_path
    FileUtils.mkdir_p out_folder
    File.write(info_path, {
        'component' => @component.name,
        'function' => name,
        'packagedAt' => timestamp,
        'config' => lambda_config
    }.to_yaml)

    # clear destination if already there
    FileUtils.remove full_destination_path if File.exist? full_destination_path

    # download file if code remote archive
    puts "INFO | Lambda #{name} | Start package process"
    puts "INFO | Lambda #{name} | Destination is #{full_destination_path}"

    md5 = Digest::MD5.new
    md5.update lambda_config['code']
    hash = md5.hexdigest
    cached_location = "#{ENV['HOME']}/.cfhighlander/cache/lambdas/#{hash}"
    if cached_downloads.key? lambda_config['code']
      puts "INFO | Lambda #{name} | Using already downloaded archive #{lambda_config['code']}"
      FileUtils.copy(cached_downloads[lambda_config['code']], full_destination_path)
    elsif File.file? cached_location
      puts "INFO | Lambda #{name} | Using cache from #{cached_location}"
      FileUtils.copy(cached_location, full_destination_path)
    else
      if lambda_config['code'].include? 'http'
        puts "INFO | Lambda #{name} |  Downloading source from #{lambda_config['code']}"
        download = open(lambda_config['code'])
        IO.copy_stream(download, "#{out_folder}/src.zip")
        FileUtils.mkdir_p("#{ENV['HOME']}/.cfhighlander/cache/lambdas")
        FileUtils.copy("#{out_folder}/src.zip", cached_location)
        FileUtils.copy("#{out_folder}/src.zip", full_destination_path)
        puts "INFO | Lambda #{name} | source cached to #{cached_location}"
        cached_downloads[lambda_config['code']] = cached_location
      elsif lambda_config['code'].include? 's3://'
        parts = lambda_config['code'].split('/')
        if parts.size < 4
          STDERR.puts "ERROR | Lambda #{name} |  Lambda function source code from s3 should be in s3://bucket/path format"
          exit -8
        end
        bucket = parts[2]
        key = parts.drop(3).join('/')
        s3 = Aws::S3::Client.new({ region: s3_bucket_region(bucket) })
        puts "INFO | Lambda #{name} | Downloading source from #{lambda_config['code']}"
        s3.get_object({ bucket: bucket, key: key, response_target: cached_location })
        puts "INFO | Lambda #{name} | source cached to #{cached_location}"
        FileUtils.copy(cached_location, full_destination_path)
        cached_downloads[lambda_config['code']] = cached_location
      else
        # zip local code
        component = @component
        component_dir = component.template.template_location
        full_path_candidate_1 = "#{component_dir}/lambdas/#{lambda_config['code']}"
        full_path_candidate_2 = "#{component_dir}/#{lambda_config['code']}"
        full_path_candidate_3 = lambda_config['code']
        full_path = full_path_candidate_1
        full_path = full_path_candidate_2 if (File.exist? full_path_candidate_2 and (not File.exist? full_path))
        full_path = full_path_candidate_3 if (File.exist? full_path_candidate_3 and (not File.exist? full_path))

        # if component extends another component, both parent and child paths should be taken in
        # consideration
        until (File.exist? full_path or component_dir.nil?)
          parent_exists = (not component.extended_component.nil?)
          component = component.extended_component if parent_exists
          component_dir = component.template.template_location if parent_exists
          full_path_candidate_1 = "#{component_dir}/lambdas/#{lambda_config['code']}" if parent_exists
          full_path_candidate_2 = "#{component_dir}/#{lambda_config['code']}" if parent_exists
          full_path_candidate_3 = "#{lambda_config['code']}" if parent_exists

          full_path = full_path_candidate_1
          full_path = full_path_candidate_2 if (File.exist? full_path_candidate_2 and (not File.exist? full_path))
          full_path = full_path_candidate_3 if (File.exist? full_path_candidate_3 and (not File.exist? full_path))

          component_dir = nil unless parent_exists
        end
        if component_dir.nil?
          STDERR.puts "ERROR | Lambda #{name} | Could not find source code directory in component #{@component.name}"
          exit -9
        end

        # lambda source can be either path to file or directory within that file
        # optionally, lambda source code
        lambda_source_dir = File.dirname(full_path)
        lambda_source_dir = full_path if Pathname.new(full_path).directory?

        # executing package command can generate files. We DO NOT want this file in source directory,
        # but rather in intermediate directory
        tmp_source_dir = "#{@workdir}/out/lambdas/tmp/#{name}"
        FileUtils.rmtree(File.dirname(tmp_source_dir)) if File.exist? tmp_source_dir
        FileUtils.mkpath(File.dirname(tmp_source_dir))
        FileUtils.copy_entry(lambda_source_dir, tmp_source_dir)
        lambda_source_dir = tmp_source_dir

        # Lambda function source code allows pre-processing (e.g. install code dependencies)
        unless lambda_config['package_cmd'].nil?
          puts "INFO | Lambda #{name} | Following code will be executed to generate lambda function #{name}:\n\n#{lambda_config['package_cmd']}\n\n"

          if @confirm_code_execution
            exit -7 unless HighLine.agree('Proceed (y/n)?')
          end

          package_cmd = "cd #{lambda_source_dir} && #{lambda_config['package_cmd']}"
          puts 'Processing package command...'
          package_result = system(package_cmd)
          unless package_result
            puts "ERROR | Lambda #{name} | create package - following command failed\n\n#{package_cmd}\n\n"
            exit -4
          end
        end
        File.delete full_destination_path if File.exist? full_destination_path

        # if source is already zip file, just add manifest to it
        if full_path.end_with? '.zip'
          FileUtils.copy full_path, full_destination_path
        else
          zip_generator = Cfhighlander::Util::ZipFileGenerator.new(lambda_source_dir, full_destination_path)
          zip_generator.write
        end
      end
    end
    # add version information to avoid same package ever deployed 2 times
    Zip::File.open(full_destination_path) do |zipfile|
      zipfile.add 'hlpackage_info.txt', info_path
    end
    sha256 = Digest::SHA256.file full_destination_path
    sha256 = sha256.base64digest
    puts "INFO | Lambda #{name} | Created zip package #{full_destination_path} with digest #{sha256}"
    @metadata['sha256'][name] = sha256
    @metadata['version'][name] = timestamp
  end if ((not @lambda_config.nil?) and @lambda_config.key? 'functions')

  return archive_paths
end

#mergeComponentConfigObject



412
413
414
415
416
417
418
419
420
421
422
# File 'lib/cfhighlander.compiler.rb', line 412

def mergeComponentConfig
  if @component.config.key? 'lambda_metadata'
    @metadata.each do |mk, mh|
      mh.each do |k, v|
        @component.config['lambda_metadata'][mk][k] = v
      end
    end
  else
    @component.config['lambda_metadata'] = @metadata
  end
end