Class: CloudFormationTool::CloudFormation::LambdaCode

Inherits:
Object
  • Object
show all
Includes:
Storable
Defined in:
lib/cloud_formation_tool/cloud_formation/lambda_code.rb

Constant Summary

Constants included from CloudFormationTool

VERSION

Instance Method Summary collapse

Methods included from Storable

#cached_object, #make_filename, #prefix, #upload

Methods included from CloudFormationTool

#aws_config, #awsas, #awscdn, #awscf, #awscreds, #awsec2, #awsecs, #awss3, #cf_bucket_name, #find_profile, #profile, #region, #s3_bucket_name

Constructor Details

#initialize(code, tpl, runtime = nil) ⇒ LambdaCode

Returns a new instance of LambdaCode.



10
11
12
13
14
15
16
17
18
19
20
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 10

def initialize(code, tpl, runtime = nil)
  @tpl = tpl
  @zipfile_safe = runtime.to_s.match(%r{^(nodejs|python)}) != nil
  @data = code
  @data['Url'] = @data.delete 'URL' if @data.key? 'URL' # normalize to CF convention if seeing old key
  if @data.key? 'Url'
    handle_url
  elsif @data.key? 'Path'
    handle_path
  end
end

Instance Method Details

#already_in_cache(uri_str) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 80

def already_in_cache(uri_str)
  limit = 10
  url = URI(uri_str)
  until url.nil?
    begin
      raise ArgumentError, 'too many HTTP redirects' if limit == 0
      Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
        request = Net::HTTP::Get.new(url)
        http.request(request) do |response|
          # handle redirects like Github likes to do
          case response
            when Net::HTTPSuccess then
              url = nil
              break if check_cached(response['ETag']) # dont read the body if its already cached
            when Net::HTTPRedirection then
              location = response['location']
              debug "Cache check redirected to #{location}"
              limit = limit - 1
              response.body
              url = URI(location)
            else
              raise ArgumentError, "Error getting response: #{response}"
          end
        end
      end
    rescue IOError => e
      retry unless url.nil?
    end
  end
  !@s3_url.nil?
end

#check_cached(etag) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 112

def check_cached(etag)
  unless etag.nil?
    etag.gsub!(/"/,'')
    o = cached_object(etag)
  end
  if o.nil?
    false
  else
    @s3_url = URI(o.public_url)
    true
  end
end

#fetch_from_url(uri_str) ⇒ Object



125
126
127
128
129
130
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 125

def fetch_from_url(uri_str)
  $__fetch_cache ||= Hash.new do |h, url|
    h[url] = fetch_from_url_real(url)
  end
  $__fetch_cache[uri_str]
end

#fetch_from_url_real(uri_str, limit = 10) ⇒ Object

Raises:

  • (ArgumentError)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 132

def fetch_from_url_real(uri_str, limit = 10)
  raise ArgumentError, 'too many HTTP redirects' if limit == 0
  response = Net::HTTP.get_response(URI(uri_str))
  case response
  when Net::HTTPSuccess then
    response
  when Net::HTTPRedirection then
    location = response['location']
    debug "redirected to #{location}"
    fetch_from_url_real(location, limit - 1)
  else
    raise CloudFormationTool::Errors::AppError, "Error downloading #{url}: #{response.value}"
  end
end

#handle_file(path) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 49

def handle_file(path)
  @data.delete 'Path'
  contents = File.read(path)
  if contents.size <= 4096 and @zipfile_safe # Convert files to ZipFile
    @data['ZipFile'] = contents
  else # upload and hope for the best
    ext = path.split('.').last
    ctype = case ext
    when 'jar'
      'application/java-archive'
    when 'zip'
      'application/zip'
    else
      'application/octet-stream'
    end
    @s3_url = URI(upload(make_filename(ext), contents, mime_type: ctype, gzip: false))
    log "uploaded Lambda function to #{@s3_url}"
  end
end

#handle_pathObject



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 36

def handle_path
  @data['Path'] = path = @tpl.resolveVal(@data['Path'])
  return unless path.is_a? String
  debug "Reading Lambda code from #{path}"
  path = if path.start_with? "/" then path else "#{@tpl.basedir}/#{path}" end
  if File.directory?(path)
    @s3_url = URI(upload(make_filename('zip'), zip_path(path), mime_type: 'application/zip', gzip: false))
    log "uploaded Lambda function to #{@s3_url}"
  else
    handle_file path
  end
end

#handle_urlObject



22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 22

def handle_url
  debug "Trying Lambda code from #{@data['Url']}"
  @data['Url'] = url = @tpl.resolveVal(@data['Url'])
  return unless url.is_a? String
  log "Downloading Lambda code from #{url}"
  if already_in_cache(url)
    debug "Reusing remote cached object instead of downloading"
  else
    res = fetch_from_url(url)
    @s3_url = URI(upload(make_filename(url.split('.').last), res.body, mime_type: res['content-type'], gzip: false))
    log "uploaded Lambda function to #{@s3_url}"
  end
end

#rdir(path, prefix = '', &block) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 158

def rdir path, prefix = '', &block
  ents = []
  (Dir.entries(path) - %w(. ..)).collect do |ent|
    diskpath = File.join(path,ent)
    localpath = prefix.length>0 ? File.join(prefix,ent) : ent
    if block_given?
      if File.directory? diskpath
        rdir diskpath, localpath, &block
      else
        yield localpath
      end
    end
    if File.directory? diskpath
      rdir diskpath, localpath
    else
      ent
    end
  end.flatten
end

#to_cloudformationObject



147
148
149
150
151
152
153
154
155
156
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 147

def to_cloudformation
  if @s3_url.nil?
    @data
  else
    {
      'S3Bucket' => @s3_url.hostname.split('.').first,
      'S3Key' => @s3_url.path[1..-1]
    }
  end
end

#zip_path(path) ⇒ Object



69
70
71
72
73
74
75
76
77
78
# File 'lib/cloud_formation_tool/cloud_formation/lambda_code.rb', line 69

def zip_path(path)
  Zip::OutputStream.write_buffer do |zf|
    rdir path do |ent|
      debug "Deflating #{ent}"
      filepath = File.join(path,ent)
      zf.put_next_entry ::Zip::Entry.new(nil, ent, nil, nil, nil, nil, nil, nil, ::Zip::DOSTime.at(File.mtime(filepath).to_i))
      zf.write File.read(filepath) 
    end
  end.string
end