ECS: add eni to a task (#5520)
This commit is contained in:
parent
c5586a1724
commit
29829e2eaa
@ -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
|
||||
|
@ -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": []}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user