More accurately mock ECS RegisterTaskDefinition (#3584)
The mocked response for ECS RegisterTaskDefinition has drifted from what actually returns when run against a real ECS endpoint. I created a minimal task definition for both EC2: ``` >>> ecs.register_task_definition( family="moto", containerDefinitions=[ { "name": "hello_world", "image": "hello-world:latest", "memory": 400 } ] )["taskDefinition"] {'taskDefinitionArn': 'arn:aws:ecs:us-east-1:************:task-definition/moto:1', 'containerDefinitions': [{'name': 'hello_world', 'image': 'hello-world:latest', 'cpu': 0, 'memory': 400, 'portMappings': [], 'essential': True, 'environment': [], 'mountPoints': [], 'volumesFrom': []}], 'family': 'moto', 'revision': 1, 'volumes': [], 'status': 'ACTIVE', 'placementConstraints': [], 'compatibilities': ['EC2']} ``` and FARGATE: ``` >>> ecs.register_task_definition( family="moto", containerDefinitions=[ { "name": "hello_world", "image": "hello-world:latest", "memory": 400 } ], requiresCompatibilities=["FARGATE"], networkMode="awsvpc", cpu="256", memory="512" )["taskDefinition"] {'taskDefinitionArn': 'arn:aws:ecs:us-east-1:************:task-definition/moto:2', 'containerDefinitions': [{'name': 'hello_world', 'image': 'hello-world:latest', 'cpu': 0, 'memory': 400, 'portMappings': [], 'essential': True, 'environment': [], 'mountPoints': [], 'volumesFrom': []}], 'family': 'moto', 'networkMode': 'awsvpc', 'revision': 2, 'volumes': [], 'status': 'ACTIVE', 'requiresAttributes': [{'name': 'com.amazonaws.ecs.capability.docker-remote-api.1.18'}, {'name': 'ecs.capability.task-eni'}], 'placementConstraints': [], 'compatibilities': ['EC2', 'FARGATE'], 'requiresCompatibilities': ['FARGATE'], 'cpu': '256', 'memory': '512'} ``` This change adds several default keys to the task based on those two real responses and the AWS documentation: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RegisterTaskDefinition.html The mock still doesn't match the real response exactly but it's much closer than it was before.
This commit is contained in:
parent
d2b751e22d
commit
6dfd64ff3c
@ -126,32 +126,72 @@ class TaskDefinition(BaseObject, CloudFormationModel):
|
|||||||
volumes=None,
|
volumes=None,
|
||||||
tags=None,
|
tags=None,
|
||||||
placement_constraints=None,
|
placement_constraints=None,
|
||||||
|
requires_compatibilities=None,
|
||||||
|
cpu=None,
|
||||||
|
memory=None,
|
||||||
):
|
):
|
||||||
self.family = family
|
self.family = family
|
||||||
self.revision = revision
|
self.revision = revision
|
||||||
self.arn = "arn:aws:ecs:{0}:012345678910:task-definition/{1}:{2}".format(
|
self.arn = "arn:aws:ecs:{0}:012345678910:task-definition/{1}:{2}".format(
|
||||||
region_name, family, revision
|
region_name, family, revision
|
||||||
)
|
)
|
||||||
self.container_definitions = container_definitions
|
|
||||||
|
default_container_definition = {
|
||||||
|
"cpu": 0,
|
||||||
|
"portMappings": [],
|
||||||
|
"essential": True,
|
||||||
|
"environment": [],
|
||||||
|
"mountPoints": [],
|
||||||
|
"volumesFrom": [],
|
||||||
|
}
|
||||||
|
self.container_definitions = []
|
||||||
|
for container_definition in container_definitions:
|
||||||
|
full_definition = default_container_definition.copy()
|
||||||
|
full_definition.update(container_definition)
|
||||||
|
self.container_definitions.append(full_definition)
|
||||||
|
|
||||||
self.tags = tags if tags is not None else []
|
self.tags = tags if tags is not None else []
|
||||||
|
|
||||||
if volumes is None:
|
if volumes is None:
|
||||||
self.volumes = []
|
self.volumes = []
|
||||||
else:
|
else:
|
||||||
self.volumes = volumes
|
self.volumes = volumes
|
||||||
if network_mode is None:
|
|
||||||
|
if not requires_compatibilities or requires_compatibilities == ["EC2"]:
|
||||||
|
self.compatibilities = ["EC2"]
|
||||||
|
else:
|
||||||
|
self.compatibilities = ["EC2", "FARGATE"]
|
||||||
|
|
||||||
|
if network_mode is None and "FARGATE" not in self.compatibilities:
|
||||||
self.network_mode = "bridge"
|
self.network_mode = "bridge"
|
||||||
|
elif "FARGATE" in self.compatibilities:
|
||||||
|
self.network_mode = "awsvpc"
|
||||||
else:
|
else:
|
||||||
self.network_mode = network_mode
|
self.network_mode = network_mode
|
||||||
|
|
||||||
self.placement_constraints = (
|
self.placement_constraints = (
|
||||||
placement_constraints if placement_constraints is not None else []
|
placement_constraints if placement_constraints is not None else []
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.requires_compatibilities = requires_compatibilities
|
||||||
|
|
||||||
|
self.cpu = cpu
|
||||||
|
self.memory = memory
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def response_object(self):
|
def response_object(self):
|
||||||
response_object = self.gen_response_object()
|
response_object = self.gen_response_object()
|
||||||
response_object["taskDefinitionArn"] = response_object["arn"]
|
response_object["taskDefinitionArn"] = response_object["arn"]
|
||||||
del response_object["arn"]
|
del response_object["arn"]
|
||||||
del response_object["tags"]
|
del response_object["tags"]
|
||||||
|
|
||||||
|
if not response_object["requiresCompatibilities"]:
|
||||||
|
del response_object["requiresCompatibilities"]
|
||||||
|
if not response_object["cpu"]:
|
||||||
|
del response_object["cpu"]
|
||||||
|
if not response_object["memory"]:
|
||||||
|
del response_object["memory"]
|
||||||
|
|
||||||
return response_object
|
return response_object
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -683,6 +723,9 @@ class EC2ContainerServiceBackend(BaseBackend):
|
|||||||
network_mode=None,
|
network_mode=None,
|
||||||
tags=None,
|
tags=None,
|
||||||
placement_constraints=None,
|
placement_constraints=None,
|
||||||
|
requires_compatibilities=None,
|
||||||
|
cpu=None,
|
||||||
|
memory=None,
|
||||||
):
|
):
|
||||||
if family in self.task_definitions:
|
if family in self.task_definitions:
|
||||||
last_id = self._get_last_task_definition_revision_id(family)
|
last_id = self._get_last_task_definition_revision_id(family)
|
||||||
@ -699,6 +742,9 @@ class EC2ContainerServiceBackend(BaseBackend):
|
|||||||
network_mode=network_mode,
|
network_mode=network_mode,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
placement_constraints=placement_constraints,
|
placement_constraints=placement_constraints,
|
||||||
|
requires_compatibilities=requires_compatibilities,
|
||||||
|
cpu=cpu,
|
||||||
|
memory=memory,
|
||||||
)
|
)
|
||||||
self.task_definitions[family][revision] = task_definition
|
self.task_definitions[family][revision] = task_definition
|
||||||
|
|
||||||
|
@ -64,6 +64,9 @@ class EC2ContainerServiceResponse(BaseResponse):
|
|||||||
tags = self._get_param("tags")
|
tags = self._get_param("tags")
|
||||||
network_mode = self._get_param("networkMode")
|
network_mode = self._get_param("networkMode")
|
||||||
placement_constraints = self._get_param("placementConstraints")
|
placement_constraints = self._get_param("placementConstraints")
|
||||||
|
requires_compatibilities = self._get_param("requiresCompatibilities")
|
||||||
|
cpu = self._get_param("cpu")
|
||||||
|
memory = self._get_param("memory")
|
||||||
task_definition = self.ecs_backend.register_task_definition(
|
task_definition = self.ecs_backend.register_task_definition(
|
||||||
family,
|
family,
|
||||||
container_definitions,
|
container_definitions,
|
||||||
@ -71,6 +74,9 @@ class EC2ContainerServiceResponse(BaseResponse):
|
|||||||
network_mode=network_mode,
|
network_mode=network_mode,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
placement_constraints=placement_constraints,
|
placement_constraints=placement_constraints,
|
||||||
|
requires_compatibilities=requires_compatibilities,
|
||||||
|
cpu=cpu,
|
||||||
|
memory=memory,
|
||||||
)
|
)
|
||||||
return json.dumps({"taskDefinition": task_definition.response_object})
|
return json.dumps({"taskDefinition": task_definition.response_object})
|
||||||
|
|
||||||
|
@ -91,53 +91,113 @@ def test_delete_cluster_exceptions():
|
|||||||
@mock_ecs
|
@mock_ecs
|
||||||
def test_register_task_definition():
|
def test_register_task_definition():
|
||||||
client = boto3.client("ecs", region_name="us-east-1")
|
client = boto3.client("ecs", region_name="us-east-1")
|
||||||
response = client.register_task_definition(
|
# Registering with minimal definition
|
||||||
|
definition = dict(
|
||||||
family="test_ecs_task",
|
family="test_ecs_task",
|
||||||
containerDefinitions=[
|
containerDefinitions=[
|
||||||
{
|
{"name": "hello_world", "image": "hello-world:latest", "memory": 400,}
|
||||||
"name": "hello_world",
|
|
||||||
"image": "docker/hello-world:latest",
|
|
||||||
"cpu": 1024,
|
|
||||||
"memory": 400,
|
|
||||||
"essential": True,
|
|
||||||
"environment": [
|
|
||||||
{"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
|
|
||||||
],
|
|
||||||
"logConfiguration": {"logDriver": "json-file"},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
networkMode="bridge",
|
|
||||||
tags=[
|
|
||||||
{"key": "createdBy", "value": "moto-unittest"},
|
|
||||||
{"key": "foo", "value": "bar"},
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
type(response["taskDefinition"]).should.be(dict)
|
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
|
||||||
|
response["taskDefinition"] = response["taskDefinition"]
|
||||||
|
response["taskDefinition"]["family"].should.equal("test_ecs_task")
|
||||||
response["taskDefinition"]["revision"].should.equal(1)
|
response["taskDefinition"]["revision"].should.equal(1)
|
||||||
response["taskDefinition"]["taskDefinitionArn"].should.equal(
|
response["taskDefinition"]["taskDefinitionArn"].should.equal(
|
||||||
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
|
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
|
||||||
)
|
)
|
||||||
|
response["taskDefinition"]["networkMode"].should.equal("bridge")
|
||||||
|
response["taskDefinition"]["volumes"].should.equal([])
|
||||||
|
response["taskDefinition"]["placementConstraints"].should.equal([])
|
||||||
|
response["taskDefinition"]["compatibilities"].should.equal(["EC2"])
|
||||||
|
response["taskDefinition"].shouldnt.have.key("requiresCompatibilities")
|
||||||
|
response["taskDefinition"].shouldnt.have.key("cpu")
|
||||||
|
response["taskDefinition"].shouldnt.have.key("memory")
|
||||||
|
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["name"].should.equal(
|
response["taskDefinition"]["containerDefinitions"][0]["name"].should.equal(
|
||||||
"hello_world"
|
"hello_world"
|
||||||
)
|
)
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["image"].should.equal(
|
response["taskDefinition"]["containerDefinitions"][0]["image"].should.equal(
|
||||||
"docker/hello-world:latest"
|
"hello-world:latest"
|
||||||
|
)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["cpu"].should.equal(0)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["portMappings"].should.equal(
|
||||||
|
[]
|
||||||
)
|
)
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["cpu"].should.equal(1024)
|
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["memory"].should.equal(400)
|
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["essential"].should.equal(
|
response["taskDefinition"]["containerDefinitions"][0]["essential"].should.equal(
|
||||||
True
|
True
|
||||||
)
|
)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["environment"].should.equal(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["mountPoints"].should.equal(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["volumesFrom"].should.equal(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Registering again increments the revision
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
|
||||||
|
response["taskDefinition"]["revision"].should.equal(2)
|
||||||
|
response["taskDefinition"]["taskDefinitionArn"].should.equal(
|
||||||
|
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:2"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Registering with optional top-level params
|
||||||
|
definition["requiresCompatibilities"] = ["FARGATE"]
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
response["taskDefinition"]["requiresCompatibilities"].should.equal(["FARGATE"])
|
||||||
|
response["taskDefinition"]["compatibilities"].should.equal(["EC2", "FARGATE"])
|
||||||
|
response["taskDefinition"]["networkMode"].should.equal("awsvpc")
|
||||||
|
|
||||||
|
definition["requiresCompatibilities"] = ["EC2", "FARGATE"]
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
response["taskDefinition"]["requiresCompatibilities"].should.equal(
|
||||||
|
["EC2", "FARGATE"]
|
||||||
|
)
|
||||||
|
response["taskDefinition"]["compatibilities"].should.equal(["EC2", "FARGATE"])
|
||||||
|
response["taskDefinition"]["networkMode"].should.equal("awsvpc")
|
||||||
|
|
||||||
|
definition["cpu"] = "512"
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
response["taskDefinition"]["cpu"].should.equal("512")
|
||||||
|
|
||||||
|
definition.update({"memory": "512"})
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
response["taskDefinition"]["memory"].should.equal("512")
|
||||||
|
|
||||||
|
# Registering with optional container params
|
||||||
|
definition["containerDefinitions"][0]["cpu"] = 512
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["cpu"].should.equal(512)
|
||||||
|
|
||||||
|
definition["containerDefinitions"][0]["essential"] = False
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
|
response["taskDefinition"]["containerDefinitions"][0]["essential"].should.equal(
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
definition["containerDefinitions"][0]["environment"] = [
|
||||||
|
{"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
|
||||||
|
]
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
|
response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
|
||||||
"name"
|
"name"
|
||||||
].should.equal("AWS_ACCESS_KEY_ID")
|
].should.equal("AWS_ACCESS_KEY_ID")
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
|
response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
|
||||||
"value"
|
"value"
|
||||||
].should.equal("SOME_ACCESS_KEY")
|
].should.equal("SOME_ACCESS_KEY")
|
||||||
|
|
||||||
|
definition["containerDefinitions"][0]["logConfiguration"] = {
|
||||||
|
"logDriver": "json-file"
|
||||||
|
}
|
||||||
|
response = client.register_task_definition(**definition)
|
||||||
response["taskDefinition"]["containerDefinitions"][0]["logConfiguration"][
|
response["taskDefinition"]["containerDefinitions"][0]["logConfiguration"][
|
||||||
"logDriver"
|
"logDriver"
|
||||||
].should.equal("json-file")
|
].should.equal("json-file")
|
||||||
response["taskDefinition"]["networkMode"].should.equal("bridge")
|
|
||||||
|
|
||||||
|
|
||||||
@mock_ecs
|
@mock_ecs
|
||||||
|
@ -269,6 +269,5 @@ def test_create_task_definition_through_cloudformation():
|
|||||||
template["Resources"]["testTaskDefinition"]["Properties"], pascal_to_camelcase
|
template["Resources"]["testTaskDefinition"]["Properties"], pascal_to_camelcase
|
||||||
)
|
)
|
||||||
task_definition["volumes"].should.equal(expected_properties["volumes"])
|
task_definition["volumes"].should.equal(expected_properties["volumes"])
|
||||||
task_definition["containerDefinitions"].should.equal(
|
for key, value in expected_properties["containerDefinitions"][0].items():
|
||||||
expected_properties["containerDefinitions"]
|
task_definition["containerDefinitions"][0][key].should.equal(value)
|
||||||
)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user