Lambda: additional optional fields (#6483)
This commit is contained in:
parent
c2f000d496
commit
5f1ccb298e
@ -89,3 +89,9 @@ class UnknownPolicyException(LambdaClientError):
|
||||
"ResourceNotFoundException",
|
||||
"No policy is associated with the given resource.",
|
||||
)
|
||||
|
||||
|
||||
class ValidationException(LambdaClientError):
|
||||
def __init__(self, value: str, property_name: str, specific_message: str):
|
||||
message = f"1 validation error detected: Value '{value}' at '{property_name}' failed to satisfy constraint: {specific_message}"
|
||||
super().__init__("ValidationException", message)
|
||||
|
@ -45,6 +45,7 @@ from .exceptions import (
|
||||
UnknownLayerVersionException,
|
||||
UnknownFunctionException,
|
||||
UnknownAliasException,
|
||||
ValidationException,
|
||||
)
|
||||
from .utils import (
|
||||
make_function_arn,
|
||||
@ -196,6 +197,22 @@ def _validate_s3_bucket_and_key(
|
||||
return key
|
||||
|
||||
|
||||
class ImageConfig:
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
self.cmd = config.get("Command", [])
|
||||
self.entry_point = config.get("EntryPoint", [])
|
||||
self.working_directory = config.get("WorkingDirectory", "")
|
||||
|
||||
def response(self) -> Dict[str, Any]:
|
||||
return dict(
|
||||
{
|
||||
"Command": self.cmd,
|
||||
"EntryPoint": self.entry_point,
|
||||
"WorkingDirectory": self.working_directory,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class Permission(CloudFormationModel):
|
||||
def __init__(self, region: str):
|
||||
self.region = region
|
||||
@ -429,6 +446,10 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
self.reserved_concurrency = spec.get("ReservedConcurrentExecutions", None)
|
||||
|
||||
# optional
|
||||
self.ephemeral_storage: str
|
||||
self.code_digest: str
|
||||
self.code_bytes: bytes
|
||||
|
||||
self.description = spec.get("Description", "")
|
||||
self.memory_size = spec.get("MemorySize", 128)
|
||||
self.package_type = spec.get("PackageType", None)
|
||||
@ -439,6 +460,13 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
self.signing_job_arn = spec.get("SigningJobArn")
|
||||
self.code_signing_config_arn = spec.get("CodeSigningConfigArn")
|
||||
self.tracing_config = spec.get("TracingConfig") or {"Mode": "PassThrough"}
|
||||
self.architectures: List[str] = spec.get("Architectures", ["x86_64"])
|
||||
self.image_config: ImageConfig = ImageConfig(spec.get("ImageConfig", {}))
|
||||
_es = spec.get("EphemeralStorage")
|
||||
if _es:
|
||||
self.ephemeral_storage = _es["Size"]
|
||||
else:
|
||||
self.ephemeral_storage = 512
|
||||
|
||||
self.logs_group_name = f"/aws/lambda/{self.function_name}"
|
||||
|
||||
@ -531,6 +559,41 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
datetime.datetime.utcnow()
|
||||
)
|
||||
|
||||
@property
|
||||
def architectures(self) -> List[str]:
|
||||
return self._architectures
|
||||
|
||||
@architectures.setter
|
||||
def architectures(self, architectures: List[str]) -> None:
|
||||
if (
|
||||
len(architectures) > 1
|
||||
or not architectures
|
||||
or architectures[0] not in ("x86_64", "arm64")
|
||||
):
|
||||
raise ValidationException(
|
||||
str(architectures),
|
||||
"architectures",
|
||||
"Member must satisfy constraint: "
|
||||
"[Member must satisfy enum value set: [x86_64, arm64], Member must not be null]",
|
||||
)
|
||||
self._architectures = architectures
|
||||
|
||||
@property
|
||||
def ephemeral_storage(self) -> int:
|
||||
return self._ephemeral_storage
|
||||
|
||||
@ephemeral_storage.setter
|
||||
def ephemeral_storage(self, ephemeral_storage: int) -> None:
|
||||
if ephemeral_storage > 10240:
|
||||
raise ValidationException(
|
||||
str(ephemeral_storage),
|
||||
"ephemeralStorage.size",
|
||||
"Member must have value less than or equal to 10240",
|
||||
)
|
||||
|
||||
# ephemeral_storage < 512 is handled by botocore 1.30.0
|
||||
self._ephemeral_storage = ephemeral_storage
|
||||
|
||||
@property
|
||||
def vpc_config(self) -> Dict[str, Any]: # type: ignore[misc]
|
||||
config = self._vpc_config.copy()
|
||||
@ -582,7 +645,16 @@ class LambdaFunction(CloudFormationModel, DockerModel):
|
||||
"SigningProfileVersionArn": self.signing_profile_version_arn,
|
||||
"SigningJobArn": self.signing_job_arn,
|
||||
"TracingConfig": self.tracing_config,
|
||||
"Architectures": self.architectures,
|
||||
"EphemeralStorage": {
|
||||
"Size": self.ephemeral_storage,
|
||||
},
|
||||
"SnapStart": {"ApplyOn": "None", "OptimizationStatus": "Off"},
|
||||
}
|
||||
if self.package_type == "Image":
|
||||
config["ImageConfigResponse"] = {
|
||||
"ImageConfig": self.image_config.response(),
|
||||
}
|
||||
if not on_create:
|
||||
# Only return this variable after the first creation
|
||||
config["LastUpdateStatus"] = "Successful"
|
||||
|
@ -2,23 +2,33 @@ Documentation on how to run Terraform Tests can be found here:
|
||||
|
||||
http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html#terraform-tests
|
||||
|
||||
To get started you need to have [Go](https://go.dev/doc/install) installed.
|
||||
|
||||
One time setup:
|
||||
|
||||
```bash
|
||||
go mod init moto
|
||||
```
|
||||
|
||||
To see a list of available tests:
|
||||
|
||||
```
|
||||
```bash
|
||||
cd tests/terraformtests/terraform-provider-aws
|
||||
git submodule init
|
||||
git submodule update
|
||||
go test ./internal/service/elb/ -v -list TestAcc
|
||||
```
|
||||
|
||||
To run a specific test:
|
||||
|
||||
```
|
||||
```bash
|
||||
moto_server -p 4566
|
||||
make terraformtests SERVICE_NAME=elb TEST_NAMES=NewTestName
|
||||
```
|
||||
|
||||
To see the list of tests that currently pass:
|
||||
|
||||
```
|
||||
```bash
|
||||
python tests/terraformtests/get_tf_services.py
|
||||
python tests/terraformtests/get_tf_tests.py ec2
|
||||
```
|
||||
|
@ -6,7 +6,7 @@ import boto3
|
||||
import hashlib
|
||||
import pytest
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from botocore.exceptions import ClientError, ParamValidationError
|
||||
from freezegun import freeze_time
|
||||
from tests.test_ecr.test_ecr_helpers import _create_image_manifest
|
||||
from moto import mock_lambda, mock_s3, mock_ecr, settings
|
||||
@ -172,6 +172,8 @@ def test_create_function_from_zipfile():
|
||||
result.pop("LastModified")
|
||||
|
||||
assert result == {
|
||||
"Architectures": ["x86_64"],
|
||||
"EphemeralStorage": {"Size": 512},
|
||||
"FunctionName": function_name,
|
||||
"FunctionArn": f"arn:aws:lambda:{_lambda_region}:{ACCOUNT_ID}:function:{function_name}",
|
||||
"Runtime": "python2.7",
|
||||
@ -190,9 +192,122 @@ def test_create_function_from_zipfile():
|
||||
"State": "Active",
|
||||
"Layers": [],
|
||||
"TracingConfig": {"Mode": "PassThrough"},
|
||||
"SnapStart": {"ApplyOn": "None", "OptimizationStatus": "Off"},
|
||||
}
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_function_from_image():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
|
||||
image_config = {
|
||||
"EntryPoint": [
|
||||
"python",
|
||||
],
|
||||
"Command": [
|
||||
"/opt/app.py",
|
||||
],
|
||||
"WorkingDirectory": "/opt",
|
||||
}
|
||||
conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Role=get_role_name(),
|
||||
Code={"ImageUri": image_uri},
|
||||
Description="test lambda function",
|
||||
ImageConfig=image_config,
|
||||
PackageType="Image",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
|
||||
result = conn.get_function(FunctionName=function_name)
|
||||
|
||||
assert "ImageConfigResponse" in result["Configuration"]
|
||||
assert result["Configuration"]["ImageConfigResponse"]["ImageConfig"] == image_config
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_function_error_bad_architecture():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
conn.create_function(
|
||||
Architectures=["foo"],
|
||||
FunctionName=function_name,
|
||||
Role=get_role_name(),
|
||||
Code={"ImageUri": image_uri},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
)
|
||||
|
||||
err = exc.value.response
|
||||
|
||||
assert err["Error"]["Code"] == "ValidationException"
|
||||
assert (
|
||||
err["Error"]["Message"]
|
||||
== "1 validation error detected: Value '['foo']' at 'architectures' failed to satisfy"
|
||||
" constraint: Member must satisfy constraint: [Member must satisfy enum value set: "
|
||||
"[x86_64, arm64], Member must not be null]"
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_function_error_ephemeral_too_big():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
|
||||
|
||||
with pytest.raises(ClientError) as exc:
|
||||
conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Role=get_role_name(),
|
||||
Code={"ImageUri": image_uri},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
EphemeralStorage={"Size": 3000000},
|
||||
)
|
||||
|
||||
err = exc.value.response
|
||||
|
||||
assert err["Error"]["Code"] == "ValidationException"
|
||||
assert (
|
||||
err["Error"]["Message"]
|
||||
== "1 validation error detected: Value '3000000' at 'ephemeralStorage.size' "
|
||||
"failed to satisfy constraint: "
|
||||
"Member must have value less than or equal to 10240"
|
||||
)
|
||||
|
||||
|
||||
@mock_lambda
|
||||
def test_create_function_error_ephemeral_too_small():
|
||||
conn = boto3.client("lambda", _lambda_region)
|
||||
function_name = str(uuid4())[0:6]
|
||||
image_uri = f"{ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/testlambdaecr:prod"
|
||||
|
||||
with pytest.raises(ParamValidationError) as exc:
|
||||
conn.create_function(
|
||||
FunctionName=function_name,
|
||||
Role=get_role_name(),
|
||||
Code={"ImageUri": image_uri},
|
||||
Description="test lambda function",
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
EphemeralStorage={"Size": 200},
|
||||
)
|
||||
|
||||
# this one is handled by botocore, not moto
|
||||
assert exc.typename == "ParamValidationError"
|
||||
|
||||
|
||||
@mock_lambda
|
||||
@pytest.mark.parametrize(
|
||||
"tracing_mode",
|
||||
@ -762,6 +877,7 @@ def test_list_create_list_get_delete_list():
|
||||
Handler="lambda_function.lambda_handler",
|
||||
Code={"S3Bucket": bucket_name, "S3Key": "test.zip"},
|
||||
Description="test lambda function",
|
||||
EphemeralStorage={"Size": 2500},
|
||||
Timeout=3,
|
||||
MemorySize=128,
|
||||
Publish=True,
|
||||
@ -789,6 +905,9 @@ def test_list_create_list_get_delete_list():
|
||||
"Layers": [],
|
||||
"LastUpdateStatus": "Successful",
|
||||
"TracingConfig": {"Mode": "PassThrough"},
|
||||
"Architectures": ["x86_64"],
|
||||
"EphemeralStorage": {"Size": 2500},
|
||||
"SnapStart": {"ApplyOn": "None", "OptimizationStatus": "Off"},
|
||||
},
|
||||
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user