Improvements: AWSLambda (#4943)
This commit is contained in:
parent
bbd4b2afc3
commit
96c9391beb
@ -3423,25 +3423,25 @@
|
|||||||
|
|
||||||
## lambda
|
## lambda
|
||||||
<details>
|
<details>
|
||||||
<summary>43% implemented</summary>
|
<summary>53% implemented</summary>
|
||||||
|
|
||||||
- [ ] add_layer_version_permission
|
- [ ] add_layer_version_permission
|
||||||
- [X] add_permission
|
- [X] add_permission
|
||||||
- [ ] create_alias
|
- [X] create_alias
|
||||||
- [ ] create_code_signing_config
|
- [ ] create_code_signing_config
|
||||||
- [X] create_event_source_mapping
|
- [X] create_event_source_mapping
|
||||||
- [X] create_function
|
- [X] create_function
|
||||||
- [ ] delete_alias
|
- [X] delete_alias
|
||||||
- [ ] delete_code_signing_config
|
- [ ] delete_code_signing_config
|
||||||
- [X] delete_event_source_mapping
|
- [X] delete_event_source_mapping
|
||||||
- [X] delete_function
|
- [X] delete_function
|
||||||
- [ ] delete_function_code_signing_config
|
- [ ] delete_function_code_signing_config
|
||||||
- [X] delete_function_concurrency
|
- [X] delete_function_concurrency
|
||||||
- [ ] delete_function_event_invoke_config
|
- [ ] delete_function_event_invoke_config
|
||||||
- [ ] delete_layer_version
|
- [X] delete_layer_version
|
||||||
- [ ] delete_provisioned_concurrency_config
|
- [ ] delete_provisioned_concurrency_config
|
||||||
- [ ] get_account_settings
|
- [ ] get_account_settings
|
||||||
- [ ] get_alias
|
- [X] get_alias
|
||||||
- [X] get_code_signing_config
|
- [X] get_code_signing_config
|
||||||
- [X] get_event_source_mapping
|
- [X] get_event_source_mapping
|
||||||
- [X] get_function
|
- [X] get_function
|
||||||
@ -3449,7 +3449,7 @@
|
|||||||
- [X] get_function_concurrency
|
- [X] get_function_concurrency
|
||||||
- [ ] get_function_configuration
|
- [ ] get_function_configuration
|
||||||
- [ ] get_function_event_invoke_config
|
- [ ] get_function_event_invoke_config
|
||||||
- [ ] get_layer_version
|
- [X] get_layer_version
|
||||||
- [ ] get_layer_version_by_arn
|
- [ ] get_layer_version_by_arn
|
||||||
- [ ] get_layer_version_policy
|
- [ ] get_layer_version_policy
|
||||||
- [X] get_policy
|
- [X] get_policy
|
||||||
@ -3477,7 +3477,7 @@
|
|||||||
- [X] remove_permission
|
- [X] remove_permission
|
||||||
- [X] tag_resource
|
- [X] tag_resource
|
||||||
- [X] untag_resource
|
- [X] untag_resource
|
||||||
- [ ] update_alias
|
- [X] update_alias
|
||||||
- [ ] update_code_signing_config
|
- [ ] update_code_signing_config
|
||||||
- [X] update_event_source_mapping
|
- [X] update_event_source_mapping
|
||||||
- [X] update_function_code
|
- [X] update_function_code
|
||||||
|
@ -29,21 +29,21 @@ lambda
|
|||||||
|
|
||||||
- [ ] add_layer_version_permission
|
- [ ] add_layer_version_permission
|
||||||
- [X] add_permission
|
- [X] add_permission
|
||||||
- [ ] create_alias
|
- [X] create_alias
|
||||||
- [ ] create_code_signing_config
|
- [ ] create_code_signing_config
|
||||||
- [X] create_event_source_mapping
|
- [X] create_event_source_mapping
|
||||||
- [X] create_function
|
- [X] create_function
|
||||||
- [ ] delete_alias
|
- [X] delete_alias
|
||||||
- [ ] delete_code_signing_config
|
- [ ] delete_code_signing_config
|
||||||
- [X] delete_event_source_mapping
|
- [X] delete_event_source_mapping
|
||||||
- [X] delete_function
|
- [X] delete_function
|
||||||
- [ ] delete_function_code_signing_config
|
- [ ] delete_function_code_signing_config
|
||||||
- [X] delete_function_concurrency
|
- [X] delete_function_concurrency
|
||||||
- [ ] delete_function_event_invoke_config
|
- [ ] delete_function_event_invoke_config
|
||||||
- [ ] delete_layer_version
|
- [X] delete_layer_version
|
||||||
- [ ] delete_provisioned_concurrency_config
|
- [ ] delete_provisioned_concurrency_config
|
||||||
- [ ] get_account_settings
|
- [ ] get_account_settings
|
||||||
- [ ] get_alias
|
- [X] get_alias
|
||||||
- [X] get_code_signing_config
|
- [X] get_code_signing_config
|
||||||
- [X] get_event_source_mapping
|
- [X] get_event_source_mapping
|
||||||
- [X] get_function
|
- [X] get_function
|
||||||
@ -51,7 +51,7 @@ lambda
|
|||||||
- [X] get_function_concurrency
|
- [X] get_function_concurrency
|
||||||
- [ ] get_function_configuration
|
- [ ] get_function_configuration
|
||||||
- [ ] get_function_event_invoke_config
|
- [ ] get_function_event_invoke_config
|
||||||
- [ ] get_layer_version
|
- [X] get_layer_version
|
||||||
- [ ] get_layer_version_by_arn
|
- [ ] get_layer_version_by_arn
|
||||||
- [ ] get_layer_version_policy
|
- [ ] get_layer_version_policy
|
||||||
- [X] get_policy
|
- [X] get_policy
|
||||||
@ -79,7 +79,11 @@ lambda
|
|||||||
- [X] remove_permission
|
- [X] remove_permission
|
||||||
- [X] tag_resource
|
- [X] tag_resource
|
||||||
- [X] untag_resource
|
- [X] untag_resource
|
||||||
- [ ] update_alias
|
- [X] update_alias
|
||||||
|
|
||||||
|
The RevisionId parameter is not yet implemented
|
||||||
|
|
||||||
|
|
||||||
- [ ] update_code_signing_config
|
- [ ] update_code_signing_config
|
||||||
- [X] update_event_source_mapping
|
- [X] update_event_source_mapping
|
||||||
- [X] update_function_code
|
- [X] update_function_code
|
||||||
|
@ -35,6 +35,27 @@ class PreconditionFailedException(JsonRESTError):
|
|||||||
super().__init__("PreconditionFailedException", message)
|
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):
|
class UnknownPolicyException(LambdaClientError):
|
||||||
code = 404
|
code = 404
|
||||||
|
|
||||||
@ -43,10 +64,3 @@ class UnknownPolicyException(LambdaClientError):
|
|||||||
"ResourceNotFoundException",
|
"ResourceNotFoundException",
|
||||||
"No policy is associated with the given resource.",
|
"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
|
import requests.exceptions
|
||||||
|
|
||||||
from moto.awslambda.policy import Policy
|
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.core.exceptions import RESTError
|
||||||
from moto.iam.models import iam_backend
|
from moto.iam.models import iam_backend
|
||||||
from moto.iam.exceptions import IAMNotFoundException
|
from moto.iam.exceptions import IAMNotFoundException
|
||||||
@ -36,7 +36,9 @@ from .exceptions import (
|
|||||||
CrossAccountNotAllowed,
|
CrossAccountNotAllowed,
|
||||||
InvalidRoleFormat,
|
InvalidRoleFormat,
|
||||||
InvalidParameterValueException,
|
InvalidParameterValueException,
|
||||||
|
UnknownLayerException,
|
||||||
UnknownFunctionException,
|
UnknownFunctionException,
|
||||||
|
UnknownAliasException,
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
make_function_arn,
|
make_function_arn,
|
||||||
@ -50,13 +52,11 @@ from moto.dynamodb import dynamodb_backends
|
|||||||
from moto.dynamodbstreams import dynamodbstreams_backends
|
from moto.dynamodbstreams import dynamodbstreams_backends
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
from moto.utilities.docker_utilities import DockerModel, parse_image_ref
|
from moto.utilities.docker_utilities import DockerModel, parse_image_ref
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
try:
|
|
||||||
from tempfile import TemporaryDirectory
|
|
||||||
except ImportError:
|
|
||||||
from backports.tempfile import TemporaryDirectory
|
|
||||||
|
|
||||||
docker_3 = docker.__version__[0] >= "3"
|
docker_3 = docker.__version__[0] >= "3"
|
||||||
|
|
||||||
@ -110,25 +110,26 @@ class _DockerDataVolumeContext:
|
|||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
# See if volume is already known
|
# See if volume is already known
|
||||||
with self.__class__._lock:
|
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
|
self._vol_ref.refcount += 1
|
||||||
if self._vol_ref.refcount > 1:
|
if self._vol_ref.refcount > 1:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# See if the volume already exists
|
# See if the volume already exists
|
||||||
for vol in self._lambda_func.docker_client.volumes.list():
|
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
|
self._vol_ref.volume = vol
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# It doesn't exist so we need to create it
|
# It doesn't exist so we need to create it
|
||||||
self._vol_ref.volume = self._lambda_func.docker_client.volumes.create(
|
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:
|
if docker_3:
|
||||||
volumes = {self.name: {"bind": "/tmp/data", "mode": "rw"}}
|
volumes = {self.name: {"bind": "/tmp/data", "mode": "rw"}}
|
||||||
else:
|
else:
|
||||||
volumes = {self.name: "/tmp/data"}
|
volumes = {self.name: "/tmp/data"}
|
||||||
|
|
||||||
self._lambda_func.docker_client.images.pull(
|
self._lambda_func.docker_client.images.pull(
|
||||||
":".join(parse_image_ref("alpine"))
|
":".join(parse_image_ref("alpine"))
|
||||||
)
|
)
|
||||||
@ -164,7 +165,17 @@ def _zipfile_content(zipfile):
|
|||||||
except Exception:
|
except Exception:
|
||||||
to_unzip_code = base64.b64decode(zipfile)
|
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):
|
def _validate_s3_bucket_and_key(data):
|
||||||
@ -228,15 +239,21 @@ class LayerVersion(CloudFormationModel):
|
|||||||
self._layer = None
|
self._layer = None
|
||||||
|
|
||||||
if "ZipFile" in self.content:
|
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:
|
else:
|
||||||
key = _validate_s3_bucket_and_key(self.content)
|
key = _validate_s3_bucket_and_key(self.content)
|
||||||
if key:
|
if key:
|
||||||
self.code_bytes = key.value
|
(
|
||||||
self.code_size = key.size
|
self.code_bytes,
|
||||||
self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
|
self.code_size,
|
||||||
|
self.code_sha_256,
|
||||||
|
self.code_digest,
|
||||||
|
) = _s3_content(key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def arn(self):
|
def arn(self):
|
||||||
@ -251,7 +268,13 @@ class LayerVersion(CloudFormationModel):
|
|||||||
|
|
||||||
def get_layer_version(self):
|
def get_layer_version(self):
|
||||||
return {
|
return {
|
||||||
|
"Content": {
|
||||||
|
"Location": "s3://",
|
||||||
|
"CodeSha256": self.code_sha_256,
|
||||||
|
"CodeSize": self.code_size,
|
||||||
|
},
|
||||||
"Version": self.version,
|
"Version": self.version,
|
||||||
|
"LayerArn": self._layer.layer_arn,
|
||||||
"LayerVersionArn": self.arn,
|
"LayerVersionArn": self.arn,
|
||||||
"CreatedDate": self.created_date,
|
"CreatedDate": self.created_date,
|
||||||
"CompatibleRuntimes": self.compatible_runtimes,
|
"CompatibleRuntimes": self.compatible_runtimes,
|
||||||
@ -288,6 +311,38 @@ class LayerVersion(CloudFormationModel):
|
|||||||
return layer_version
|
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):
|
class Layer(object):
|
||||||
def __init__(self, name, region):
|
def __init__(self, name, region):
|
||||||
self.region = region
|
self.region = region
|
||||||
@ -302,6 +357,9 @@ class Layer(object):
|
|||||||
layer_version.attach(self, self._latest_version)
|
layer_version.attach(self, self._latest_version)
|
||||||
self.layer_versions[str(self._latest_version)] = layer_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):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
"LayerName": self.name,
|
"LayerName": self.name,
|
||||||
@ -338,6 +396,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
self.signing_profile_version_arn = spec.get("SigningProfileVersionArn")
|
self.signing_profile_version_arn = spec.get("SigningProfileVersionArn")
|
||||||
self.signing_job_arn = spec.get("SigningJobArn")
|
self.signing_job_arn = spec.get("SigningJobArn")
|
||||||
self.code_signing_config_arn = spec.get("CodeSigningConfigArn")
|
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)
|
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")
|
self.last_modified = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
if "ZipFile" in self.code:
|
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
|
# TODO: we should be putting this in a lambda bucket
|
||||||
self.code["UUID"] = str(uuid.uuid4())
|
self.code["UUID"] = str(uuid.uuid4())
|
||||||
@ -361,9 +423,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
else:
|
else:
|
||||||
key = _validate_s3_bucket_and_key(self.code)
|
key = _validate_s3_bucket_and_key(self.code)
|
||||||
if key:
|
if key:
|
||||||
self.code_bytes = key.value
|
(
|
||||||
self.code_size = key.size
|
self.code_bytes,
|
||||||
self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
|
self.code_size,
|
||||||
|
self.code_sha_256,
|
||||||
|
self.code_digest,
|
||||||
|
) = _s3_content(key)
|
||||||
else:
|
else:
|
||||||
self.code_bytes = ""
|
self.code_bytes = ""
|
||||||
self.code_size = 0
|
self.code_size = 0
|
||||||
@ -378,6 +443,8 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
else:
|
else:
|
||||||
self.tags = dict()
|
self.tags = dict()
|
||||||
|
|
||||||
|
self._aliases = dict()
|
||||||
|
|
||||||
def set_version(self, version):
|
def set_version(self, version):
|
||||||
self.function_arn = make_function_ver_arn(
|
self.function_arn = make_function_ver_arn(
|
||||||
self.region, ACCOUNT_ID, self.function_name, version
|
self.region, ACCOUNT_ID, self.function_name, version
|
||||||
@ -420,7 +487,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
"FunctionName": self.function_name,
|
"FunctionName": self.function_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_configuration(self):
|
def get_configuration(self, on_create=False):
|
||||||
config = {
|
config = {
|
||||||
"CodeSha256": self.code_sha_256,
|
"CodeSha256": self.code_sha_256,
|
||||||
"CodeSize": self.code_size,
|
"CodeSize": self.code_size,
|
||||||
@ -440,7 +507,11 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
"Layers": self.layers,
|
"Layers": self.layers,
|
||||||
"SigningProfileVersionArn": self.signing_profile_version_arn,
|
"SigningProfileVersionArn": self.signing_profile_version_arn,
|
||||||
"SigningJobArn": self.signing_job_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:
|
if self.environment_vars:
|
||||||
config["Environment"] = {"Variables": self.environment_vars}
|
config["Environment"] = {"Variables": self.environment_vars}
|
||||||
|
|
||||||
@ -456,6 +527,8 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
},
|
},
|
||||||
"Configuration": self.get_configuration(),
|
"Configuration": self.get_configuration(),
|
||||||
}
|
}
|
||||||
|
if self.tags:
|
||||||
|
code["Tags"] = self.tags
|
||||||
if self.reserved_concurrency:
|
if self.reserved_concurrency:
|
||||||
code.update(
|
code.update(
|
||||||
{
|
{
|
||||||
@ -496,19 +569,12 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
if "ZipFile" in updated_spec:
|
if "ZipFile" in updated_spec:
|
||||||
self.code["ZipFile"] = updated_spec["ZipFile"]
|
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
|
self.code_bytes,
|
||||||
# in both places now
|
self.code_size,
|
||||||
try:
|
self.code_sha_256,
|
||||||
to_unzip_code = base64.b64decode(
|
self.code_digest,
|
||||||
bytes(updated_spec["ZipFile"], "utf-8")
|
) = _zipfile_content(updated_spec["ZipFile"])
|
||||||
)
|
|
||||||
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()
|
|
||||||
|
|
||||||
# TODO: we should be putting this in a lambda bucket
|
# TODO: we should be putting this in a lambda bucket
|
||||||
self.code["UUID"] = str(uuid.uuid4())
|
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.",
|
"Error occurred while GetObject. S3 Error Code: NoSuchKey. S3 Error Message: The specified key does not exist.",
|
||||||
)
|
)
|
||||||
if key:
|
if key:
|
||||||
self.code_bytes = key.value
|
(
|
||||||
self.code_size = key.size
|
self.code_bytes,
|
||||||
self.code_sha_256 = hashlib.sha256(key.value).hexdigest()
|
self.code_size,
|
||||||
|
self.code_sha_256,
|
||||||
|
self.code_digest,
|
||||||
|
) = _s3_content(key)
|
||||||
self.code["S3Bucket"] = updated_spec["S3Bucket"]
|
self.code["S3Bucket"] = updated_spec["S3Bucket"]
|
||||||
self.code["S3Key"] = updated_spec["S3Key"]
|
self.code["S3Key"] = updated_spec["S3Key"]
|
||||||
|
|
||||||
@ -781,6 +850,32 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
|||||||
def delete(self, region):
|
def delete(self, region):
|
||||||
lambda_backends[region].delete_function(self.function_name)
|
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):
|
class EventSourceMapping(CloudFormationModel):
|
||||||
def __init__(self, spec):
|
def __init__(self, spec):
|
||||||
@ -934,8 +1029,9 @@ class LambdaVersion(CloudFormationModel):
|
|||||||
|
|
||||||
class LambdaStorage(object):
|
class LambdaStorage(object):
|
||||||
def __init__(self, region_name):
|
def __init__(self, region_name):
|
||||||
# Format 'func_name' {'alias': {}, 'versions': []}
|
# Format 'func_name' {'versions': []}
|
||||||
self._functions = {}
|
self._functions = {}
|
||||||
|
self._aliases = dict()
|
||||||
self._arns = weakref.WeakValueDictionary()
|
self._arns = weakref.WeakValueDictionary()
|
||||||
self.region_name = region_name
|
self.region_name = region_name
|
||||||
|
|
||||||
@ -950,8 +1046,25 @@ class LambdaStorage(object):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_alias(self, name, alias):
|
def delete_alias(self, name, function_name):
|
||||||
return self._functions[name]["alias"].get(alias, None)
|
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):
|
def get_function_by_name(self, name, qualifier=None):
|
||||||
if name not in self._functions:
|
if name not in self._functions:
|
||||||
@ -974,6 +1087,11 @@ class LambdaStorage(object):
|
|||||||
return [latest] + self._functions[name]["versions"]
|
return [latest] + self._functions[name]["versions"]
|
||||||
|
|
||||||
def get_arn(self, arn):
|
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)
|
return self._arns.get(arn, None)
|
||||||
|
|
||||||
def get_function_by_name_or_arn(self, name_or_arn, qualifier=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:
|
if fn.function_name in self._functions:
|
||||||
self._functions[fn.function_name]["latest"] = fn
|
self._functions[fn.function_name]["latest"] = fn
|
||||||
else:
|
else:
|
||||||
self._functions[fn.function_name] = {
|
self._functions[fn.function_name] = {"latest": fn, "versions": []}
|
||||||
"latest": fn,
|
|
||||||
"versions": [],
|
|
||||||
"alias": weakref.WeakValueDictionary(),
|
|
||||||
}
|
|
||||||
# instantiate a new policy for this version of the lambda
|
# instantiate a new policy for this version of the lambda
|
||||||
fn.policy = Policy(fn)
|
fn.policy = Policy(fn)
|
||||||
self._arns[fn.function_arn] = fn
|
self._arns[fn.function_arn] = fn
|
||||||
@ -1122,6 +1236,17 @@ class LayerStorage(object):
|
|||||||
def list_layers(self):
|
def list_layers(self):
|
||||||
return [layer.to_dict() for layer in self._layers.values()]
|
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):
|
def get_layer_versions(self, layer_name):
|
||||||
if layer_name in self._layers:
|
if layer_name in self._layers:
|
||||||
return list(iter(self._layers[layer_name].layer_versions.values()))
|
return list(iter(self._layers[layer_name].layer_versions.values()))
|
||||||
@ -1188,7 +1313,7 @@ class LambdaBackend(BaseBackend):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, region_name):
|
def __init__(self, region_name):
|
||||||
self._lambdas = LambdaStorage(region_name)
|
self._lambdas = LambdaStorage(region_name=region_name)
|
||||||
self._event_source_mappings = {}
|
self._event_source_mappings = {}
|
||||||
self._layers = LayerStorage()
|
self._layers = LayerStorage()
|
||||||
self.region_name = region_name
|
self.region_name = region_name
|
||||||
@ -1205,6 +1330,29 @@ class LambdaBackend(BaseBackend):
|
|||||||
service_region, zones, "lambda"
|
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):
|
def create_function(self, spec):
|
||||||
function_name = spec.get("FunctionName", None)
|
function_name = spec.get("FunctionName", None)
|
||||||
if function_name is None:
|
if function_name is None:
|
||||||
@ -1274,9 +1422,7 @@ class LambdaBackend(BaseBackend):
|
|||||||
required = ["LayerName", "Content"]
|
required = ["LayerName", "Content"]
|
||||||
for param in required:
|
for param in required:
|
||||||
if not spec.get(param):
|
if not spec.get(param):
|
||||||
raise RESTError(
|
raise InvalidParameterValueException("Missing {}".format(param))
|
||||||
"InvalidParameterValueException", "Missing {}".format(param)
|
|
||||||
)
|
|
||||||
layer_version = LayerVersion(spec, self.region_name)
|
layer_version = LayerVersion(spec, self.region_name)
|
||||||
self._layers.put_layer_version(layer_version)
|
self._layers.put_layer_version(layer_version)
|
||||||
return layer_version
|
return layer_version
|
||||||
@ -1284,6 +1430,12 @@ class LambdaBackend(BaseBackend):
|
|||||||
def list_layers(self):
|
def list_layers(self):
|
||||||
return self._layers.list_layers()
|
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):
|
def get_layer_versions(self, layer_name):
|
||||||
return self._layers.get_layer_versions(layer_name)
|
return self._layers.get_layer_versions(layer_name)
|
||||||
|
|
||||||
@ -1485,10 +1637,8 @@ class LambdaBackend(BaseBackend):
|
|||||||
|
|
||||||
def get_policy(self, function_name):
|
def get_policy(self, function_name):
|
||||||
fn = self.get_function(function_name)
|
fn = self.get_function(function_name)
|
||||||
return fn.policy.get_policy()
|
if not fn:
|
||||||
|
raise UnknownFunctionException(function_name)
|
||||||
def get_policy_wire_format(self, function_name):
|
|
||||||
fn = self.get_function(function_name)
|
|
||||||
return fn.policy.wire_format()
|
return fn.policy.wire_format()
|
||||||
|
|
||||||
def update_function_code(self, function_name, qualifier, body):
|
def update_function_code(self, function_name, qualifier, body):
|
||||||
|
@ -47,6 +47,20 @@ class LambdaResponse(BaseResponse):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Cannot handle request")
|
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):
|
def event_source_mapping(self, request, full_url, headers):
|
||||||
self.setup_class(request, full_url, headers)
|
self.setup_class(request, full_url, headers)
|
||||||
path = request.path if hasattr(request, "path") else path_url(request.url)
|
path = request.path if hasattr(request, "path") else path_url(request.url)
|
||||||
@ -65,6 +79,13 @@ class LambdaResponse(BaseResponse):
|
|||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return self._list_layers()
|
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):
|
def layers_versions(self, request, full_url, headers):
|
||||||
self.setup_class(request, full_url, headers)
|
self.setup_class(request, full_url, headers)
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
@ -182,11 +203,8 @@ class LambdaResponse(BaseResponse):
|
|||||||
def _get_policy(self, request):
|
def _get_policy(self, request):
|
||||||
path = request.path if hasattr(request, "path") else path_url(request.url)
|
path = request.path if hasattr(request, "path") else path_url(request.url)
|
||||||
function_name = unquote(path.split("/")[-2])
|
function_name = unquote(path.split("/")[-2])
|
||||||
if self.lambda_backend.get_function(function_name):
|
out = self.lambda_backend.get_policy(function_name)
|
||||||
out = self.lambda_backend.get_policy_wire_format(function_name)
|
return 200, {}, out
|
||||||
return 200, {}, out
|
|
||||||
else:
|
|
||||||
return 404, {}, "{}"
|
|
||||||
|
|
||||||
def _del_policy(self, request, querystring):
|
def _del_policy(self, request, querystring):
|
||||||
path = request.path if hasattr(request, "path") else path_url(request.url)
|
path = request.path if hasattr(request, "path") else path_url(request.url)
|
||||||
@ -270,7 +288,7 @@ class LambdaResponse(BaseResponse):
|
|||||||
|
|
||||||
def _create_function(self):
|
def _create_function(self):
|
||||||
fn = self.lambda_backend.create_function(self.json_body)
|
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)
|
return 201, {}, json.dumps(config)
|
||||||
|
|
||||||
def _create_event_source_mapping(self):
|
def _create_event_source_mapping(self):
|
||||||
@ -466,6 +484,20 @@ class LambdaResponse(BaseResponse):
|
|||||||
layers = self.lambda_backend.list_layers()
|
layers = self.lambda_backend.list_layers()
|
||||||
return 200, {}, json.dumps({"Layers": 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):
|
def _get_layer_versions(self):
|
||||||
layer_name = self.path.rsplit("/", 2)[-2]
|
layer_name = self.path.rsplit("/", 2)[-2]
|
||||||
layer_versions = self.lambda_backend.get_layer_versions(layer_name)
|
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)
|
layer_version = self.lambda_backend.publish_layer_version(spec)
|
||||||
config = layer_version.get_layer_version()
|
config = layer_version.get_layer_version()
|
||||||
return 201, {}, json.dumps(config)
|
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 = {
|
url_paths = {
|
||||||
r"{0}/(?P<api_version>[^/]+)/functions/?$": response.root,
|
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_:%-]+)/?$": 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>[^/]+)/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/?$": response.event_source_mappings,
|
||||||
r"{0}/(?P<api_version>[^/]+)/event-source-mappings/(?P<UUID>[\w_-]+)/?$": response.event_source_mapping,
|
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>[^/]+)/functions/(?P<function_name>[\w_:%-]+)/concurrency/?$": response.function_concurrency,
|
||||||
r"{0}/(?P<api_version>[^/]+)/layers/?$": response.list_layers,
|
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/?$": 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
|
TestAccAWSKinesisStream
|
||||||
TestAccAWSKmsAlias
|
TestAccAWSKmsAlias
|
||||||
TestAccAWSKmsSecretDataSource
|
TestAccAWSKmsSecretDataSource
|
||||||
|
TestAccAWSLambdaAlias
|
||||||
|
TestAccAWSLambdaLayerVersion
|
||||||
TestAccAWSMq
|
TestAccAWSMq
|
||||||
TestAccAWSNatGateway
|
TestAccAWSNatGateway
|
||||||
TestAccAWSPartition
|
TestAccAWSPartition
|
||||||
@ -133,5 +135,7 @@ TestAccDataSourceAWSEFSAccessPoint
|
|||||||
TestAccDataSourceAWSEFSAccessPoints
|
TestAccDataSourceAWSEFSAccessPoints
|
||||||
TestAccDataSourceAwsEfsFileSystem
|
TestAccDataSourceAwsEfsFileSystem
|
||||||
TestAccDataSourceAwsEfsMountTarget
|
TestAccDataSourceAwsEfsMountTarget
|
||||||
|
TestAccDataSourceAWSLambdaLayerVersion
|
||||||
|
TestAccDataSourceAwsLambdaInvocation
|
||||||
TestAccDataSourceAwsNetworkInterface_
|
TestAccDataSourceAwsNetworkInterface_
|
||||||
TestValidateSSMDocumentPermissions
|
TestValidateSSMDocumentPermissions
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import base64
|
||||||
import botocore.client
|
import botocore.client
|
||||||
import boto3
|
import boto3
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
import sure # noqa # pylint: disable=unused-import
|
import sure # noqa # pylint: disable=unused-import
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -122,37 +122,19 @@ def test_create_function_from_aws_bucket():
|
|||||||
Publish=True,
|
Publish=True,
|
||||||
VpcConfig={"SecurityGroupIds": ["sg-123abc"], "SubnetIds": ["subnet-123abc"]},
|
VpcConfig={"SecurityGroupIds": ["sg-123abc"], "SubnetIds": ["subnet-123abc"]},
|
||||||
)
|
)
|
||||||
# this is hard to match against, so remove it
|
|
||||||
result["ResponseMetadata"].pop("HTTPHeaders", None)
|
result.should.have.key("FunctionName").equals(function_name)
|
||||||
# Botocore inserts retry attempts not seen in Python27
|
result.should.have.key("FunctionArn").equals(
|
||||||
result["ResponseMetadata"].pop("RetryAttempts", None)
|
"arn:aws:lambda:{}:{}:function:{}".format(
|
||||||
result.pop("LastModified")
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
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("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
|
@mock_lambda
|
||||||
@ -191,16 +173,46 @@ def test_create_function_from_zipfile():
|
|||||||
"Description": "test lambda function",
|
"Description": "test lambda function",
|
||||||
"Timeout": 3,
|
"Timeout": 3,
|
||||||
"MemorySize": 128,
|
"MemorySize": 128,
|
||||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
"CodeSha256": base64.b64encode(hashlib.sha256(zip_content).digest()).decode(
|
||||||
|
"utf-8"
|
||||||
|
),
|
||||||
"Version": "1",
|
"Version": "1",
|
||||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||||
"ResponseMetadata": {"HTTPStatusCode": 201},
|
"ResponseMetadata": {"HTTPStatusCode": 201},
|
||||||
"State": "Active",
|
"State": "Active",
|
||||||
"Layers": [],
|
"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_lambda
|
||||||
@mock_s3
|
@mock_s3
|
||||||
@freeze_time("2015-01-01 00:00:00")
|
@freeze_time("2015-01-01 00:00:00")
|
||||||
@ -243,7 +255,7 @@ def test_get_function():
|
|||||||
result["Code"]["RepositoryType"].should.equal("S3")
|
result["Code"]["RepositoryType"].should.equal("S3")
|
||||||
|
|
||||||
result["Configuration"]["CodeSha256"].should.equal(
|
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"]["CodeSize"].should.equal(len(zip_content))
|
||||||
result["Configuration"]["Description"].should.equal("test lambda function")
|
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 = 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["CodeSize"].should.equal(len(zip_content))
|
||||||
result["Description"].should.equal("test lambda function")
|
result["Description"].should.equal("test lambda function")
|
||||||
result.should.contain("FunctionArn")
|
result.should.contain("FunctionArn")
|
||||||
@ -600,7 +614,9 @@ def test_list_create_list_get_delete_list():
|
|||||||
"RepositoryType": "S3",
|
"RepositoryType": "S3",
|
||||||
},
|
},
|
||||||
"Configuration": {
|
"Configuration": {
|
||||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
"CodeSha256": base64.b64encode(hashlib.sha256(zip_content).digest()).decode(
|
||||||
|
"utf-8"
|
||||||
|
),
|
||||||
"CodeSize": len(zip_content),
|
"CodeSize": len(zip_content),
|
||||||
"Description": "test lambda function",
|
"Description": "test lambda function",
|
||||||
"FunctionName": function_name,
|
"FunctionName": function_name,
|
||||||
@ -613,6 +629,8 @@ def test_list_create_list_get_delete_list():
|
|||||||
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
"VpcConfig": {"SecurityGroupIds": [], "SubnetIds": []},
|
||||||
"State": "Active",
|
"State": "Active",
|
||||||
"Layers": [],
|
"Layers": [],
|
||||||
|
"LastUpdateStatus": "Successful",
|
||||||
|
"TracingConfig": {"Mode": "PassThrough"},
|
||||||
},
|
},
|
||||||
"ResponseMetadata": {"HTTPStatusCode": 200},
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
}
|
}
|
||||||
@ -673,90 +691,6 @@ def test_list_create_list_get_delete_list():
|
|||||||
func_names.shouldnt.contain(function_name)
|
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
|
@mock_lambda
|
||||||
@freeze_time("2015-01-01 00:00:00")
|
@freeze_time("2015-01-01 00:00:00")
|
||||||
def test_get_function_created_with_zipfile():
|
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 = conn.get_function(FunctionName=function_name)
|
||||||
response["Configuration"].pop("LastModified")
|
|
||||||
|
|
||||||
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||||
assert len(response["Code"]) == 2
|
assert len(response["Code"]) == 2
|
||||||
@ -784,100 +717,26 @@ def test_get_function_created_with_zipfile():
|
|||||||
assert response["Code"]["Location"].startswith(
|
assert response["Code"]["Location"].startswith(
|
||||||
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
||||||
)
|
)
|
||||||
response["Configuration"].should.equal(
|
response.should.have.key("Configuration")
|
||||||
{
|
config = response["Configuration"]
|
||||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
config.should.have.key("CodeSha256").equals(
|
||||||
"CodeSize": len(zip_content),
|
base64.b64encode(hashlib.sha256(zip_content).digest()).decode("utf-8")
|
||||||
"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": [],
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
config.should.have.key("CodeSize").equals(len(zip_content))
|
||||||
|
config.should.have.key("Description").equals("test lambda function")
|
||||||
@pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
|
config.should.have.key("FunctionArn").equals(
|
||||||
@mock_lambda
|
f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}"
|
||||||
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]
|
config.should.have.key("FunctionName").equals(function_name)
|
||||||
|
config.should.have.key("Handler").equals("lambda_function.handler")
|
||||||
response = conn.add_permission(
|
config.should.have.key("MemorySize").equals(128)
|
||||||
FunctionName=name_or_arn,
|
config.should.have.key("Role").equals(get_role_name())
|
||||||
StatementId="1",
|
config.should.have.key("Runtime").equals("python2.7")
|
||||||
Action="lambda:InvokeFunction",
|
config.should.have.key("Timeout").equals(3)
|
||||||
Principal="432143214321",
|
config.should.have.key("Version").equals("$LATEST")
|
||||||
SourceArn="arn:aws:lambda:us-west-2:account-id:function:helloworld",
|
config.should.have.key("State").equals("Active")
|
||||||
SourceAccount="123412341234",
|
config.should.have.key("Layers").equals([])
|
||||||
EventSourceToken="blah",
|
config.should.have.key("LastUpdateStatus").equals("Successful")
|
||||||
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"
|
|
||||||
|
|
||||||
|
|
||||||
@mock_lambda
|
@mock_lambda
|
||||||
@ -1085,7 +944,6 @@ def test_update_function_zip(key):
|
|||||||
)
|
)
|
||||||
|
|
||||||
response = conn.get_function(FunctionName=function_name, Qualifier="2")
|
response = conn.get_function(FunctionName=function_name, Qualifier="2")
|
||||||
response["Configuration"].pop("LastModified")
|
|
||||||
|
|
||||||
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||||
assert len(response["Code"]) == 2
|
assert len(response["Code"]) == 2
|
||||||
@ -1093,26 +951,16 @@ def test_update_function_zip(key):
|
|||||||
assert response["Code"]["Location"].startswith(
|
assert response["Code"]["Location"].startswith(
|
||||||
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
||||||
)
|
)
|
||||||
response["Configuration"].should.equal(
|
|
||||||
{
|
config = response["Configuration"]
|
||||||
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
|
config.should.have.key("CodeSize").equals(len(zip_content_two))
|
||||||
"CodeSize": len(zip_content_two),
|
config.should.have.key("Description").equals("test lambda function")
|
||||||
"Description": "test lambda function",
|
config.should.have.key("FunctionArn").equals(
|
||||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:{}:2".format(
|
f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}:2"
|
||||||
_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.should.have.key("FunctionName").equals(function_name)
|
||||||
|
config.should.have.key("Version").equals("2")
|
||||||
|
config.should.have.key("LastUpdateStatus").equals("Successful")
|
||||||
|
|
||||||
|
|
||||||
@mock_lambda
|
@mock_lambda
|
||||||
@ -1131,7 +979,7 @@ def test_update_function_s3():
|
|||||||
conn = boto3.client("lambda", _lambda_region)
|
conn = boto3.client("lambda", _lambda_region)
|
||||||
function_name = str(uuid4())[0:6]
|
function_name = str(uuid4())[0:6]
|
||||||
|
|
||||||
fxn = conn.create_function(
|
conn.create_function(
|
||||||
FunctionName=function_name,
|
FunctionName=function_name,
|
||||||
Runtime="python2.7",
|
Runtime="python2.7",
|
||||||
Role=get_role_name(),
|
Role=get_role_name(),
|
||||||
@ -1154,7 +1002,6 @@ def test_update_function_s3():
|
|||||||
)
|
)
|
||||||
|
|
||||||
response = conn.get_function(FunctionName=function_name, Qualifier="2")
|
response = conn.get_function(FunctionName=function_name, Qualifier="2")
|
||||||
response["Configuration"].pop("LastModified")
|
|
||||||
|
|
||||||
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
|
||||||
assert len(response["Code"]) == 2
|
assert len(response["Code"]) == 2
|
||||||
@ -1162,26 +1009,19 @@ def test_update_function_s3():
|
|||||||
assert response["Code"]["Location"].startswith(
|
assert response["Code"]["Location"].startswith(
|
||||||
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
"s3://awslambda-{0}-tasks.s3-{0}.amazonaws.com".format(_lambda_region)
|
||||||
)
|
)
|
||||||
response["Configuration"].should.equal(
|
|
||||||
{
|
config = response["Configuration"]
|
||||||
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
|
config.should.have.key("CodeSha256").equals(
|
||||||
"CodeSize": len(zip_content_two),
|
base64.b64encode(hashlib.sha256(zip_content_two).digest()).decode("utf-8")
|
||||||
"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.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
|
@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
|
@mock_lambda
|
||||||
def test_remove_unknown_permission_throws_error():
|
def test_remove_unknown_permission_throws_error():
|
||||||
conn = boto3.client("lambda", _lambda_region)
|
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 botocore.exceptions import ClientError
|
||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
from moto import mock_lambda, mock_s3
|
from moto import mock_lambda, mock_s3
|
||||||
from moto.core.exceptions import RESTError
|
|
||||||
from moto.sts.models import ACCOUNT_ID
|
from moto.sts.models import ACCOUNT_ID
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -15,6 +14,23 @@ _lambda_region = "us-west-2"
|
|||||||
boto3.setup_default_session(region_name=_lambda_region)
|
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_lambda
|
||||||
@mock_s3
|
@mock_s3
|
||||||
@freeze_time("2015-01-01 00:00:00")
|
@freeze_time("2015-01-01 00:00:00")
|
||||||
@ -31,13 +47,6 @@ def test_get_lambda_layers():
|
|||||||
conn = boto3.client("lambda", _lambda_region)
|
conn = boto3.client("lambda", _lambda_region)
|
||||||
layer_name = str(uuid4())[0:6]
|
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(
|
conn.publish_layer_version(
|
||||||
LayerName=layer_name,
|
LayerName=layer_name,
|
||||||
Content={"ZipFile": get_test_zip_file1()},
|
Content={"ZipFile": get_test_zip_file1()},
|
||||||
@ -123,3 +132,93 @@ def test_get_lambda_layers():
|
|||||||
Environment={"Variables": {"test_variable": "test_value"}},
|
Environment={"Variables": {"test_variable": "test_value"}},
|
||||||
Layers=[(expected_arn + "3")],
|
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…
x
Reference in New Issue
Block a user