class CFnDK::TemplatePackager

Public Class Methods

new(template_file, region, package, global_config, s3_client, sts_client) click to toggle source
# File lib/cfndk/template_packager.rb, line 7
def initialize(template_file, region, package, global_config, s3_client, sts_client)
  @template_file = template_file
  @region = region
  @package = package
  @global_config = global_config
  @s3_client = s3_client
  @sts_client = sts_client
  @template_body = nil
  @is_uploaded = false
end

Public Instance Methods

large_template?() click to toggle source
# File lib/cfndk/template_packager.rb, line 18
def large_template?
  template_body.size > 51200
end
package_templte() click to toggle source
# File lib/cfndk/template_packager.rb, line 43
def package_templte
  if !@template_body
    if !@package
      @template_body = File.open(@template_file, 'r').read
      return @template_body
    end
    orgTemplate = File.open(@template_file, 'r').read
    CFnDK.logger.debug('Original Template:' + orgTemplate)
    if is_json?(orgTemplate)
      data = JSON.parse(orgTemplate)
    else
      data = YAML.load(orgTemplate.gsub(/!/, '____CFNDK!____'))
    end
    
    if data['Resources']
      data['Resources'].each do |k, v|
        next unless v.key?('Type')
        t = v['Type'] 
        properties = v['Properties'] || {}
        case t
        when 'AWS::CloudFormation::Stack' then
          if properties['TemplateURL'] =~ /^\s*./
            tp = TemplatePackager.new(File.dirname(@template_file) + '/' + properties['TemplateURL'].sub(/^\s*.\//, ''), @region, @package, @global_config, @s3_client, @sts_client)
            v['Properties']['TemplateURL'] = tp.upload_template_file
          end
        when 'AWS::Lambda::Function' then
          if properties['Code'].kind_of?(String)
            result = upload_zip_file(File.dirname(@template_file) + '/' + properties['Code'].sub(/^\s*.\//, ''))
            v['Properties']['Code'] = {
              'S3Bucket' => result['bucket'],
              'S3Key' => result['key']  
            }
          end
        when 'AWS::Serverless::Function' then
          if properties['CodeUri'].kind_of?(String)
            result = upload_zip_file(File.dirname(@template_file) + '/' + properties['CodeUri'].sub(/^\s*.\//, ''))
            v['Properties']['CodeUri'] = {
              'Bucket' => result['bucket'],
              'Key' => result['key']  
            }
          end
        when 'AWS::Serverless::Api' then
          if properties['DefinitionUri'].kind_of?(String)
            result = upload_file(File.dirname(@template_file) + '/' + properties['DefinitionUri'].sub(/^\s*.\//, ''))
            v['Properties']['DefinitionUri'] = {
              'Bucket' => result['bucket'],
              'Key' => result['key']  
            }
          end
        when 'AWS::ApiGateway::RestApi' then
          if properties['BodyS3Location'].kind_of?(String)
            result = upload_file(File.dirname(@template_file) + '/' + properties['BodyS3Location'].sub(/^\s*.\//, ''))
            v['Properties']['BodyS3Location'] = {
              'Bucket' => result['bucket'],
              'Key' => result['key']  
            }
          end
        end
        ## TODO support resources
        # * AWS::AppSync::GraphQLSchema DefinitionS3Location
        # * AWS::AppSync::Resolver RequestMappingTemplateS3Location
        # * AWS::AppSync::Resolver ResponseMappingTemplateS3Location
        # * AWS::ElasticBeanstalk::ApplicationVersion SourceBundle
        # * AWS::Glue::Job Command ScriptLocation
        # * AWS::Include Location
      end
    end

    if is_json?(orgTemplate)
      @template_body = JSON.dump(data)
    else
      @template_body = YAML.dump_stream(data).gsub(/____CFNDK!____/, '!')
    end
    CFnDK.logger.info('Template Packager diff: ' + @template_file) 
    CFnDK.logger.info(CFnDK.diff(orgTemplate, @template_body).to_s)
    CFnDK.logger.debug('Package Template size: ' + @template_body.size.to_s)
    CFnDK.logger.debug('Package Template:' + @template_body)
  end
  @template_body
end
template_body() click to toggle source
# File lib/cfndk/template_packager.rb, line 22
def template_body
  package_templte
end
upload_template_file() click to toggle source
# File lib/cfndk/template_packager.rb, line 26
def upload_template_file
  key = [@global_config.s3_template_hash, @template_file].compact.join('/')
  url = "https://s3.amazonaws.com/#{bucket_name}/#{key}"

  unless @is_uploaded
    create_bucket
    @s3_client.put_object(
      body: template_body,
      bucket: bucket_name,
      key: key
    )
    @is_uploaded = true
    CFnDK.logger.info('Put S3 object: ' + url + ' Size: ' + template_body.size.to_s)
  end
  url
end

Private Instance Methods

bucket_name() click to toggle source
# File lib/cfndk/template_packager.rb, line 197
def bucket_name
  resp = @sts_client.get_caller_identity({})
  resp.account + '-' + @region + '-' + @global_config.s3_template_bucket
end
create_bucket() click to toggle source
# File lib/cfndk/template_packager.rb, line 170
def create_bucket
  begin
    @s3_client.head_bucket(bucket: bucket_name)
  rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::Forbidden
    @s3_client.create_bucket(bucket: bucket_name)
    CFnDK.logger.info('Creatt S3 bucket: ' + bucket_name)
    @s3_client.put_bucket_lifecycle_configuration(
      bucket: bucket_name,
      lifecycle_configuration: {
        rules: [
          {
            expiration: {
              days: 1,
            },
            status: 'Enabled',
            id: 'Delete Old Files',
            prefix: '',
            abort_incomplete_multipart_upload: {
              days_after_initiation: 1,
            },
          },
        ],
      }
    )
  end      
end
is_json?(str) click to toggle source
# File lib/cfndk/template_packager.rb, line 202
def is_json?(str)
  begin
    !!JSON.parse(str)
  rescue
    false
  end
end
upload_file(path) click to toggle source
# File lib/cfndk/template_packager.rb, line 153
def upload_file(path)
  create_bucket
  key = [@global_config.s3_template_hash, path.sub(/^.\//, '')].compact.join('/')

  @s3_client.put_object(
    body: File.open(path, 'r').read,
    bucket: bucket_name,
    key: key
  )
  url = "https://s3.amazonaws.com/#{bucket_name}/#{key}"
  CFnDK.logger.info('Put S3 object: ' + url)
  {
    'bucket' => bucket_name,
    'key' => key
  }
end
upload_zip_file(path) click to toggle source
# File lib/cfndk/template_packager.rb, line 126
def upload_zip_file(path)
  create_bucket
  key = [@global_config.s3_template_hash, path.sub(/^.\//, '') + ".zip"].compact.join('/')


  buffer = Zip::OutputStream.write_buffer do |out|
    Dir.glob(path + '/**/*') do |file|
      if (!File.directory?(file))
        out.put_next_entry(file.delete_prefix(path + '/'))
        out.write(File.open(file, 'r').read)
      end
    end
  end

  @s3_client.put_object(
    body: buffer.string,
    bucket: bucket_name,
    key: key
  )
  url = "https://s3.amazonaws.com/#{bucket_name}/#{key}"
  CFnDK.logger.info('Put S3 object: ' + url)
  {
    'bucket' => bucket_name,
    'key' => key
  }
end