1# Copyright (c) 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the 5# "Software"), to deal in the Software without restriction, including 6# without limitation the rights to use, copy, modify, merge, publish, dis- 7# tribute, sublicense, and/or sell copies of the Software, and to permit 8# persons to whom the Software is furnished to do so, subject to the fol- 9# lowing conditions: 10# 11# The above copyright notice and this permission notice shall be included 12# in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21# 22import os 23 24from boto.compat import json 25from boto.exception import JSONResponseError 26from boto.connection import AWSAuthConnection 27from boto.regioninfo import RegionInfo 28from boto.awslambda import exceptions 29 30 31class AWSLambdaConnection(AWSAuthConnection): 32 """ 33 AWS Lambda 34 **Overview** 35 36 This is the AWS Lambda API Reference. The AWS Lambda Developer 37 Guide provides additional information. For the service overview, 38 go to `What is AWS Lambda`_, and for information about how the 39 service works, go to `AWS LambdaL How it Works`_ in the AWS Lambda 40 Developer Guide. 41 """ 42 APIVersion = "2014-11-11" 43 DefaultRegionName = "us-east-1" 44 DefaultRegionEndpoint = "lambda.us-east-1.amazonaws.com" 45 ResponseError = JSONResponseError 46 47 _faults = { 48 "InvalidRequestContentException": exceptions.InvalidRequestContentException, 49 "ResourceNotFoundException": exceptions.ResourceNotFoundException, 50 "InvalidParameterValueException": exceptions.InvalidParameterValueException, 51 "ServiceException": exceptions.ServiceException, 52 } 53 54 55 def __init__(self, **kwargs): 56 region = kwargs.get('region') 57 if not region: 58 region = RegionInfo(self, self.DefaultRegionName, 59 self.DefaultRegionEndpoint) 60 else: 61 del kwargs['region'] 62 kwargs['host'] = region.endpoint 63 super(AWSLambdaConnection, self).__init__(**kwargs) 64 self.region = region 65 66 def _required_auth_capability(self): 67 return ['hmac-v4'] 68 69 def add_event_source(self, event_source, function_name, role, 70 batch_size=None, parameters=None): 71 """ 72 Identifies an Amazon Kinesis stream as the event source for an 73 AWS Lambda function. AWS Lambda invokes the specified function 74 when records are posted to the stream. 75 76 This is the pull model, where AWS Lambda invokes the function. 77 For more information, go to `AWS LambdaL How it Works`_ in the 78 AWS Lambda Developer Guide. 79 80 This association between an Amazon Kinesis stream and an AWS 81 Lambda function is called the event source mapping. You 82 provide the configuration information (for example, which 83 stream to read from and which AWS Lambda function to invoke) 84 for the event source mapping in the request body. 85 86 This operation requires permission for the `iam:PassRole` 87 action for the IAM role. It also requires permission for the 88 `lambda:AddEventSource` action. 89 90 :type event_source: string 91 :param event_source: The Amazon Resource Name (ARN) of the Amazon 92 Kinesis stream that is the event source. Any record added to this 93 stream causes AWS Lambda to invoke your Lambda function. AWS Lambda 94 POSTs the Amazon Kinesis event, containing records, to your Lambda 95 function as JSON. 96 97 :type function_name: string 98 :param function_name: The Lambda function to invoke when AWS Lambda 99 detects an event on the stream. 100 101 :type role: string 102 :param role: The ARN of the IAM role (invocation role) that AWS Lambda 103 can assume to read from the stream and invoke the function. 104 105 :type batch_size: integer 106 :param batch_size: The largest number of records that AWS Lambda will 107 give to your function in a single event. The default is 100 108 records. 109 110 :type parameters: map 111 :param parameters: A map (key-value pairs) defining the configuration 112 for AWS Lambda to use when reading the event source. Currently, AWS 113 Lambda supports only the `InitialPositionInStream` key. The valid 114 values are: "TRIM_HORIZON" and "LATEST". The default value is 115 "TRIM_HORIZON". For more information, go to `ShardIteratorType`_ in 116 the Amazon Kinesis Service API Reference. 117 118 """ 119 120 uri = '/2014-11-13/event-source-mappings/' 121 params = { 122 'EventSource': event_source, 123 'FunctionName': function_name, 124 'Role': role, 125 } 126 headers = {} 127 query_params = {} 128 if batch_size is not None: 129 params['BatchSize'] = batch_size 130 if parameters is not None: 131 params['Parameters'] = parameters 132 return self.make_request('POST', uri, expected_status=200, 133 data=json.dumps(params), headers=headers, 134 params=query_params) 135 136 def delete_function(self, function_name): 137 """ 138 Deletes the specified Lambda function code and configuration. 139 140 This operation requires permission for the 141 `lambda:DeleteFunction` action. 142 143 :type function_name: string 144 :param function_name: The Lambda function to delete. 145 146 """ 147 148 uri = '/2014-11-13/functions/{0}'.format(function_name) 149 return self.make_request('DELETE', uri, expected_status=204) 150 151 def get_event_source(self, uuid): 152 """ 153 Returns configuration information for the specified event 154 source mapping (see AddEventSource). 155 156 This operation requires permission for the 157 `lambda:GetEventSource` action. 158 159 :type uuid: string 160 :param uuid: The AWS Lambda assigned ID of the event source mapping. 161 162 """ 163 164 uri = '/2014-11-13/event-source-mappings/{0}'.format(uuid) 165 return self.make_request('GET', uri, expected_status=200) 166 167 def get_function(self, function_name): 168 """ 169 Returns the configuration information of the Lambda function 170 and a presigned URL link to the .zip file you uploaded with 171 UploadFunction so you can download the .zip file. Note that 172 the URL is valid for up to 10 minutes. The configuration 173 information is the same information you provided as parameters 174 when uploading the function. 175 176 This operation requires permission for the 177 `lambda:GetFunction` action. 178 179 :type function_name: string 180 :param function_name: The Lambda function name. 181 182 """ 183 184 uri = '/2014-11-13/functions/{0}'.format(function_name) 185 return self.make_request('GET', uri, expected_status=200) 186 187 def get_function_configuration(self, function_name): 188 """ 189 Returns the configuration information of the Lambda function. 190 This the same information you provided as parameters when 191 uploading the function by using UploadFunction. 192 193 This operation requires permission for the 194 `lambda:GetFunctionConfiguration` operation. 195 196 :type function_name: string 197 :param function_name: The name of the Lambda function for which you 198 want to retrieve the configuration information. 199 200 """ 201 202 uri = '/2014-11-13/functions/{0}/configuration'.format(function_name) 203 return self.make_request('GET', uri, expected_status=200) 204 205 def invoke_async(self, function_name, invoke_args): 206 """ 207 Submits an invocation request to AWS Lambda. Upon receiving 208 the request, Lambda executes the specified function 209 asynchronously. To see the logs generated by the Lambda 210 function execution, see the CloudWatch logs console. 211 212 This operation requires permission for the 213 `lambda:InvokeAsync` action. 214 215 :type function_name: string 216 :param function_name: The Lambda function name. 217 218 :type invoke_args: blob 219 :param invoke_args: JSON that you want to provide to your Lambda 220 function as input. 221 222 """ 223 uri = '/2014-11-13/functions/{0}/invoke-async/'.format(function_name) 224 headers = {} 225 query_params = {} 226 try: 227 content_length = str(len(invoke_args)) 228 except (TypeError, AttributeError): 229 # If a file like object is provided and seekable, try to retrieve 230 # the file size via fstat. 231 try: 232 invoke_args.tell() 233 except (AttributeError, OSError, IOError): 234 raise TypeError( 235 "File-like object passed to parameter " 236 "``invoke_args`` must be seekable." 237 ) 238 content_length = str(os.fstat(invoke_args.fileno()).st_size) 239 headers['Content-Length'] = content_length 240 return self.make_request('POST', uri, expected_status=202, 241 data=invoke_args, headers=headers, 242 params=query_params) 243 244 def list_event_sources(self, event_source_arn=None, function_name=None, 245 marker=None, max_items=None): 246 """ 247 Returns a list of event source mappings. For each mapping, the 248 API returns configuration information (see AddEventSource). 249 You can optionally specify filters to retrieve specific event 250 source mappings. 251 252 This operation requires permission for the 253 `lambda:ListEventSources` action. 254 255 :type event_source_arn: string 256 :param event_source_arn: The Amazon Resource Name (ARN) of the Amazon 257 Kinesis stream. 258 259 :type function_name: string 260 :param function_name: The name of the AWS Lambda function. 261 262 :type marker: string 263 :param marker: Optional string. An opaque pagination token returned 264 from a previous `ListEventSources` operation. If present, specifies 265 to continue the list from where the returning call left off. 266 267 :type max_items: integer 268 :param max_items: Optional integer. Specifies the maximum number of 269 event sources to return in response. This value must be greater 270 than 0. 271 272 """ 273 274 uri = '/2014-11-13/event-source-mappings/' 275 params = {} 276 headers = {} 277 query_params = {} 278 if event_source_arn is not None: 279 query_params['EventSource'] = event_source_arn 280 if function_name is not None: 281 query_params['FunctionName'] = function_name 282 if marker is not None: 283 query_params['Marker'] = marker 284 if max_items is not None: 285 query_params['MaxItems'] = max_items 286 return self.make_request('GET', uri, expected_status=200, 287 data=json.dumps(params), headers=headers, 288 params=query_params) 289 290 def list_functions(self, marker=None, max_items=None): 291 """ 292 Returns a list of your Lambda functions. For each function, 293 the response includes the function configuration information. 294 You must use GetFunction to retrieve the code for your 295 function. 296 297 This operation requires permission for the 298 `lambda:ListFunctions` action. 299 300 :type marker: string 301 :param marker: Optional string. An opaque pagination token returned 302 from a previous `ListFunctions` operation. If present, indicates 303 where to continue the listing. 304 305 :type max_items: integer 306 :param max_items: Optional integer. Specifies the maximum number of AWS 307 Lambda functions to return in response. This parameter value must 308 be greater than 0. 309 310 """ 311 312 uri = '/2014-11-13/functions/' 313 params = {} 314 headers = {} 315 query_params = {} 316 if marker is not None: 317 query_params['Marker'] = marker 318 if max_items is not None: 319 query_params['MaxItems'] = max_items 320 return self.make_request('GET', uri, expected_status=200, 321 data=json.dumps(params), headers=headers, 322 params=query_params) 323 324 def remove_event_source(self, uuid): 325 """ 326 Removes an event source mapping. This means AWS Lambda will no 327 longer invoke the function for events in the associated 328 source. 329 330 This operation requires permission for the 331 `lambda:RemoveEventSource` action. 332 333 :type uuid: string 334 :param uuid: The event source mapping ID. 335 336 """ 337 338 uri = '/2014-11-13/event-source-mappings/{0}'.format(uuid) 339 return self.make_request('DELETE', uri, expected_status=204) 340 341 def update_function_configuration(self, function_name, role=None, 342 handler=None, description=None, 343 timeout=None, memory_size=None): 344 """ 345 Updates the configuration parameters for the specified Lambda 346 function by using the values provided in the request. You 347 provide only the parameters you want to change. This operation 348 must only be used on an existing Lambda function and cannot be 349 used to update the function's code. 350 351 This operation requires permission for the 352 `lambda:UpdateFunctionConfiguration` action. 353 354 :type function_name: string 355 :param function_name: The name of the Lambda function. 356 357 :type role: string 358 :param role: The Amazon Resource Name (ARN) of the IAM role that Lambda 359 will assume when it executes your function. 360 361 :type handler: string 362 :param handler: The function that Lambda calls to begin executing your 363 function. For Node.js, it is the module-name.export value in your 364 function. 365 366 :type description: string 367 :param description: A short user-defined function description. Lambda 368 does not use this value. Assign a meaningful description as you see 369 fit. 370 371 :type timeout: integer 372 :param timeout: The function execution time at which Lambda should 373 terminate the function. Because the execution time has cost 374 implications, we recommend you set this value based on your 375 expected execution time. The default is 3 seconds. 376 377 :type memory_size: integer 378 :param memory_size: The amount of memory, in MB, your Lambda function 379 is given. Lambda uses this memory size to infer the amount of CPU 380 allocated to your function. Your function use-case determines your 381 CPU and memory requirements. For example, a database operation 382 might need less memory compared to an image processing function. 383 The default value is 128 MB. The value must be a multiple of 64 MB. 384 385 """ 386 387 uri = '/2014-11-13/functions/{0}/configuration'.format(function_name) 388 params = {} 389 headers = {} 390 query_params = {} 391 if role is not None: 392 query_params['Role'] = role 393 if handler is not None: 394 query_params['Handler'] = handler 395 if description is not None: 396 query_params['Description'] = description 397 if timeout is not None: 398 query_params['Timeout'] = timeout 399 if memory_size is not None: 400 query_params['MemorySize'] = memory_size 401 return self.make_request('PUT', uri, expected_status=200, 402 data=json.dumps(params), headers=headers, 403 params=query_params) 404 405 def upload_function(self, function_name, function_zip, runtime, role, 406 handler, mode, description=None, timeout=None, 407 memory_size=None): 408 """ 409 Creates a new Lambda function or updates an existing function. 410 The function metadata is created from the request parameters, 411 and the code for the function is provided by a .zip file in 412 the request body. If the function name already exists, the 413 existing Lambda function is updated with the new code and 414 metadata. 415 416 This operation requires permission for the 417 `lambda:UploadFunction` action. 418 419 :type function_name: string 420 :param function_name: The name you want to assign to the function you 421 are uploading. The function names appear in the console and are 422 returned in the ListFunctions API. Function names are used to 423 specify functions to other AWS Lambda APIs, such as InvokeAsync. 424 425 :type function_zip: blob 426 :param function_zip: A .zip file containing your packaged source code. 427 For more information about creating a .zip file, go to `AWS LambdaL 428 How it Works`_ in the AWS Lambda Developer Guide. 429 430 :type runtime: string 431 :param runtime: The runtime environment for the Lambda function you are 432 uploading. Currently, Lambda supports only "nodejs" as the runtime. 433 434 :type role: string 435 :param role: The Amazon Resource Name (ARN) of the IAM role that Lambda 436 assumes when it executes your function to access any other Amazon 437 Web Services (AWS) resources. 438 439 :type handler: string 440 :param handler: The function that Lambda calls to begin execution. For 441 Node.js, it is the module-name . export value in your function. 442 443 :type mode: string 444 :param mode: How the Lambda function will be invoked. Lambda supports 445 only the "event" mode. 446 447 :type description: string 448 :param description: A short, user-defined function description. Lambda 449 does not use this value. Assign a meaningful description as you see 450 fit. 451 452 :type timeout: integer 453 :param timeout: The function execution time at which Lambda should 454 terminate the function. Because the execution time has cost 455 implications, we recommend you set this value based on your 456 expected execution time. The default is 3 seconds. 457 458 :type memory_size: integer 459 :param memory_size: The amount of memory, in MB, your Lambda function 460 is given. Lambda uses this memory size to infer the amount of CPU 461 allocated to your function. Your function use-case determines your 462 CPU and memory requirements. For example, database operation might 463 need less memory compared to image processing function. The default 464 value is 128 MB. The value must be a multiple of 64 MB. 465 466 """ 467 uri = '/2014-11-13/functions/{0}'.format(function_name) 468 headers = {} 469 query_params = {} 470 if runtime is not None: 471 query_params['Runtime'] = runtime 472 if role is not None: 473 query_params['Role'] = role 474 if handler is not None: 475 query_params['Handler'] = handler 476 if mode is not None: 477 query_params['Mode'] = mode 478 if description is not None: 479 query_params['Description'] = description 480 if timeout is not None: 481 query_params['Timeout'] = timeout 482 if memory_size is not None: 483 query_params['MemorySize'] = memory_size 484 485 try: 486 content_length = str(len(function_zip)) 487 except (TypeError, AttributeError): 488 # If a file like object is provided and seekable, try to retrieve 489 # the file size via fstat. 490 try: 491 function_zip.tell() 492 except (AttributeError, OSError, IOError): 493 raise TypeError( 494 "File-like object passed to parameter " 495 "``function_zip`` must be seekable." 496 ) 497 content_length = str(os.fstat(function_zip.fileno()).st_size) 498 headers['Content-Length'] = content_length 499 return self.make_request('PUT', uri, expected_status=201, 500 data=function_zip, headers=headers, 501 params=query_params) 502 503 def make_request(self, verb, resource, headers=None, data='', 504 expected_status=None, params=None): 505 if headers is None: 506 headers = {} 507 response = AWSAuthConnection.make_request( 508 self, verb, resource, headers=headers, data=data, params=params) 509 body = response.read().decode('utf-8') 510 if body: 511 body = json.loads(body) 512 if response.status == expected_status: 513 return body 514 else: 515 error_type = response.getheader('x-amzn-ErrorType').split(':')[0] 516 error_class = self._faults.get(error_type, self.ResponseError) 517 raise error_class(response.status, response.reason, body) 518