class Cfhighlander::Compiler::LambdaResolver

Public Class Methods

new(component, lambda_key, workdir, confirm_code_execution = true, mock_resolve=false) click to toggle source
# File lib/cfhighlander.compiler.rb, line 237
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

Public Instance Methods

generateMockArchives() click to toggle source
# File lib/cfhighlander.compiler.rb, line 402
def generateMockArchives
  @lambda_config['functions'].each do |name, _|
    @metadata['sha256'][name] = 'mockSHA'
    @metadata['version'][name] = 'mockVersion'
  end
  return []
end
generateSourceArchives() click to toggle source
# File lib/cfhighlander.compiler.rb, line 252
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
mergeComponentConfig() click to toggle source
# File lib/cfhighlander.compiler.rb, line 410
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