Class: DPL::Provider::Lambda

Inherits:
DPL::Provider show all
Defined in:
lib/dpl/provider/lambda.rb

Instance Method Summary collapse

Instance Method Details

#access_key_idObject



14
15
16
# File 'lib/dpl/provider/lambda.rb', line 14

def access_key_id
  options[:access_key_id] || context.env['AWS_ACCESS_KEY_ID'] || raise(Error, "missing access_key_id")
end

#check_authObject



169
170
171
# File 'lib/dpl/provider/lambda.rb', line 169

def check_auth
  log "Using Access Key: #{access_key_id[-4..-1].rjust(20, '*')}"
end

#cleanupObject



238
239
# File 'lib/dpl/provider/lambda.rb', line 238

def cleanup
end

#create_zip(dest_file_path, src_directory_path, files) ⇒ Object



155
156
157
158
159
160
161
162
163
# File 'lib/dpl/provider/lambda.rb', line 155

def create_zip(dest_file_path, src_directory_path, files)
  Zip::File.open(dest_file_path, Zip::File::CREATE) do |zipfile|
    files.each do |file|
      zipfile.add(file.sub(src_directory_path + File::SEPARATOR, ''), file)
    end
  end

  dest_file_path
end

#dead_letter_arnObject



185
186
187
# File 'lib/dpl/provider/lambda.rb', line 185

def dead_letter_arn
  options[:dead_letter_arn] ? { :target_arn => options[:dead_letter_arn]} : nil
end

#default_descriptionObject



209
210
211
# File 'lib/dpl/provider/lambda.rb', line 209

def default_description
  "Deploy build #{context.env['TRAVIS_BUILD_NUMBER']} to AWS Lambda via Travis CI"
end

#default_kms_key_arnObject



193
194
195
# File 'lib/dpl/provider/lambda.rb', line 193

def default_kms_key_arn
  nil
end

#default_memory_sizeObject



213
214
215
# File 'lib/dpl/provider/lambda.rb', line 213

def default_memory_size
  128
end

#default_module_nameObject



217
218
219
# File 'lib/dpl/provider/lambda.rb', line 217

def default_module_name
  'index'
end

#default_runtimeObject



201
202
203
# File 'lib/dpl/provider/lambda.rb', line 201

def default_runtime
  'nodejs'
end

#default_timeoutObject



205
206
207
# File 'lib/dpl/provider/lambda.rb', line 205

def default_timeout
  3 # seconds
end

#environment_variablesObject



181
182
183
# File 'lib/dpl/provider/lambda.rb', line 181

def environment_variables
  options[:environment_variables] ? { :variables => split_string_array_to_hash(options[:environment_variables]) } : nil
end

#function_tagsObject



197
198
199
# File 'lib/dpl/provider/lambda.rb', line 197

def function_tags
  options[:function_tags] ? split_string_array_to_hash(options[:function_tags]) : nil
end

#function_zipObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/dpl/provider/lambda.rb', line 119

def function_zip
  target_zip_path = File.absolute_path(options[:zip] || Dir.pwd)
  dest_file_path = output_file_path

  if File.directory?(target_zip_path)
    zip_directory(dest_file_path, target_zip_path)
  elsif File.file?(target_zip_path)
    zip_file(dest_file_path, target_zip_path)
  else
    error('Invalid zip option. If set, must be path to directory, js file, or a zip file.')
  end

  File.new(dest_file_path)
end

#handlerObject



112
113
114
115
116
117
# File 'lib/dpl/provider/lambda.rb', line 112

def handler
  module_name = options[:module_name] || default_module_name
  handler_name = option(:handler_name)

  "#{module_name}.#{handler_name}"
end

#lambdaObject



10
11
12
# File 'lib/dpl/provider/lambda.rb', line 10

def lambda
  @lambda ||= ::Aws::Lambda::Client.new(lambda_options)
end

#lambda_optionsObject



22
23
24
25
26
27
# File 'lib/dpl/provider/lambda.rb', line 22

def lambda_options
  {
      region:      options[:region] || 'us-east-1',
      credentials: ::Aws::Credentials.new(access_key_id, secret_access_key)
  }
end

#needs_key?Boolean

Returns:

  • (Boolean)


165
166
167
# File 'lib/dpl/provider/lambda.rb', line 165

def needs_key?
  false
end

#output_file_pathObject



173
174
175
# File 'lib/dpl/provider/lambda.rb', line 173

def output_file_path
  @output_file_path ||= '/tmp/' + random_chars(8) + '-lambda.zip'
end

#publishObject



221
222
223
# File 'lib/dpl/provider/lambda.rb', line 221

def publish
  !!options[:publish]
end

#push_appObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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/dpl/provider/lambda.rb', line 29

def push_app

  # The original LambdaPreview client supported create/update in one call
  # To keep compatibility we try to fetch the function and then decide
  # whether to update the code or create a new function

  function_name = options[:name] || option(:function_name)

  begin
    response = lambda.get_function({function_name: function_name})

    log "Function #{function_name} already exists, updating."

    # Options defined at
    #   https://docs.aws.amazon.com/sdkforruby/api/Aws/Lambda/Client.html#update_function_configuration-instance_method
    response = lambda.update_function_configuration({
                 function_name:        function_name,
                 description:          options[:description]    || default_description,
                 timeout:              options[:timeout]        || default_timeout,
                 memory_size:          options[:memory_size]    || default_memory_size,
                 role:                 option(:role),
                 handler:              handler,
                 runtime:              options[:runtime]        || default_runtime,
                 vpc_config:           vpc_config,
                 environment:          environment_variables,
                 dead_letter_config:   dead_letter_arn,
                 kms_key_arn:          options[:kms_key_arn] || default_kms_key_arn,
                 tracing_config:       tracing_mode
               })

    log "Updated configuration of function: #{response.function_name}."

    if function_tags
      log "Add tags to function #{response.function_name}."
      response = lambda.tag_resource({
              resource: response.function_arn,
              tags: function_tags
            })
    end

    # Options defined at
    #   https://docs.aws.amazon.com/sdkforruby/api/Aws/Lambda/Client.html#update_function_code-instance_method
    response = lambda.update_function_code({
                                             function_name:  options[:name] || option(:function_name),
                                             zip_file:       function_zip,
                                             publish:        publish
                                         })

    log "Updated code of function: #{response.function_name}."
  rescue ::Aws::Lambda::Errors::ResourceNotFoundException
    log "Function #{function_name} does not exist, creating."
    # Options defined at
    #   https://docs.aws.amazon.com/lambda/latest/dg/API_CreateFunction.html
    response = lambda.create_function({
                function_name:        options[:name]           || option(:function_name),
                description:          options[:description]    || default_description,
                timeout:              options[:timeout]        || default_timeout,
                memory_size:          options[:memory_size]    || default_memory_size,
                role:                 option(:role),
                handler:              handler,
                code: {
                 zip_file:           function_zip,
                },
                runtime:              options[:runtime]        || default_runtime,
                publish:              publish,
                vpc_config:           vpc_config,
                environment:          environment_variables,
                dead_letter_config:   dead_letter_arn,
                kms_key_arn:          options[:kms_key_arn] || default_kms_key_arn,
                tracing_config:       tracing_mode,
                tags:                 function_tags
              })

    log "Created lambda: #{response.function_name}."
  end
rescue ::Aws::Lambda::Errors::ServiceException => exception
  error(exception.message)
rescue ::Aws::Lambda::Errors::InvalidParameterValueException => exception
  error(exception.message)
rescue ::Aws::Lambda::Errors::ResourceNotFoundException => exception
  error(exception.message)
end

#random_chars(count = 8) ⇒ Object



234
235
236
# File 'lib/dpl/provider/lambda.rb', line 234

def random_chars(count=8)
  (36**(count-1) + rand(36**count - 36**(count-1))).to_s(36)
end

#secret_access_keyObject



18
19
20
# File 'lib/dpl/provider/lambda.rb', line 18

def secret_access_key
  options[:secret_access_key] || context.env['AWS_SECRET_ACCESS_KEY'] || raise(Error, "missing secret_access_key")
end

#split_string_array_to_hash(arr, delimiter = "=") ⇒ Object



225
226
227
228
229
230
231
232
# File 'lib/dpl/provider/lambda.rb', line 225

def split_string_array_to_hash(arr, delimiter="=")
  variables = {}
  Array(arr).map do |val|
    keyval = val.split(delimiter)
    variables[keyval[0]] = keyval[1]
  end
  variables
end

#tracing_modeObject



189
190
191
# File 'lib/dpl/provider/lambda.rb', line 189

def tracing_mode
  options[:tracing_mode] ? { :mode => options[:tracing_mode]} : nil
end

#uncleanupObject



241
242
# File 'lib/dpl/provider/lambda.rb', line 241

def uncleanup
end

#vpc_configObject



177
178
179
# File 'lib/dpl/provider/lambda.rb', line 177

def vpc_config
  options[:subnet_ids] && options[:security_group_ids] ? { :subnet_ids => Array(options[:subnet_ids]), :security_group_ids => Array(options[:security_group_ids]) } : nil
end

#zip_directory(dest_file_path, target_directory_path) ⇒ Object



148
149
150
151
152
153
# File 'lib/dpl/provider/lambda.rb', line 148

def zip_directory(dest_file_path, target_directory_path)
  glob_args = Array(File.join(target_directory_path, '**', '**'))
  glob_args << File::FNM_DOTMATCH if options[:dot_match]
  files = Dir.glob(*glob_args).reject {|ent| File.directory?(ent)}
  create_zip(dest_file_path, target_directory_path, files)
end

#zip_file(dest_file_path, target_file_path) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/dpl/provider/lambda.rb', line 134

def zip_file(dest_file_path, target_file_path)
  if File.extname(target_file_path) == '.zip'
    # Just copy it to the destination right away, since it is already a zip.
    FileUtils.cp(target_file_path, dest_file_path)
    dest_file_path
  else
    # Zip up the file.
    src_directory_path = File.dirname(target_file_path)
    files = [ target_file_path ]

    create_zip(dest_file_path, src_directory_path, files)
  end
end