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



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

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

#cleanupObject



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

def cleanup
end

#create_zip(dest_file_path, src_directory_path, files) ⇒ Object



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

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



187
188
189
# File 'lib/dpl/provider/lambda.rb', line 187

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

#default_descriptionObject



211
212
213
# File 'lib/dpl/provider/lambda.rb', line 211

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

#default_kms_key_arnObject



195
196
197
# File 'lib/dpl/provider/lambda.rb', line 195

def default_kms_key_arn
  nil
end

#default_memory_sizeObject



215
216
217
# File 'lib/dpl/provider/lambda.rb', line 215

def default_memory_size
  128
end

#default_module_nameObject



219
220
221
# File 'lib/dpl/provider/lambda.rb', line 219

def default_module_name
  'index'
end

#default_runtimeObject



203
204
205
# File 'lib/dpl/provider/lambda.rb', line 203

def default_runtime
  'nodejs'
end

#default_timeoutObject



207
208
209
# File 'lib/dpl/provider/lambda.rb', line 207

def default_timeout
  3 # seconds
end

#environment_variablesObject



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

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

#function_tagsObject



199
200
201
# File 'lib/dpl/provider/lambda.rb', line 199

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)


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

def needs_key?
  false
end

#output_file_pathObject



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

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

#publishObject



223
224
225
# File 'lib/dpl/provider/lambda.rb', line 223

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



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

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



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

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

#tracing_modeObject



191
192
193
# File 'lib/dpl/provider/lambda.rb', line 191

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

#uncleanupObject



243
244
# File 'lib/dpl/provider/lambda.rb', line 243

def uncleanup
end

#vpc_configObject



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

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



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

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
147
# File 'lib/dpl/provider/lambda.rb', line 134

def zip_file(dest_file_path, target_file_path)
  # Test .zip or .jar file extensions.
  if %w[.zip .jar].include?(File.extname(target_file_path))
    # Just copy it to the destination right away, since it is already a zip or jar.
    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