ECS: add eni to a task (#5520)

This commit is contained in:
rafcio19 2022-10-03 22:31:18 +01:00 committed by GitHub
parent c5586a1724
commit 29829e2eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 164 additions and 0 deletions

View File

@ -13,6 +13,8 @@ from moto.core.utils import (
remap_nested_keys,
BackendDict,
)
from ..ec2.utils import random_private_ip
from moto.ec2 import ec2_backends
from moto.moto_api._internal import mock_random
from moto.utilities.tagging_service import TaggingService
@ -300,6 +302,7 @@ class Task(BaseObject):
overrides=None,
started_by="",
tags=None,
networking_configuration=None,
):
self.id = str(mock_random.uuid4())
self.cluster_name = cluster.name
@ -318,6 +321,42 @@ class Task(BaseObject):
self.region_name = cluster.region_name
self._account_id = backend.account_id
self._backend = backend
self.attachments = []
if task_definition.network_mode == "awsvpc":
if not networking_configuration:
raise InvalidParameterException(
"Network Configuration must be provided when networkMode 'awsvpc' is specified."
)
self.network_configuration = networking_configuration
net_conf = networking_configuration["awsvpcConfiguration"]
ec2_backend = ec2_backends[self._account_id][self.region_name]
eni = ec2_backend.create_network_interface(
subnet=net_conf["subnets"][0],
private_ip_address=random_private_ip(),
group_ids=net_conf["securityGroups"],
description="moto ECS",
)
eni.status = "in-use"
eni.device_index = 0
self.attachments.append(
{
"id": str(mock_random.uuid4()),
"type": "ElasticNetworkInterface",
"status": "ATTACHED",
"details": [
{"name": "subnetId", "value": net_conf["subnets"][0]},
{"name": "networkInterfaceId", "value": eni.id},
{"name": "macAddress", "value": eni.mac_address},
{"name": "privateDnsName", "value": eni.private_dns_name},
{"name": "privateIPv4Address", "value": eni.private_ip_address},
],
}
)
@property
def task_arn(self):
@ -975,6 +1014,7 @@ class EC2ContainerServiceBackend(BaseBackend):
started_by,
tags,
launch_type,
networking_configuration=None,
):
cluster = self._get_cluster(cluster_str)
@ -1018,6 +1058,7 @@ class EC2ContainerServiceBackend(BaseBackend):
started_by=started_by or "",
tags=tags or [],
launch_type=launch_type or "",
networking_configuration=networking_configuration,
)
self.update_container_instance_resources(
container_instance, resource_requirements

View File

@ -145,6 +145,7 @@ class EC2ContainerServiceResponse(BaseResponse):
started_by = self._get_param("startedBy")
tags = self._get_param("tags")
launch_type = self._get_param("launchType")
network_configuration = self._get_param("networkConfiguration")
tasks = self.ecs_backend.run_task(
cluster_str,
task_definition_str,
@ -153,6 +154,7 @@ class EC2ContainerServiceResponse(BaseResponse):
started_by,
tags,
launch_type,
network_configuration,
)
return json.dumps(
{"tasks": [task.response_object for task in tasks], "failures": []}

View File

@ -1705,6 +1705,88 @@ def test_run_task():
response["tasks"][0]["tags"][0].get("value").should.equal("tagValue0")
@mock_ec2
@mock_ecs
def test_run_task_awsvpc_network():
# Setup
client = boto3.client("ecs", region_name="us-east-1")
ec2_client = boto3.client("ec2", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
# ECS setup
setup_resources = setup_ecs(client, ec2)
# Execute
response = client.run_task(
cluster="test_ecs_cluster",
overrides={},
taskDefinition="test_ecs_task",
startedBy="moto",
launchType="FARGATE",
networkConfiguration={
"awsvpcConfiguration": {
"subnets": [setup_resources[0].id],
"securityGroups": [setup_resources[1].id],
}
},
)
# Verify
len(response["tasks"]).should.equal(1)
response["tasks"][0]["lastStatus"].should.equal("RUNNING")
response["tasks"][0]["desiredStatus"].should.equal("RUNNING")
response["tasks"][0]["startedBy"].should.equal("moto")
response["tasks"][0]["stoppedReason"].should.equal("")
eni = ec2_client.describe_network_interfaces(
Filters=[{"Name": "description", "Values": ["moto ECS"]}]
)["NetworkInterfaces"][0]
try:
# should be UUID
UUID(response["tasks"][0]["attachments"][0]["id"])
except ValueError:
assert False
response["tasks"][0]["attachments"][0]["status"].should.equal("ATTACHED")
response["tasks"][0]["attachments"][0]["type"].should.equal(
"ElasticNetworkInterface"
)
details = response["tasks"][0]["attachments"][0]["details"]
assert {"name": "subnetId", "value": setup_resources[0].id} in details
assert {"name": "privateDnsName", "value": eni["PrivateDnsName"]} in details
assert {"name": "privateIPv4Address", "value": eni["PrivateIpAddress"]} in details
assert {"name": "networkInterfaceId", "value": eni["NetworkInterfaceId"]} in details
assert {"name": "macAddress", "value": eni["MacAddress"]} in details
@mock_ec2
@mock_ecs
def test_run_task_awsvpc_network_error():
# Setup
client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
# ECS setup
setup_ecs(client, ec2)
# Execute
with pytest.raises(ClientError) as exc:
client.run_task(
cluster="test_ecs_cluster",
overrides={},
taskDefinition="test_ecs_task",
startedBy="moto",
launchType="FARGATE",
)
err = exc.value.response["Error"]
assert err["Code"].equals("InvalidParameterException")
assert err["Message"].equals(
"Network Configuration must be provided when networkMode 'awsvpc' is specified."
)
@mock_ec2
@mock_ecs
def test_run_task_default_cluster():
@ -3640,3 +3722,42 @@ def test_list_tasks_with_filters():
cluster="test_cluster_1", containerInstance=container_id_1, startedBy="bar"
)
len(resp["taskArns"]).should.equal(1)
def setup_ecs(client, ec2):
"""test helper"""
vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock="10.0.0.0/18")
sg = ec2.create_security_group(
VpcId=vpc.id, GroupName="test-ecs", Description="moto ecs"
)
test_cluster_name = "test_ecs_cluster"
client.create_cluster(clusterName=test_cluster_name)
test_instance = ec2.create_instances(
ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
)[0]
instance_id_document = json.dumps(
ec2_utils.generate_instance_identity_document(test_instance)
)
client.register_container_instance(
cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
)
client.register_task_definition(
family="test_ecs_task",
networkMode="awsvpc",
containerDefinitions=[
{
"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"},
}
],
)
return subnet, sg