Improvements: AWSLambda (#4943)
This commit is contained in:
parent
bbd4b2afc3
commit
96c9391beb
@ -3423,25 +3423,25 @@
|
||||
|
||||
## lambda
|
||||
<details>
|
||||
<summary>43% implemented</summary>
|
||||
<summary>53% implemented</summary>
|
||||
|
||||
- [ ] add_layer_version_permission
|
||||
- [X] add_permission
|
||||
- [ ] create_alias
|
||||
- [X] create_alias
|
||||
- [ ] create_code_signing_config
|
||||
- [X] create_event_source_mapping
|
||||
- [X] create_function
|
||||
- [ ] delete_alias
|
||||
- [X] delete_alias
|
||||
- [ ] delete_code_signing_config
|
||||
- [X] delete_event_source_mapping
|
||||
- [X] delete_function
|
||||
- [ ] delete_function_code_signing_config
|
||||
- [X] delete_function_concurrency
|
||||
- [ ] delete_function_event_invoke_config
|
||||
- [ ] delete_layer_version
|
||||
- [X] delete_layer_version
|
||||
- [ ] delete_provisioned_concurrency_config
|
||||
- [ ] get_account_settings
|
||||
- [ ] get_alias
|
||||
- [X] get_alias
|
||||
- [X] get_code_signing_config
|
||||
- [X] get_event_source_mapping
|
||||
- [X] get_function
|
||||
@ -3449,7 +3449,7 @@
|
||||
- [X] get_function_concurrency
|
||||
- [ ] get_function_configuration
|
||||
- [ ] get_function_event_invoke_config
|
||||
- [ ] get_layer_version
|
||||
- [X] get_layer_version
|
||||
- [ ] get_layer_version_by_arn
|
||||
- [ ] get_layer_version_policy
|
||||
- [X] get_policy
|
||||
@ -3477,7 +3477,7 @@
|
||||
- [X] remove_permission
|
||||
- [X] tag_resource
|
||||
- [X] untag_resource
|
||||
- [ ] update_alias
|
||||
- [X] update_alias
|
||||
- [ ] update_code_signing_config
|
||||
- [X] update_event_source_mapping
|
||||
- [X] update_function_code
|
||||
|
@ -29,21 +29,21 @@ lambda
|
||||
|
||||
- [ ] add_layer_version_permission
|
||||
- [X] add_permission
|
||||
- [ ] create_alias
|
||||
- [X] create_alias
|
||||
- [ ] create_code_signing_config
|
||||
- [X] create_event_source_mapping
|
||||
- [X] create_function
|
||||
- [ ] delete_alias
|
||||
- [X] delete_alias
|
||||
- [ ] delete_code_signing_config
|
||||
- [X] delete_event_source_mapping
|
||||
- [X] delete_function
|
||||
- [ ] delete_function_code_signing_config
|
||||
- [X] delete_function_concurrency
|
||||
- [ ] delete_function_event_invoke_config
|
||||
- [ ] delete_layer_version
|
||||
- [X] delete_layer_version
|
||||
- [ ] delete_provisioned_concurrency_config
|
||||
- [ ] get_account_settings
|
||||
- [ ] get_alias
|
||||
- [X] get_alias
|
||||
- [X] get_code_signing_config
|
||||
- [X] get_event_source_mapping
|
||||
- [X] get_function
|
||||
@ -51,7 +51,7 @@ lambda
|
||||
- [X] get_function_concurrency
|
||||
- [ ] get_function_configuration
|
||||
- [ ] get_function_event_invoke_config
|
||||
- [ ] get_layer_version
|
||||
- [X] get_layer_version
|
||||
- [ ] get_layer_version_by_arn
|
||||
- [ ] get_layer_version_policy
|
||||
- [X] get_policy
|
||||
@ -79,7 +79,11 @@ lambda
|
||||
- [X] remove_permission
|
||||
- [X] tag_resource
|
||||
- [X] untag_resource
|
||||
- [ ] update_alias
|
||||
- [X] update_alias
|
||||
|
||||
The RevisionId parameter is not yet implemented
|
||||
|
||||
|
||||
- [ ] update_code_signing_config
|
||||
- [X] update_event_source_mapping
|
||||
- [X] update_function_code
|
||||
|
@ -35,6 +35,27 @@ class PreconditionFailedException(JsonRESTError):
|
||||
super().__init__("PreconditionFailedException", message)
|
||||
|
||||
|
||||
class UnknownAliasException(LambdaClientError):
|
||||
code = 404
|
||||
|
||||
def __init__(self, arn):
|
||||
super().__init__("ResourceNotFoundException", f"Cannot find alias arn: {arn}")
|
||||
|
||||
|
||||
class UnknownFunctionException(LambdaClientError):
|
||||
code = 404
|
||||
|
||||
def __init__(self, arn):
|
||||
super().__init__("ResourceNotFoundException", f"Function not found: {arn}")
|
||||
|
||||
|
||||
class UnknownLayerException(LambdaClientError):
|
||||
code = 404
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("ResourceNotFoundException", "Cannot find layer")
|
||||
|
||||
|
||||
class UnknownPolicyException(LambdaClientError):
|
||||
code = 404
|
||||
|
||||
@ -43,10 +64,3 @@ class UnknownPolicyException(LambdaClientError):
|
||||
"ResourceNotFoundException",
|
||||
"No policy is associated with the given resource.",
|
||||
)
|
||||
|
||||
|
||||
class UnknownFunctionException(LambdaClientError):
|
||||
code = 404
|
||||
|
||||
def __init__(self, arn):
|
||||
super().__init__("ResourceNotFoundException", f"Function not found: {arn}")
|
||||
|
@ -23,7 +23,7 @@ import weakref
|
||||
import requests.exceptions
|
||||
|
||||
from moto.awslambda.policy import Policy
|
||||
from moto.core import BaseBackend, CloudFormationModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.iam.models import iam_backend
|
||||
from moto.iam.exceptions import IAMNotFoundException
|
||||
@ -36,7 +36,9 @@ from .exceptions import (
|
||||
CrossAccountNotAllowed,
|
||||
InvalidRoleFormat,
|
||||
InvalidParameterValueException,
|
||||
UnknownLayerException,
|
||||
UnknownFunctionException,
|
||||
UnknownAliasException,
|
||||
)
|
||||
from .utils import (
|
||||
make_function_arn,
|
||||
@ -50,13 +52,11 @@ from moto.dynamodb import dynamodb_backends
|
||||
from moto.dynamodbstreams import dynamodbstreams_backends
|
||||
from moto.core import ACCOUNT_ID
|
||||
from moto.utilities.docker_utilities import DockerModel, parse_image_ref
|
||||
from tempfile import TemporaryDirectory
|
||||
from uuid import uuid4
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from tempfile import TemporaryDirectory
|
||||
except ImportError:
|
||||
from backports.tempfile import TemporaryDirectory
|
||||
|
||||
docker_3 = docker.__version__[0] >= "3"
|
||||
|
||||
@ -110,25 +110,26 @@ class _DockerDataVolumeContext:
|
||||
def __enter__(self):
|
||||
# See if volume is already known
|
||||
with self.__class__._lock:
|
||||
self._vol_ref = self.__class__._data_vol_map[self._lambda_func.code_sha_256]
|
||||
self._vol_ref = self.__class__._data_vol_map[self._lambda_func.code_digest]
|
||||
self._vol_ref.refcount += 1
|
||||
if self._vol_ref.refcount > 1:
|
||||
return self
|
||||
|
||||
# See if the volume already exists
|
||||
for vol in self._lambda_func.docker_client.volumes.list():
|
||||
if vol.name == self._lambda_func.code_sha_256:
|
||||
if vol.name == self._lambda_func.code_digest:
|
||||
self._vol_ref.volume = vol
|
||||
return self
|
||||
|
||||
# It doesn't exist so we need to create it
|
||||
self._vol_ref.volume = self._lambda_func.docker_client.volumes.create(
|
||||
self._lambda_func.code_sha_256
|
||||
self._lambda_func.code_digest
|
||||
)
|
||||
if docker_3:
|
||||
volumes = {self.name: {"bind": "/tmp/data", "mode": "rw"}}
|
||||
else:
|
||||
volumes = {self.name: "/tmp/data"}
|
||||
|
||||
self._lambda_func.docker_client.images.pull(
|
||||
":".join(parse_image_ref("alpine"))
|
||||
)
|
||||
@ -164,7 +165,17 @@ def _zipfile_content(zipfile):
|
||||
except Exception:
|
||||
to_unzip_code = base64.b64decode(zipfile)
|
||||
|
||||
return to_unzip_code, len(to_unzip_code), hashlib.sha256(to_unzip_code).hexdigest()
|
||||
sha_code = hashlib.sha256(to_unzip_code)
|
||||
base64ed_sha = base64.b64encode(sha_code.digest()).decode("utf-8")
|
||||
sha_hex_digest = sha_code.hexdigest()
|
||||
return to_unzip_code, len(to_unzip_code), base64ed_sha, sha_hex_digest
|
||||
|
||||
|
||||
def _s3_content(key):
|
||||
sha_code = hashlib.sha256(key.value)
|
||||
base64ed_sha = base64.b64encode(sha_code.digest()).decode("utf-8")
|
||||
sha_hex_digest = sha_code.hexdigest()
|
||||
return key.value, key.size, base64ed_sha, sha_hex_digest
|
||||
|
||||
|
||||
def _validate_s3_bucket_and_key(data):
|
||||
@ -228,15 +239,21 @@ class LayerVersion(CloudFormationModel):
|
||||
self._layer = None
|
||||
|
||||
if "ZipFile" in self.content:
|
||||
self.code_bytes, self.code_size, self.code_sha_256 = _zipfile_content(
|
||||
self.content["ZipFile"]
|
||||
)
|
||||
(
|
||||
self.code_bytes,
|
||||
self.code_size,
|
||||
self.code_sha_256,
|
||||
self.code_digest,
|
||||
) = _zipfile_content(self.content["ZipFile"])
|
||||
else:
|
||||
key = _validate_s3_bucket_and_key(self.content)
|
||||
if key:
|
||||
self.code_bytes = key.value
|
||||
self.code_size = key.size
|
||||
self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
|
||||
(
|
||||
self.code_bytes,
|
||||
self.code_size,
|
||||
self.code_sha_256,
|
||||
self.code_digest,
|
||||
) = _s3_content(key)
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
@ -251,7 +268,13 @@ class LayerVersion(CloudFormationModel):
|
||||
|
||||
def get_layer_version(self):
|
||||
return {
|
||||
"Content": {
|
||||
"Location": "s3://",
|
||||
"CodeSha256": self.code_sha_256,
|
||||
"CodeSize": self.code_size,
|
||||
},
|
||||
"Version": self.version,
|
||||
"LayerArn": self._layer.layer_arn,
|
||||
"LayerVersionArn": self.arn,
|
||||
"CreatedDate": self.created_date,
|
||||
"CompatibleRuntimes": self.compatible_runtimes,
|
||||
@ -288,6 +311,38 @@ class LayerVersion(CloudFormationModel):
|
||||
return layer_version
|
||||
|
||||
|
||||
class LambdaAlias(BaseModel):
|
||||
def __init__(
|
||||
self, region, name, function_name, function_version, description, routing_config
|
||||
):
|
||||
self.arn = (
|
||||
f"arn:aws:lambda:{region}:{ACCOUNT_ID}:function:{function_name}:{name}"
|
||||
)
|
||||
self.name = name
|
||||
self.function_version = function_version
|
||||
self.description = description
|
||||
self.routing_config = routing_config
|
||||
self.revision_id = str(uuid4())
|
||||
|
||||
def update(self, description, function_version, routing_config):
|
||||
if description is not None:
|
||||
self.description = description
|
||||
if function_version is not None:
|
||||
self.function_version = function_version
|
||||
if routing_config is not None:
|
||||
self.routing_config = routing_config
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"AliasArn": self.arn,
|
||||
"Description": self.description,
|
||||
"FunctionVersion": self.function_version,
|
||||
"Name": self.name,
|
||||
"RevisionId": self.revision_id,
|
||||
"RoutingConfig": self.routing_config or None,
|
||||
}
|
||||
|
||||
|
||||
class Layer(object):
|
||||
def __init__(self, name, region):
|
||||
self.region = region
|
||||
@ -302,6 +357,9 @@ class Layer(object):
|
||||
layer_version.attach(self, self._latest_version)
|
||||
self.layer_versions[str(self._latest_version)] = layer_version
|
||||
|
||||
def delete_version(self, layer_version):
|
||||
self.layer_versions.pop(str(layer_version), None)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"LayerName": self.name,
|
||||
@ -338,6 +396,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
self.signing_profile_version_arn = spec.get("SigningProfileVersionArn")
|
||||
self.signing_job_arn = spec.get("SigningJobArn")
|
||||
self.code_signing_config_arn = spec.get("CodeSigningConfigArn")
|
||||
self.tracing_config = spec.get("TracingConfig") or {"Mode": "PassThrough"}
|
||||
|
||||
self.logs_group_name = "/aws/lambda/{}".format(self.function_name)
|
||||
|
||||
@ -351,9 +410,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
self.last_modified = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
if "ZipFile" in self.code:
|
||||
self.code_bytes, self.code_size, self.code_sha_256 = _zipfile_content(
|
||||
self.code["ZipFile"]
|
||||
)
|
||||
(
|
||||
self.code_bytes,
|
||||
self.code_size,
|
||||
self.code_sha_256,
|
||||
self.code_digest,
|
||||
) = _zipfile_content(self.code["ZipFile"])
|
||||
|
||||
# TODO: we should be putting this in a lambda bucket
|
||||
self.code["UUID"] = str(uuid.uuid4())
|
||||
@ -361,9 +423,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
else:
|
||||
key = _validate_s3_bucket_and_key(self.code)
|
||||
if key:
|
||||
self.code_bytes = key.value
|
||||
self.code_size = key.size
|
||||
self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
|
||||
(
|
||||
self.code_bytes,
|
||||
self.code_size,
|
||||
self.code_sha_256,
|
||||
self.code_digest,
|
||||
) = _s3_content(key)
|
||||
else:
|
||||
self.code_bytes = ""
|
||||
self.code_size = 0
|
||||
@ -378,6 +443,8 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
else:
|
||||
self.tags = dict()
|
||||
|
||||
self._aliases = dict()
|
||||
|
||||
def set_version(self, version):
|
||||
self.function_arn = make_function_ver_arn(
|
||||
self.region, ACCOUNT_ID, self.function_name, version
|
||||
@ -420,7 +487,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
"FunctionName": self.function_name,
|
||||
}
|
||||
|
||||
def get_configuration(self):
|
||||
def get_configuration(self, on_create=False):
|
||||
config = {
|
||||
"CodeSha256": self.code_sha_256,
|
||||
"CodeSize": self.code_size,
|
||||
@ -440,7 +507,11 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
"Layers": self.layers,
|
||||
"SigningProfileVersionArn": self.signing_profile_version_arn,
|
||||
"SigningJobArn": self.signing_job_arn,
|
||||
"TracingConfig": self.tracing_config,
|
||||
}
|
||||
if not on_create:
|
||||
# Only return this variable after the first creation
|
||||
config["LastUpdateStatus"] = "Successful"
|
||||
if self.environment_vars:
|
||||
config["Environment"] = {"Variables": self.environment_vars}
|
||||
|
||||
@ -456,6 +527,8 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
},
|
||||
"Configuration": self.get_configuration(),
|
||||
}
|
||||
if self.tags:
|
||||
code["Tags"] = self.tags
|
||||
if self.reserved_concurrency:
|
||||
code.update(
|
||||
{
|
||||
@ -496,19 +569,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
if "ZipFile" in updated_spec:
|
||||
self.code["ZipFile"] = updated_spec["ZipFile"]
|
||||
|
||||
# using the "hackery" from __init__ because it seems to work
|
||||
# TODOs and FIXMEs included, because they'll need to be fixed
|
||||
# in both places now
|
||||
try:
|
||||
to_unzip_code = base64.b64decode(
|
||||
bytes(updated_spec["ZipFile"], "utf-8")
|
||||
)
|
||||
except Exception:
|
||||
to_unzip_code = base64.b64decode(updated_spec["ZipFile"])
|
||||
|
||||
self.code_bytes = to_unzip_code
|
||||
self.code_size = len(to_unzip_code)
|
||||
self.code_sha_256 = hashlib.sha256(to_unzip_code).hexdigest()
|
||||
(
|
||||
self.code_bytes,
|
||||
self.code_size,
|
||||
self.code_sha_256,
|
||||
self.code_digest,
|
||||
) = _zipfile_content(updated_spec["ZipFile"])
|
||||
|
||||
# TODO: we should be putting this in a lambda bucket
|
||||
self.code["UUID"] = str(uuid.uuid4())
|
||||
@ -533,9 +599,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
"Error occurred while GetObject. S3 Error Code: NoSuchKey. S3 Error Message: The specified key does not exist.",
|
||||
)
|
||||
if key:
|
||||
self.code_bytes = key.value
|
||||
self.code_size = key.size
|
||||
self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
|
||||
(
|
||||
self.code_bytes,
|
||||
self.code_size,
|
||||
self.code_sha_256,
|
||||
self.code_digest,
|
||||
) = _s3_content(key)
|
||||
self.code["S3Bucket"] = updated_spec["S3Bucket"]
|
||||
self.code["S3Key"] = updated_spec["S3Key"]
|
||||
|
||||
@ -781,6 +850,32 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
def delete(self, region):
|
||||
lambda_backends[region].delete_function(self.function_name)
|
||||
|
||||
def delete_alias(self, name):
|
||||
self._aliases.pop(name, None)
|
||||
|
||||
def get_alias(self, name):
|
||||
if name in self._aliases:
|
||||
return self._aliases[name]
|
||||
arn = f"arn:aws:lambda:{self.region}:{ACCOUNT_ID}:function:{self.function_name}:{name}"
|
||||
raise UnknownAliasException(arn)
|
||||
|
||||
def put_alias(self, name, description, function_version, routing_config):
|
||||
alias = LambdaAlias(
|
||||
region=self.region,
|
||||
name=name,
|
||||
function_name=self.function_name,
|
||||
function_version=function_version,
|
||||
description=description,
|
||||
routing_config=routing_config,
|
||||
)
|
||||
self._aliases[name] = alias
|
||||
return alias
|
||||
|
||||
def update_alias(self, name, description, function_version, routing_config):
|
||||
alias = self.get_alias(name)
|
||||
alias.update(description, function_version, routing_config)
|
||||
return alias
|
||||
|
||||
|
||||
class EventSourceMapping(CloudFormationModel):
|
||||
def __init__(self, spec):
|
||||
@ -934,8 +1029,9 @@ class LambdaVersion(CloudFormationModel):
|
||||
|
||||
class LambdaStorage(object):
|
||||
def __init__(self, region_name):
|
||||
# Format 'func_name' {'alias': {}, 'versions': []}
|
||||
# Format 'func_name' {'versions': []}
|
||||
self._functions = {}
|
||||
self._aliases = dict()
|
||||
self._arns = weakref.WeakValueDictionary()
|
||||
self.region_name = region_name
|
||||
|
||||
@ -950,8 +1046,25 @@ class LambdaStorage(object):
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def _get_alias(self, name, alias):
|
||||
return self._functions[name]["alias"].get(alias, None)
|
||||
def delete_alias(self, name, function_name):
|
||||
fn = self.get_function_by_name_or_arn(function_name)
|
||||
return fn.delete_alias(name)
|
||||
|
||||
def get_alias(self, name, function_name):
|
||||
fn = self.get_function_by_name_or_arn(function_name)
|
||||
return fn.get_alias(name)
|
||||
|
||||
def put_alias(
|
||||
self, name, function_name, function_version, description, routing_config
|
||||
):
|
||||
fn = self.get_function_by_name_or_arn(function_name)
|
||||
return fn.put_alias(name, description, function_version, routing_config)
|
||||
|
||||
def update_alias(
|
||||
self, name, function_name, function_version, description, routing_config
|
||||
):
|
||||
fn = self.get_function_by_name_or_arn(function_name)
|
||||
return fn.update_alias(name, description, function_version, routing_config)
|
||||
|
||||
def get_function_by_name(self, name, qualifier=None):
|
||||
if name not in self._functions:
|
||||
@ -974,6 +1087,11 @@ class LambdaStorage(object):
|
||||
return [latest] + self._functions[name]["versions"]
|
||||
|
||||
def get_arn(self, arn):
|
||||
# Function ARN may contain an alias
|
||||
# arn:aws:lambda:region:account_id:function:<fn_name>:<alias_name>
|
||||
if ":" in arn.split(":function:")[-1]:
|
||||
# arn = arn:aws:lambda:region:account_id:function:<fn_name>
|
||||
arn = ":".join(arn.split(":")[0:-1])
|
||||
return self._arns.get(arn, None)
|
||||
|
||||
def get_function_by_name_or_arn(self, name_or_arn, qualifier=None):
|
||||
@ -1002,11 +1120,7 @@ class LambdaStorage(object):
|
||||
if fn.function_name in self._functions:
|
||||
self._functions[fn.function_name]["latest"] = fn
|
||||
else:
|
||||
self._functions[fn.function_name] = {
|
||||
"latest": fn,
|
||||
"versions": [],
|
||||
"alias": weakref.WeakValueDictionary(),
|
||||
}
|
||||
self._functions[fn.function_name] = {"latest": fn, "versions": []}
|
||||
# instantiate a new policy for this version of the lambda
|
||||
fn.policy = Policy(fn)
|
||||
self._arns[fn.function_arn] = fn
|
||||
@ -1122,6 +1236,17 @@ class LayerStorage(object):
|
||||
def list_layers(self):
|
||||
return [layer.to_dict() for layer in self._layers.values()]
|
||||
|
||||
def delete_layer_version(self, layer_name, layer_version):
|
||||
self._layers[layer_name].delete_version(layer_version)
|
||||
|
||||
def get_layer_version(self, layer_name, layer_version):
|
||||
if layer_name not in self._layers:
|
||||
raise UnknownLayerException()
|
||||
for lv in self._layers[layer_name].layer_versions.values():
|
||||
if lv.version == int(layer_version):
|
||||
return lv
|
||||
raise UnknownLayerException()
|
||||
|
||||
def get_layer_versions(self, layer_name):
|
||||
if layer_name in self._layers:
|
||||
return list(iter(self._layers[layer_name].layer_versions.values()))
|
||||
@ -1188,7 +1313,7 @@ class LambdaBackend(BaseBackend):
|
||||
"""
|
||||
|
||||
def __init__(self, region_name):
|
||||
self._lambdas = LambdaStorage(region_name)
|
||||
self._lambdas = LambdaStorage(region_name=region_name)
|
||||
self._event_source_mappings = {}
|
||||
self._layers = LayerStorage()
|
||||
self.region_name = region_name
|
||||
@ -1205,6 +1330,29 @@ class LambdaBackend(BaseBackend):
|
||||
service_region, zones, "lambda"
|
||||
)
|
||||
|
||||
def create_alias(
|
||||
self, name, function_name, function_version, description, routing_config
|
||||
):
|
||||
return self._lambdas.put_alias(
|
||||
name, function_name, function_version, description, routing_config
|
||||
)
|
||||
|
||||
def delete_alias(self, name, function_name):
|
||||
return self._lambdas.delete_alias(name, function_name)
|
||||
|
||||
def get_alias(self, name, function_name):
|
||||
return self._lambdas.get_alias(name, function_name)
|
||||
|
||||
def update_alias(
|
||||
self, name, function_name, function_version, description, routing_config
|
||||
):
|
||||
"""
|
||||
The RevisionId parameter is not yet implemented
|
||||
"""
|
||||
return self._lambdas.update_alias(
|
||||
name, function_name, function_version, description, routing_config
|
||||
)
|
||||
|
||||
def create_function(self, spec):
|
||||
function_name = spec.get("FunctionName", None)
|
||||
if function_name is None:
|
||||
@ -1274,9 +1422,7 @@ class LambdaBackend(BaseBackend):
|
||||
required = ["LayerName", "Content"]
|
||||
for param in required:
|
||||
if not spec.get(param):
|
||||
raise RESTError(
|
||||
"InvalidParameterValueException", "Missing {}".format(param)
|
||||
)
|
||||
raise InvalidParameterValueException("Missing {}".format(param))
|
||||
layer_version = LayerVersion(spec, self.region_name)
|
||||
self._layers.put_layer_version(layer_version)
|
||||
return layer_version
|
||||
@ -1284,6 +1430,12 @@ class LambdaBackend(BaseBackend):
|
||||
def list_layers(self):
|
||||
return self._layers.list_layers()
|
||||
|
||||
def delete_layer_version(self, layer_name, layer_version):
|
||||
return self._layers.delete_layer_version(layer_name, layer_version)
|
||||
|
||||
def get_layer_version(self, layer_name, layer_version):
|
||||
return self._layers.get_layer_version(layer_name, layer_version)
|
||||
|
||||
def get_layer_versions(self, layer_name):
|
||||
return self._layers.get_layer_versions(layer_name)
|
||||
|
||||
@ -1485,10 +1637,8 @@ class LambdaBackend(BaseBackend):
|
||||
|
||||
def get_policy(self, function_name):
|
||||
fn = self.get_function(function_name)
|
||||
return fn.policy.get_policy()
|
||||
|
||||
def get_policy_wire_format(self, function_name):
|
||||
fn = self.get_function(function_name)
|
||||
if not fn:
|
||||
raise UnknownFunctionException(function_name)
|
||||
return fn.policy.wire_format()
|
||||
|
||||
def update_function_code(self, function_name, qualifier, body):
|
||||
|
@ -47,6 +47,20 @@ class LambdaResponse(BaseResponse):
|
||||
else:
|
||||
raise ValueError("Cannot handle request")
|
||||
|
||||
def aliases(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return self._create_alias()
|
||||
|
||||
def alias(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "DELETE":
|
||||
return self._delete_alias()
|
||||
elif request.method == "GET":
|
||||
return self._get_alias()
|
||||
elif request.method == "PUT":
|
||||
return self._update_alias()
|
||||
|
||||
def event_source_mapping(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
path = request.path if hasattr(request, "path") else path_url(request.url)
|
||||
@ -65,6 +79,13 @@ class LambdaResponse(BaseResponse):
|
||||
if request.method == "GET":
|
||||
return self._list_layers()
|
||||
|
||||
def layers_version(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "DELETE":
|
||||
return self._delete_layer_version()
|
||||
elif request.method == "GET":
|
||||
return self._get_layer_version()
|
||||
|
||||
def layers_versions(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
if request.method == "GET":
|
||||
@ -182,11 +203,8 @@ class LambdaResponse(BaseResponse):
|
||||
def _get_policy(self, request):
|
||||
path = request.path if hasattr(request, "path") else path_url(request.url)
|
||||
function_name = unquote(path.split("/")[-2])
|
||||
if self.lambda_backend.get_function(function_name):
|
||||
out = self.lambda_backend.get_policy_wire_format(function_name)
|
||||
return 200, {}, out
|
||||
else:
|
||||
return 404, {}, "{}"
|
||||
out = self.lambda_backend.get_policy(function_name)
|
||||
return 200, {}, out
|
||||
|
||||
def _del_policy(self, request, querystring):
|
||||
path = request.path if hasattr(request, "path") else path_url(request.url)
|
||||
@ -270,7 +288,7 @@ class LambdaResponse(BaseResponse):
|
||||
|
||||
def _create_function(self):
|
||||
fn = self.lambda_backend.create_function(self.json_body)
|
||||
config = fn.get_configuration()
|
||||
config = fn.get_configuration(on_create=True)
|
||||
return 201, {}, json.dumps(config)
|
||||
|
||||
def _create_event_source_mapping(self):
|
||||
@ -466,6 +484,20 @@ class LambdaResponse(BaseResponse):
|
||||
layers = self.lambda_backend.list_layers()
|
||||
return 200, {}, json.dumps({"Layers": layers})
|
||||
|
||||
def _delete_layer_version(self):
|
||||
layer_name = self.path.split("/")[-3]
|
||||
layer_version = self.path.split("/")[-1]
|
||||
|
||||
self.lambda_backend.delete_layer_version(layer_name, layer_version)
|
||||
return 200, {}, "{}"
|
||||
|
||||
def _get_layer_version(self):
|
||||
layer_name = self.path.split("/")[-3]
|
||||
layer_version = self.path.split("/")[-1]
|
||||
|
||||
layer = self.lambda_backend.get_layer_version(layer_name, layer_version)
|
||||
return 200, {}, json.dumps(layer.get_layer_version())
|
||||
|
||||
def _get_layer_versions(self):
|
||||
layer_name = self.path.rsplit("/", 2)[-2]
|
||||
layer_versions = self.lambda_backend.get_layer_versions(layer_name)
|
||||
@ -484,3 +516,49 @@ class LambdaResponse(BaseResponse):
|
||||
layer_version = self.lambda_backend.publish_layer_version(spec)
|
||||
config = layer_version.get_layer_version()
|
||||
return 201, {}, json.dumps(config)
|
||||
|
||||
def _create_alias(self):
|
||||
function_name = unquote(self.path.rsplit("/", 2)[-2])
|
||||
params = json.loads(self.body)
|
||||
alias_name = params.get("Name")
|
||||
description = params.get("Description", "")
|
||||
function_version = params.get("FunctionVersion")
|
||||
routing_config = params.get("RoutingConfig")
|
||||
alias = self.lambda_backend.create_alias(
|
||||
name=alias_name,
|
||||
function_name=function_name,
|
||||
function_version=function_version,
|
||||
description=description,
|
||||
routing_config=routing_config,
|
||||
)
|
||||
return 201, {}, json.dumps(alias.to_json())
|
||||
|
||||
def _delete_alias(self):
|
||||
function_name = unquote(self.path.rsplit("/")[-3])
|
||||
alias_name = unquote(self.path.rsplit("/", 2)[-1])
|
||||
self.lambda_backend.delete_alias(name=alias_name, function_name=function_name)
|
||||
return 201, {}, "{}"
|
||||
|
||||
def _get_alias(self):
|
||||
function_name = unquote(self.path.rsplit("/")[-3])
|
||||
alias_name = unquote(self.path.rsplit("/", 2)[-1])
|
||||
alias = self.lambda_backend.get_alias(
|
||||
name=alias_name, function_name=function_name
|
||||
)
|
||||
return 201, {}, json.dumps(alias.to_json())
|
||||
|
||||
def _update_alias(self):
|
||||
function_name = unquote(self.path.rsplit("/")[-3])
|
||||
alias_name = unquote(self.path.rsplit("/", 2)[-1])
|
||||
params = json.loads(self.body)
|
||||
description = params.get("Description")
|
||||
function_version = params.get("FunctionVersion")
|
||||
routing_config = params.get("RoutingConfig")
|
||||
alias = self.lambda_backend.update_alias(
|
||||
name=alias_name,
|
||||
function_name=function_name,
|
||||
function_version=function_version,
|
||||
description=description,
|
||||
routing_config=routing_config,
|
||||
)
|
||||
return 201, {}, json.dumps(alias.to_json())
|
||||
|
@ -7,6 +7,8 @@ response = LambdaResponse()
|
||||
url_paths = {
|
||||
r"{0}/(?P<api_version>[^/]+)/functions/?$": response.root,
|
||||
r"{0}/(?P<api_version>[^/]+)/functions/(?P<function_name>[\w_:%-]+)/?$": response.function,
|
||||
r"{0}/(?P<api_version>[^/]+)/functions/(?P<function_name>[\w_:%-]+)/aliases$": response.aliases,
|
||||
r"{0}/(?P<api_version>[^/]+)/functions/(?P<function_name>[\w_:%-]+)/aliases/(?P<alias_name>[\w_-]+)$": response.alias,
|
||||
r"{0}/(?P<api_version>[^/]+)/functions/(?P<function_name>[\w_:%-]+)/versions/?$": response.versions,
|
||||
r"{0}/(?P<api_version>[^/]+)/event-source-mappings/?$": response.event_source_mappings,
|
||||
r"{0}/(?P<api_version>[^/]+)/event-source-mappings/(?P<UUID>[\w_-]+)/?$": response.event_source_mapping,
|
||||
@ -22,4 +24,5 @@ url_paths = {
|
||||
r"{0}/(?P<api_version>[^/]+)/functions/(?P<function_name>[\w_:%-]+)/concurrency/?$": response.function_concurrency,
|
||||
r"{0}/(?P<api_version>[^/]+)/layers/?$": response.list_layers,
|
||||
r"{0}/(?P<api_version>[^/]+)/layers/(?P<layer_name>[\w_-]+)/versions/?$": response.layers_versions,
|
||||
r"{0}/(?P<api_version>[^/]+)/layers/(?P<layer_name>[\w_-]+)/versions/(?P<layer_version>[\w_-]+)$": response.layers_version,
|
||||
}
|
||||
|
@ -98,6 +98,8 @@ TestAccAWSIPRanges
|
||||
TestAccAWSKinesisStream
|
||||
TestAccAWSKmsAlias
|
||||
TestAccAWSKmsSecretDataSource
|
||||
TestAccAWSLambdaAlias
|
||||
TestAccAWSLambdaLayerVersion
|
||||
TestAccAWSMq
|
||||
TestAccAWSNatGateway
|
||||
TestAccAWSPartition
|
||||
@ -133,5 +135,7 @@ TestAccDataSourceAWSEFSAccessPoint
|
||||
TestAccDataSourceAWSEFSAccessPoints
|
||||
TestAccDataSourceAwsEfsFileSystem
|
||||
TestAccDataSourceAwsEfsMountTarget
|
||||
TestAccDataSourceAWSLambdaLayerVersion
|
||||
TestAccDataSourceAwsLambdaInvocation
|
||||
TestAccDataSourceAwsNetworkInterface_
|
||||
TestValidateSSMDocumentPermissions
|
||||
|
@ -1,7 +1,7 @@
|
||||
import base64
|
||||
import botocore.client
|
||||
import boto3
|
||||
import hashlib
|
||||
import json
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
import pytest
|
||||
|
||||
@ -122,37 +122,19 @@ def test_create_function_from_aws_bucket():
|
||||
Publish=True,
|
||||
VpcConfig={"SecurityGroupIds": ["sg-123abc"], "SubnetIds": ["subnet-123abc"]},
|
||||
)
|
||||
# this is hard to match against, so remove it
|
||||
result["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||
# Botocore inserts retry attempts not seen in Python27
|
||||
result["ResponseMetadata"].pop("RetryAttempts", None)
|
||||
result.pop("LastModified")
|
||||
result.should.equal(
|
||||
{
|
||||
"FunctionName": function_name,
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:{}".format(
|
||||
_lambda_region, ACCOUNT_ID, function_name
|
||||
),
|
||||
"Runtime": "python2.7",
|
||||
"Role": result["Role"],
|
||||
"Handler": "lambda_function.lambda_handler",
|
||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||
"CodeSize": len(zip_content),
|
||||
"Description": "test lambda function",
|
||||
"Timeout": 3,
|
||||
"MemorySize": 128,
|
||||
"PackageType": "ZIP",
|
||||
"Version": "1",
|
||||
"VpcConfig": {
|
||||
"SecurityGroupIds": ["sg-123abc"],
|
||||
"SubnetIds": ["subnet-123abc"],
|
||||
"VpcId": "vpc-123abc",
|
||||
},
|
||||
"ResponseMetadata": {"HTTPStatusCode": 201},
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
}
|
||||
|
||||
result.should.have.key("FunctionName").equals(function_name)
|
||||
result.should.have.key("FunctionArn").equals(
|
||||
"arn:aws:lambda:{}:{}:function:{}".format(
|
||||
_lambda_region, ACCOUNT_ID, function_name
|
||||
)
|
||||
)
|
||||
result.should.have.key("Runtime").equals("python2.7")
|
||||
result.should.have.key("Handler").equals("lambda_function.lambda_handler")
|
||||
result.should.have.key("CodeSha256").equals(
|
||||
base64.b64encode(hashlib.sha256(zip_content).digest()).decode("utf-8")
|
||||
)
|
||||
result.should.have.key("State").equals("Active")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@ -191,16 +173,46 @@ def test_create_function_from_zipfile():
|
||||
"Description": "test lambda function",
|
||||
"Timeout": 3,
|
||||
"MemorySize": 128,
|
||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||
"CodeSha256": base64.b64encode(hashlib.sha256(zip_content).digest()).decode(
|
||||
"utf-8"
|
||||
),
|
||||
"Version": "1",
|
||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||
"ResponseMetadata": {"HTTPStatusCode": 201},
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
"TracingConfig": {"Mode": "PassThrough"},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@pytest.mark.parametrize(
|
||||
"tracing_mode",
|
||||
[(None, "PassThrough"), ("PassThrough", "PassThrough"), ("Active", "Active")],
|
||||
)
|
||||
def test_create_function__with_tracingmode(tracing_mode):
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
source, output = tracing_mode
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
kwargs = dict(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
if source:
|
||||
kwargs["TracingConfig"] = {"Mode": source}
|
||||
result = conn.create_function(**kwargs)
|
||||
result.should.have.key("TracingConfig").should.equal({"Mode": output})
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
@freeze_time("2015-01-01 00:00:00")
|
||||
@ -243,7 +255,7 @@ def test_get_function():
|
||||
result["Code"]["RepositoryType"].should.equal("S3")
|
||||
|
||||
result["Configuration"]["CodeSha256"].should.equal(
|
||||
hashlib.sha256(zip_content).hexdigest()
|
||||
base64.b64encode(hashlib.sha256(zip_content).digest()).decode("utf-8")
|
||||
)
|
||||
result["Configuration"]["CodeSize"].should.equal(len(zip_content))
|
||||
result["Configuration"]["Description"].should.equal("test lambda function")
|
||||
@ -309,7 +321,9 @@ def test_get_function_configuration(key):
|
||||
|
||||
result = conn.get_function_configuration(FunctionName=name_or_arn)
|
||||
|
||||
result["CodeSha256"].should.equal(hashlib.sha256(zip_content).hexdigest())
|
||||
result["CodeSha256"].should.equal(
|
||||
base64.b64encode(hashlib.sha256(zip_content).digest()).decode("utf-8")
|
||||
)
|
||||
result["CodeSize"].should.equal(len(zip_content))
|
||||
result["Description"].should.equal("test lambda function")
|
||||
result.should.contain("FunctionArn")
|
||||
@ -600,7 +614,9 @@ def test_list_create_list_get_delete_list():
|
||||
"RepositoryType": "S3",
|
||||
},
|
||||
"Configuration": {
|
||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||
"CodeSha256": base64.b64encode(hashlib.sha256(zip_content).digest()).decode(
|
||||
"utf-8"
|
||||
),
|
||||
"CodeSize": len(zip_content),
|
||||
"Description": "test lambda function",
|
||||
"FunctionName": function_name,
|
||||
@ -613,6 +629,8 @@ def test_list_create_list_get_delete_list():
|
||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
"LastUpdateStatus": "Successful",
|
||||
"TracingConfig": {"Mode": "PassThrough"},
|
||||
},
|
||||
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||
}
|
||||
@ -673,90 +691,6 @@ def test_list_create_list_get_delete_list():
|
||||
func_names.shouldnt.contain(function_name)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_tags():
|
||||
"""
|
||||
test list_tags -> tag_resource -> list_tags -> tag_resource -> list_tags -> untag_resource -> list_tags integration
|
||||
"""
|
||||
bucket_name = str(uuid4())
|
||||
s3_conn = boto3.client("s3", _lambda_region)
|
||||
s3_conn.create_bucket(
|
||||
Bucket=bucket_name,
|
||||
CreateBucketConfiguration={"LocationConstraint": _lambda_region},
|
||||
)
|
||||
|
||||
zip_content = get_test_zip_file2()
|
||||
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
function = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
|
||||
# List tags when there are none
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(dict())
|
||||
|
||||
# List tags when there is one
|
||||
conn.tag_resource(Resource=function["FunctionArn"], Tags=dict(spam="eggs"))[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(200)
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(
|
||||
dict(spam="eggs")
|
||||
)
|
||||
|
||||
# List tags when another has been added
|
||||
conn.tag_resource(Resource=function["FunctionArn"], Tags=dict(foo="bar"))[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(200)
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(
|
||||
dict(spam="eggs", foo="bar")
|
||||
)
|
||||
|
||||
# Untag resource
|
||||
conn.untag_resource(Resource=function["FunctionArn"], TagKeys=["spam", "trolls"])[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(204)
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(
|
||||
dict(foo="bar")
|
||||
)
|
||||
|
||||
# Untag a tag that does not exist (no error and no change)
|
||||
conn.untag_resource(Resource=function["FunctionArn"], TagKeys=["spam"])[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(204)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_tags_not_found():
|
||||
"""
|
||||
Test list_tags and tag_resource when the lambda with the given arn does not exist
|
||||
"""
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
conn.list_tags.when.called_with(
|
||||
Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID)
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
conn.tag_resource.when.called_with(
|
||||
Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
|
||||
Tags=dict(spam="eggs"),
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
conn.untag_resource.when.called_with(
|
||||
Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
|
||||
TagKeys=["spam"],
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@freeze_time("2015-01-01 00:00:00")
|
||||
def test_get_function_created_with_zipfile():
|
||||
@ -776,7 +710,6 @@ def test_get_function_created_with_zipfile():
|
||||
)
|
||||
|
||||
response = conn.get_function(FunctionName=function_name)
|
||||
response["Configuration"].pop("LastModified")
|
||||
|
||||
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||
assert len(response["Code"]) == 2
|
||||
@ -784,100 +717,26 @@ def test_get_function_created_with_zipfile():
|
||||
assert response["Code"]["Location"].startswith(
|
||||
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
||||
)
|
||||
response["Configuration"].should.equal(
|
||||
{
|
||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||
"CodeSize": len(zip_content),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:{}".format(
|
||||
_lambda_region, ACCOUNT_ID, function_name
|
||||
),
|
||||
"FunctionName": function_name,
|
||||
"Handler": "lambda_function.handler",
|
||||
"MemorySize": 128,
|
||||
"Role": get_role_name(),
|
||||
"Runtime": "python2.7",
|
||||
"Timeout": 3,
|
||||
"Version": "$LATEST",
|
||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
}
|
||||
response.should.have.key("Configuration")
|
||||
config = response["Configuration"]
|
||||
config.should.have.key("CodeSha256").equals(
|
||||
base64.b64encode(hashlib.sha256(zip_content).digest()).decode("utf-8")
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
||||
@mock_lambda
|
||||
def test_add_function_permission(key):
|
||||
"""
|
||||
Parametrized to ensure that we can add permission by using the FunctionName and the FunctionArn
|
||||
"""
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
f = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=(get_role_name()),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
config.should.have.key("CodeSize").equals(len(zip_content))
|
||||
config.should.have.key("Description").equals("test lambda function")
|
||||
config.should.have.key("FunctionArn").equals(
|
||||
f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}"
|
||||
)
|
||||
name_or_arn = f[key]
|
||||
|
||||
response = conn.add_permission(
|
||||
FunctionName=name_or_arn,
|
||||
StatementId="1",
|
||||
Action="lambda:InvokeFunction",
|
||||
Principal="432143214321",
|
||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
||||
SourceAccount="123412341234",
|
||||
EventSourceToken="blah",
|
||||
Qualifier="2",
|
||||
)
|
||||
assert "Statement" in response
|
||||
res = json.loads(response["Statement"])
|
||||
assert res["Action"] == "lambda:InvokeFunction"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
||||
@mock_lambda
|
||||
def test_get_function_policy(key):
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
f = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
name_or_arn = f[key]
|
||||
|
||||
conn.add_permission(
|
||||
FunctionName=name_or_arn,
|
||||
StatementId="1",
|
||||
Action="lambda:InvokeFunction",
|
||||
Principal="432143214321",
|
||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
||||
SourceAccount="123412341234",
|
||||
EventSourceToken="blah",
|
||||
Qualifier="2",
|
||||
)
|
||||
|
||||
response = conn.get_policy(FunctionName=name_or_arn)
|
||||
|
||||
assert "Policy" in response
|
||||
res = json.loads(response["Policy"])
|
||||
assert res["Statement"][0]["Action"] == "lambda:InvokeFunction"
|
||||
config.should.have.key("FunctionName").equals(function_name)
|
||||
config.should.have.key("Handler").equals("lambda_function.handler")
|
||||
config.should.have.key("MemorySize").equals(128)
|
||||
config.should.have.key("Role").equals(get_role_name())
|
||||
config.should.have.key("Runtime").equals("python2.7")
|
||||
config.should.have.key("Timeout").equals(3)
|
||||
config.should.have.key("Version").equals("$LATEST")
|
||||
config.should.have.key("State").equals("Active")
|
||||
config.should.have.key("Layers").equals([])
|
||||
config.should.have.key("LastUpdateStatus").equals("Successful")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@ -1085,7 +944,6 @@ def test_update_function_zip(key):
|
||||
)
|
||||
|
||||
response = conn.get_function(FunctionName=function_name, Qualifier="2")
|
||||
response["Configuration"].pop("LastModified")
|
||||
|
||||
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||
assert len(response["Code"]) == 2
|
||||
@ -1093,26 +951,16 @@ def test_update_function_zip(key):
|
||||
assert response["Code"]["Location"].startswith(
|
||||
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
||||
)
|
||||
response["Configuration"].should.equal(
|
||||
{
|
||||
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
|
||||
"CodeSize": len(zip_content_two),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:{}:2".format(
|
||||
_lambda_region, ACCOUNT_ID, function_name
|
||||
),
|
||||
"FunctionName": function_name,
|
||||
"Handler": "lambda_function.lambda_handler",
|
||||
"MemorySize": 128,
|
||||
"Role": fxn["Role"],
|
||||
"Runtime": "python2.7",
|
||||
"Timeout": 3,
|
||||
"Version": "2",
|
||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
}
|
||||
|
||||
config = response["Configuration"]
|
||||
config.should.have.key("CodeSize").equals(len(zip_content_two))
|
||||
config.should.have.key("Description").equals("test lambda function")
|
||||
config.should.have.key("FunctionArn").equals(
|
||||
f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:2"
|
||||
)
|
||||
config.should.have.key("FunctionName").equals(function_name)
|
||||
config.should.have.key("Version").equals("2")
|
||||
config.should.have.key("LastUpdateStatus").equals("Successful")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@ -1131,7 +979,7 @@ def test_update_function_s3():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
fxn = conn.create_function(
|
||||
conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
@ -1154,7 +1002,6 @@ def test_update_function_s3():
|
||||
)
|
||||
|
||||
response = conn.get_function(FunctionName=function_name, Qualifier="2")
|
||||
response["Configuration"].pop("LastModified")
|
||||
|
||||
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||
assert len(response["Code"]) == 2
|
||||
@ -1162,26 +1009,19 @@ def test_update_function_s3():
|
||||
assert response["Code"]["Location"].startswith(
|
||||
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
||||
)
|
||||
response["Configuration"].should.equal(
|
||||
{
|
||||
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
|
||||
"CodeSize": len(zip_content_two),
|
||||
"Description": "test lambda function",
|
||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:{}:2".format(
|
||||
_lambda_region, ACCOUNT_ID, function_name
|
||||
),
|
||||
"FunctionName": function_name,
|
||||
"Handler": "lambda_function.lambda_handler",
|
||||
"MemorySize": 128,
|
||||
"Role": fxn["Role"],
|
||||
"Runtime": "python2.7",
|
||||
"Timeout": 3,
|
||||
"Version": "2",
|
||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
}
|
||||
|
||||
config = response["Configuration"]
|
||||
config.should.have.key("CodeSha256").equals(
|
||||
base64.b64encode(hashlib.sha256(zip_content_two).digest()).decode("utf-8")
|
||||
)
|
||||
config.should.have.key("CodeSize").equals(len(zip_content_two))
|
||||
config.should.have.key("Description").equals("test lambda function")
|
||||
config.should.have.key("FunctionArn").equals(
|
||||
f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:2"
|
||||
)
|
||||
config.should.have.key("FunctionName").equals(function_name)
|
||||
config.should.have.key("Version").equals("2")
|
||||
config.should.have.key("LastUpdateStatus").equals("Successful")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@ -1210,45 +1050,6 @@ def test_create_function_with_unknown_arn():
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
||||
@mock_lambda
|
||||
def test_remove_function_permission(key):
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
f = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=(get_role_name()),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
name_or_arn = f[key]
|
||||
|
||||
conn.add_permission(
|
||||
FunctionName=name_or_arn,
|
||||
StatementId="1",
|
||||
Action="lambda:InvokeFunction",
|
||||
Principal="432143214321",
|
||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
||||
SourceAccount="123412341234",
|
||||
EventSourceToken="blah",
|
||||
Qualifier="2",
|
||||
)
|
||||
|
||||
remove = conn.remove_permission(
|
||||
FunctionName=name_or_arn, StatementId="1", Qualifier="2"
|
||||
)
|
||||
remove["ResponseMetadata"]["HTTPStatusCode"].should.equal(204)
|
||||
policy = conn.get_policy(FunctionName=name_or_arn, Qualifier="2")["Policy"]
|
||||
policy = json.loads(policy)
|
||||
policy["Statement"].should.equal([])
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_remove_unknown_permission_throws_error():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
|
299
tests/test_awslambda/test_lambda_alias.py
Normal file
299
tests/test_awslambda/test_lambda_alias.py
Normal file
@ -0,0 +1,299 @@
|
||||
"""Unit tests for lambda-supported APIs."""
|
||||
import boto3
|
||||
import pytest
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from moto import mock_lambda
|
||||
from moto.core import ACCOUNT_ID
|
||||
from uuid import uuid4
|
||||
from .utilities import (
|
||||
get_role_name,
|
||||
get_test_zip_file1,
|
||||
)
|
||||
|
||||
# See our Development Tips on writing tests for hints on how to write good tests:
|
||||
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_alias():
|
||||
client = boto3.client("lambda", region_name="ap-southeast-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
resp = client.create_alias(
|
||||
FunctionName=function_name, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:ap-southeast-1:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("$LATEST")
|
||||
resp.should.have.key("Description").equals("")
|
||||
resp.should.have.key("RevisionId")
|
||||
resp.shouldnt.have.key("RoutingConfig")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_alias_with_routing_config():
|
||||
client = boto3.client("lambda", region_name="ap-southeast-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
resp = client.create_alias(
|
||||
FunctionName=function_name,
|
||||
Name="alias1",
|
||||
FunctionVersion="$LATEST",
|
||||
Description="desc",
|
||||
RoutingConfig={"AdditionalVersionWeights": {"2": 0.5}},
|
||||
)
|
||||
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("Description").equals("desc")
|
||||
resp.should.have.key("RoutingConfig").equals(
|
||||
{"AdditionalVersionWeights": {"2": 0.5}}
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_alias_using_function_arn():
|
||||
client = boto3.client("lambda", region_name="ap-southeast-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
fn = client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
fn_arn = fn["FunctionArn"]
|
||||
|
||||
resp = client.create_alias(
|
||||
FunctionName=fn_arn, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:ap-southeast-1:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("$LATEST")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_delete_alias():
|
||||
client = boto3.client("lambda", region_name="us-east-2")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
client.create_alias(
|
||||
FunctionName=function_name, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
|
||||
client.delete_alias(FunctionName=function_name, Name="alias1")
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.get_alias(FunctionName=function_name, Name="alias1")
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_get_alias():
|
||||
client = boto3.client("lambda", region_name="us-west-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
client.create_alias(
|
||||
FunctionName=function_name, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
|
||||
resp = client.get_alias(FunctionName=function_name, Name="alias1")
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:us-west-1:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("$LATEST")
|
||||
resp.should.have.key("Description").equals("")
|
||||
resp.should.have.key("RevisionId")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_get_alias_using_function_arn():
|
||||
client = boto3.client("lambda", region_name="us-west-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
fn = client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
fn_arn = fn["FunctionArn"]
|
||||
|
||||
client.create_alias(
|
||||
FunctionName=function_name, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
|
||||
resp = client.get_alias(FunctionName=fn_arn, Name="alias1")
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:us-west-1:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("$LATEST")
|
||||
resp.should.have.key("Description").equals("")
|
||||
resp.should.have.key("RevisionId")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_get_alias_using_alias_arn():
|
||||
client = boto3.client("lambda", region_name="us-west-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
alias = client.create_alias(
|
||||
FunctionName=function_name, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
alias_arn = alias["AliasArn"]
|
||||
|
||||
resp = client.get_alias(FunctionName=alias_arn, Name="alias1")
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:us-west-1:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("$LATEST")
|
||||
resp.should.have.key("Description").equals("")
|
||||
resp.should.have.key("RevisionId")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_get_unknown_alias():
|
||||
client = boto3.client("lambda", region_name="us-west-1")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.get_alias(FunctionName=function_name, Name="unknown")
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
err["Message"].should.equal(
|
||||
f"Cannot find alias arn: arn:aws:lambda:us-west-1:{ACCOUNT_ID}:function:{function_name}:unknown"
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_update_alias():
|
||||
client = boto3.client("lambda", region_name="us-east-2")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
client.create_alias(
|
||||
FunctionName=function_name, Name="alias1", FunctionVersion="$LATEST"
|
||||
)
|
||||
|
||||
resp = client.update_alias(
|
||||
FunctionName=function_name,
|
||||
Name="alias1",
|
||||
FunctionVersion="1",
|
||||
Description="updated desc",
|
||||
)
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:us-east-2:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("1")
|
||||
resp.should.have.key("Description").equals("updated desc")
|
||||
resp.should.have.key("RevisionId")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_update_alias_routingconfig():
|
||||
client = boto3.client("lambda", region_name="us-east-2")
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
client.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python3.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"ZipFile": get_test_zip_file1()},
|
||||
)
|
||||
|
||||
client.create_alias(
|
||||
FunctionName=function_name,
|
||||
Name="alias1",
|
||||
Description="desc",
|
||||
FunctionVersion="$LATEST",
|
||||
)
|
||||
|
||||
resp = client.update_alias(
|
||||
FunctionName=function_name,
|
||||
Name="alias1",
|
||||
RoutingConfig={"AdditionalVersionWeights": {"2": 0.5}},
|
||||
)
|
||||
|
||||
resp.should.have.key("AliasArn").equals(
|
||||
f"arn:aws:lambda:us-east-2:{ACCOUNT_ID}:function:{function_name}:alias1"
|
||||
)
|
||||
resp.should.have.key("Name").equals("alias1")
|
||||
resp.should.have.key("FunctionVersion").equals("$LATEST")
|
||||
resp.should.have.key("Description").equals("desc")
|
||||
resp.should.have.key("RoutingConfig").equals(
|
||||
{"AdditionalVersionWeights": {"2": 0.5}}
|
||||
)
|
@ -5,7 +5,6 @@ import sure # noqa # pylint: disable=unused-import
|
||||
from botocore.exceptions import ClientError
|
||||
from freezegun import freeze_time
|
||||
from moto import mock_lambda, mock_s3
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.sts.models import ACCOUNT_ID
|
||||
from uuid import uuid4
|
||||
|
||||
@ -15,6 +14,23 @@ _lambda_region = "us-west-2"
|
||||
boto3.setup_default_session(region_name=_lambda_region)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_publish_lambda_layers__without_content():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
layer_name = str(uuid4())[0:6]
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
conn.publish_layer_version(
|
||||
LayerName=layer_name,
|
||||
Content={},
|
||||
CompatibleRuntimes=["python3.6"],
|
||||
LicenseInfo="MIT",
|
||||
)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValueException")
|
||||
err["Message"].should.equal("Missing Content")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
@freeze_time("2015-01-01 00:00:00")
|
||||
@ -31,13 +47,6 @@ def test_get_lambda_layers():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
layer_name = str(uuid4())[0:6]
|
||||
|
||||
with pytest.raises((RESTError, ClientError)):
|
||||
conn.publish_layer_version(
|
||||
LayerName=layer_name,
|
||||
Content={},
|
||||
CompatibleRuntimes=["python3.6"],
|
||||
LicenseInfo="MIT",
|
||||
)
|
||||
conn.publish_layer_version(
|
||||
LayerName=layer_name,
|
||||
Content={"ZipFile": get_test_zip_file1()},
|
||||
@ -123,3 +132,93 @@ def test_get_lambda_layers():
|
||||
Environment={"Variables": {"test_variable": "test_value"}},
|
||||
Layers=[(expected_arn + "3")],
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_get_layer_version():
|
||||
bucket_name = str(uuid4())
|
||||
s3_conn = boto3.client("s3", _lambda_region)
|
||||
s3_conn.create_bucket(
|
||||
Bucket=bucket_name,
|
||||
CreateBucketConfiguration={"LocationConstraint": _lambda_region},
|
||||
)
|
||||
|
||||
zip_content = get_test_zip_file1()
|
||||
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
layer_name = str(uuid4())[0:6]
|
||||
|
||||
resp = conn.publish_layer_version(
|
||||
LayerName=layer_name,
|
||||
Content={"ZipFile": get_test_zip_file1()},
|
||||
CompatibleRuntimes=["python3.6"],
|
||||
LicenseInfo="MIT",
|
||||
)
|
||||
layer_version = resp["Version"]
|
||||
|
||||
resp = conn.get_layer_version(LayerName=layer_name, VersionNumber=layer_version)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_get_layer_version__unknown():
|
||||
bucket_name = str(uuid4())
|
||||
s3_conn = boto3.client("s3", _lambda_region)
|
||||
s3_conn.create_bucket(
|
||||
Bucket=bucket_name,
|
||||
CreateBucketConfiguration={"LocationConstraint": _lambda_region},
|
||||
)
|
||||
|
||||
zip_content = get_test_zip_file1()
|
||||
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
layer_name = str(uuid4())[0:6]
|
||||
|
||||
# Delete Layer that never existed
|
||||
with pytest.raises(ClientError) as exc:
|
||||
conn.get_layer_version(LayerName=layer_name, VersionNumber=1)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
|
||||
conn.publish_layer_version(
|
||||
LayerName=layer_name,
|
||||
Content={"ZipFile": get_test_zip_file1()},
|
||||
CompatibleRuntimes=["python3.6"],
|
||||
LicenseInfo="MIT",
|
||||
)
|
||||
|
||||
# Delete Version that never existed
|
||||
with pytest.raises(ClientError) as exc:
|
||||
conn.get_layer_version(LayerName=layer_name, VersionNumber=999)
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_delete_layer_version():
|
||||
bucket_name = str(uuid4())
|
||||
s3_conn = boto3.client("s3", _lambda_region)
|
||||
s3_conn.create_bucket(
|
||||
Bucket=bucket_name,
|
||||
CreateBucketConfiguration={"LocationConstraint": _lambda_region},
|
||||
)
|
||||
|
||||
zip_content = get_test_zip_file1()
|
||||
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
layer_name = str(uuid4())[0:6]
|
||||
|
||||
resp = conn.publish_layer_version(
|
||||
LayerName=layer_name,
|
||||
Content={"ZipFile": get_test_zip_file1()},
|
||||
CompatibleRuntimes=["python3.6"],
|
||||
LicenseInfo="MIT",
|
||||
)
|
||||
layer_version = resp["Version"]
|
||||
|
||||
conn.delete_layer_version(LayerName=layer_name, VersionNumber=layer_version)
|
||||
|
||||
result = conn.list_layer_versions(LayerName=layer_name)["LayerVersions"]
|
||||
result.should.equal([])
|
||||
|
137
tests/test_awslambda/test_lambda_policy.py
Normal file
137
tests/test_awslambda/test_lambda_policy.py
Normal file
@ -0,0 +1,137 @@
|
||||
import boto3
|
||||
import json
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
import pytest
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from moto import mock_lambda, mock_s3
|
||||
from uuid import uuid4
|
||||
from .utilities import get_role_name, get_test_zip_file1
|
||||
|
||||
_lambda_region = "us-west-2"
|
||||
boto3.setup_default_session(region_name=_lambda_region)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
||||
@mock_lambda
|
||||
def test_add_function_permission(key):
|
||||
"""
|
||||
Parametrized to ensure that we can add permission by using the FunctionName and the FunctionArn
|
||||
"""
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
f = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=(get_role_name()),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
name_or_arn = f[key]
|
||||
|
||||
response = conn.add_permission(
|
||||
FunctionName=name_or_arn,
|
||||
StatementId="1",
|
||||
Action="lambda:InvokeFunction",
|
||||
Principal="432143214321",
|
||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
||||
SourceAccount="123412341234",
|
||||
EventSourceToken="blah",
|
||||
Qualifier="2",
|
||||
)
|
||||
assert "Statement" in response
|
||||
res = json.loads(response["Statement"])
|
||||
assert res["Action"] == "lambda:InvokeFunction"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
||||
@mock_lambda
|
||||
def test_get_function_policy(key):
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
f = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
name_or_arn = f[key]
|
||||
|
||||
conn.add_permission(
|
||||
FunctionName=name_or_arn,
|
||||
StatementId="1",
|
||||
Action="lambda:InvokeFunction",
|
||||
Principal="432143214321",
|
||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
||||
SourceAccount="123412341234",
|
||||
EventSourceToken="blah",
|
||||
Qualifier="2",
|
||||
)
|
||||
|
||||
response = conn.get_policy(FunctionName=name_or_arn)
|
||||
|
||||
assert "Policy" in response
|
||||
res = json.loads(response["Policy"])
|
||||
assert res["Statement"][0]["Action"] == "lambda:InvokeFunction"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
||||
@mock_lambda
|
||||
def test_remove_function_permission(key):
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
zip_content = get_test_zip_file1()
|
||||
function_name = str(uuid4())[0:6]
|
||||
f = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=(get_role_name()),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"ZipFile": zip_content},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
name_or_arn = f[key]
|
||||
|
||||
conn.add_permission(
|
||||
FunctionName=name_or_arn,
|
||||
StatementId="1",
|
||||
Action="lambda:InvokeFunction",
|
||||
Principal="432143214321",
|
||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
||||
SourceAccount="123412341234",
|
||||
EventSourceToken="blah",
|
||||
Qualifier="2",
|
||||
)
|
||||
|
||||
remove = conn.remove_permission(
|
||||
FunctionName=name_or_arn, StatementId="1", Qualifier="2"
|
||||
)
|
||||
remove["ResponseMetadata"]["HTTPStatusCode"].should.equal(204)
|
||||
policy = conn.get_policy(FunctionName=name_or_arn, Qualifier="2")["Policy"]
|
||||
policy = json.loads(policy)
|
||||
policy["Statement"].should.equal([])
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_get_unknown_policy():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
conn.get_policy(FunctionName="unknown")
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
err["Message"].should.equal("Function not found: unknown")
|
120
tests/test_awslambda/test_lambda_tags.py
Normal file
120
tests/test_awslambda/test_lambda_tags.py
Normal file
@ -0,0 +1,120 @@
|
||||
import botocore.client
|
||||
import boto3
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
from moto import mock_lambda, mock_s3
|
||||
from moto.core.models import ACCOUNT_ID
|
||||
from uuid import uuid4
|
||||
from .utilities import get_role_name, get_test_zip_file2
|
||||
|
||||
_lambda_region = "us-east-1"
|
||||
boto3.setup_default_session(region_name=_lambda_region)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_tags():
|
||||
"""
|
||||
test list_tags -> tag_resource -> list_tags -> tag_resource -> list_tags -> untag_resource -> list_tags integration
|
||||
"""
|
||||
bucket_name = str(uuid4())
|
||||
s3_conn = boto3.client("s3", _lambda_region)
|
||||
s3_conn.create_bucket(Bucket=bucket_name)
|
||||
|
||||
zip_content = get_test_zip_file2()
|
||||
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
function = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
|
||||
# List tags when there are none
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(dict())
|
||||
|
||||
# List tags when there is one
|
||||
conn.tag_resource(Resource=function["FunctionArn"], Tags=dict(spam="eggs"))[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(200)
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(
|
||||
dict(spam="eggs")
|
||||
)
|
||||
|
||||
# List tags when another has been added
|
||||
conn.tag_resource(Resource=function["FunctionArn"], Tags=dict(foo="bar"))[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(200)
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(
|
||||
dict(spam="eggs", foo="bar")
|
||||
)
|
||||
|
||||
# Untag resource
|
||||
conn.untag_resource(Resource=function["FunctionArn"], TagKeys=["spam", "trolls"])[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(204)
|
||||
conn.list_tags(Resource=function["FunctionArn"])["Tags"].should.equal(
|
||||
dict(foo="bar")
|
||||
)
|
||||
|
||||
# Untag a tag that does not exist (no error and no change)
|
||||
conn.untag_resource(Resource=function["FunctionArn"], TagKeys=["spam"])[
|
||||
"ResponseMetadata"
|
||||
]["HTTPStatusCode"].should.equal(204)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@mock_s3
|
||||
def test_create_function_with_tags():
|
||||
bucket_name = str(uuid4())
|
||||
s3_conn = boto3.client("s3", _lambda_region)
|
||||
s3_conn.create_bucket(Bucket=bucket_name)
|
||||
|
||||
zip_content = get_test_zip_file2()
|
||||
s3_conn.put_object(Bucket=bucket_name, Key="test.zip", Body=zip_content)
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
|
||||
function = conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Runtime="python2.7",
|
||||
Role=get_role_name(),
|
||||
Handler="lambda_function.handler",
|
||||
Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
|
||||
Tags={"key1": "val1", "key2": "val2"},
|
||||
)
|
||||
|
||||
tags = conn.list_tags(Resource=function["FunctionArn"])["Tags"]
|
||||
tags.should.equal({"key1": "val1", "key2": "val2"})
|
||||
|
||||
result = conn.get_function(FunctionName=function_name)
|
||||
result.should.have.key("Tags").equals({"key1": "val1", "key2": "val2"})
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_tags_not_found():
|
||||
"""
|
||||
Test list_tags and tag_resource when the lambda with the given arn does not exist
|
||||
"""
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
conn.list_tags.when.called_with(
|
||||
Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID)
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
conn.tag_resource.when.called_with(
|
||||
Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
|
||||
Tags=dict(spam="eggs"),
|
||||
).should.throw(botocore.client.ClientError)
|
||||
|
||||
conn.untag_resource.when.called_with(
|
||||
Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
|
||||
TagKeys=["spam"],
|
||||
).should.throw(botocore.client.ClientError)
|
Loading…
Reference in New Issue
Block a user