ECS - Enable option to use new ARN format (#4450)

This commit is contained in:
Bert Blommers 2021-10-21 10:05:10 +00:00 committed by GitHub
parent 125936d269
commit 7f0ef4a0cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 160 additions and 17 deletions

View File

@ -7,6 +7,7 @@ from random import random, randint
import pytz
from boto3 import Session
from moto import settings
from moto.core import BaseBackend, BaseModel, CloudFormationModel, ACCOUNT_ID
from moto.core.exceptions import JsonRESTError
from moto.core.utils import unix_time, pascal_to_camelcase, remap_nested_keys
@ -272,10 +273,9 @@ class Task(BaseObject):
started_by="",
tags=[],
):
self.id = str(uuid.uuid4())
self.cluster_name = cluster.name
self.cluster_arn = cluster.arn
self.task_arn = "arn:aws:ecs:{0}:{1}:task/{2}".format(
cluster.region_name, ACCOUNT_ID, str(uuid.uuid4())
)
self.container_instance_arn = container_instance_arn
self.last_status = "RUNNING"
self.desired_status = "RUNNING"
@ -286,10 +286,20 @@ class Task(BaseObject):
self.tags = tags
self.stopped_reason = ""
self.resource_requirements = resource_requirements
self.region_name = cluster.region_name
@property
def task_arn(self):
if settings.ecs_new_arn_format():
return f"arn:aws:ecs:{self.region_name}:{ACCOUNT_ID}:task/{self.cluster_name}/{self.id}"
return "arn:aws:ecs:{0}:{1}:task/{2}".format(
self.region_name, ACCOUNT_ID, self.id
)
@property
def response_object(self):
response_object = self.gen_response_object()
response_object["taskArn"] = self.task_arn
return response_object
@ -307,10 +317,8 @@ class Service(BaseObject, CloudFormationModel):
launch_type=None,
service_registries=None,
):
self.cluster_name = cluster.name
self.cluster_arn = cluster.arn
self.arn = "arn:aws:ecs:{0}:{1}:service/{2}".format(
cluster.region_name, ACCOUNT_ID, service_name
)
self.name = service_name
self.status = "ACTIVE"
self.running_count = 0
@ -346,6 +354,15 @@ class Service(BaseObject, CloudFormationModel):
)
self.tags = tags if tags is not None else []
self.pending_count = 0
self.region_name = cluster.region_name
@property
def arn(self):
if settings.ecs_new_arn_format():
return f"arn:aws:ecs:{self.region_name}:{ACCOUNT_ID}:service/{self.cluster_name}/{self.name}"
return "arn:aws:ecs:{0}:{1}:service/{2}".format(
self.region_name, ACCOUNT_ID, self.name
)
@property
def physical_resource_id(self):
@ -354,7 +371,7 @@ class Service(BaseObject, CloudFormationModel):
@property
def response_object(self):
response_object = self.gen_response_object()
del response_object["name"], response_object["arn"], response_object["tags"]
del response_object["name"], response_object["tags"]
response_object["serviceName"] = self.name
response_object["serviceArn"] = self.arn
response_object["schedulingStrategy"] = self.scheduling_strategy
@ -450,7 +467,7 @@ class Service(BaseObject, CloudFormationModel):
class ContainerInstance(BaseObject):
def __init__(self, ec2_instance_id, region_name):
def __init__(self, ec2_instance_id, region_name, cluster_name):
self.ec2_instance_id = ec2_instance_id
self.agent_connected = True
self.status = "ACTIVE"
@ -486,9 +503,6 @@ class ContainerInstance(BaseObject):
"type": "STRINGSET",
},
]
self.container_instance_arn = "arn:aws:ecs:{0}:{1}:container-instance/{2}".format(
region_name, ACCOUNT_ID, str(uuid.uuid4())
)
self.pending_tasks_count = 0
self.remaining_resources = [
{
@ -539,10 +553,22 @@ class ContainerInstance(BaseObject):
else "linux", # options are windows and linux, linux is default
}
self.registered_at = datetime.now(pytz.utc)
self.region_name = region_name
self.id = str(uuid.uuid4())
self.cluster_name = cluster_name
@property
def container_instance_arn(self):
if settings.ecs_new_arn_format():
return f"arn:aws:ecs:{self.region_name}:{ACCOUNT_ID}:container-instance/{self.cluster_name}/{self.id}"
return (
f"arn:aws:ecs:{self.region_name}:{ACCOUNT_ID}:container-instance/{self.id}"
)
@property
def response_object(self):
response_object = self.gen_response_object()
response_object["containerInstanceArn"] = self.container_instance_arn
response_object["attributes"] = [
self._format_attribute(name, value)
for name, value in response_object["attributes"].items()
@ -1198,7 +1224,9 @@ class EC2ContainerServiceBackend(BaseBackend):
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise Exception("{0} is not a cluster".format(cluster_name))
container_instance = ContainerInstance(ec2_instance_id, self.region_name)
container_instance = ContainerInstance(
ec2_instance_id, self.region_name, cluster_name
)
if not self.container_instances.get(cluster_name):
self.container_instances[cluster_name] = {}
container_instance_id = container_instance.container_instance_arn.split("/")[-1]

View File

@ -32,3 +32,7 @@ def get_s3_default_key_buffer_size():
"MOTO_S3_DEFAULT_KEY_BUFFER_SIZE", S3_UPLOAD_PART_MIN_SIZE - 1024
)
)
def ecs_new_arn_format():
return os.environ.get("MOTO_ECS_NEW_ARN", "false").lower() == "true"

View File

@ -2,15 +2,16 @@ from datetime import datetime
from botocore.exceptions import ClientError
import boto3
import mock
import sure # noqa # pylint: disable=unused-import
import json
import os
from moto.core import ACCOUNT_ID
from moto.ec2 import utils as ec2_utils
from uuid import UUID
from moto import mock_ecs
from moto import mock_ec2
from moto import mock_ecs, mock_ec2, settings
from moto.ecs.exceptions import (
ClusterNotFoundException,
ServiceNotFoundException,
@ -20,6 +21,7 @@ from moto.ecs.exceptions import (
)
import pytest
from tests import EXAMPLE_AMI_ID
from unittest import SkipTest
@mock_ecs
@ -724,6 +726,36 @@ def test_describe_services():
response["services"][1]["launchType"].should.equal("EC2")
@mock_ecs
@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
def test_describe_services_new_arn():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
client = boto3.client("ecs", region_name="us-east-1")
_ = client.create_cluster(clusterName="test_ecs_cluster")
_ = client.register_task_definition(
family="test_ecs_task",
containerDefinitions=[
{"name": "hello_world", "image": "docker/hello-world:latest",}
],
)
_ = client.create_service(
cluster="test_ecs_cluster",
serviceName="test_ecs_service1",
taskDefinition="test_ecs_task",
desiredCount=2,
tags=[{"key": "Name", "value": "test_ecs_service1"}],
)
response = client.describe_services(
cluster="test_ecs_cluster", services=["test_ecs_service1"]
)
response["services"][0]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:{}:service/test_ecs_cluster/test_ecs_service1".format(
ACCOUNT_ID
)
)
@mock_ecs
def test_describe_services_scheduling_strategy():
client = boto3.client("ecs", region_name="us-east-1")
@ -1125,6 +1157,37 @@ def test_register_container_instance():
)
@mock_ec2
@mock_ecs
@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
def test_register_container_instance_new_arn_format():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
ecs_client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
test_cluster_name = "test_ecs_cluster"
ecs_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)
)
response = ecs_client.register_container_instance(
cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
)
full_arn = response["containerInstance"]["containerInstanceArn"]
full_arn.should.match(
f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:container-instance/{test_cluster_name}/[a-z0-9-]+$"
)
@mock_ec2
@mock_ecs
def test_deregister_container_instance():
@ -1545,8 +1608,8 @@ def test_run_task_default_cluster():
startedBy="moto",
)
len(response["tasks"]).should.equal(2)
response["tasks"][0]["taskArn"].should.contain(
"arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
response["tasks"][0]["taskArn"].should.match(
"arn:aws:ecs:us-east-1:{}:task/[a-z0-9-]+$".format(ACCOUNT_ID)
)
response["tasks"][0]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:{}:cluster/default".format(ACCOUNT_ID)
@ -1564,6 +1627,54 @@ def test_run_task_default_cluster():
response["tasks"][0]["stoppedReason"].should.equal("")
@mock_ec2
@mock_ecs
@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
def test_run_task_default_cluster_new_arn_format():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
test_cluster_name = "default"
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",
containerDefinitions=[
{
"name": "hello_world",
"image": "docker/hello-world:latest",
"cpu": 1024,
"memory": 400,
}
],
)
response = client.run_task(
launchType="FARGATE",
overrides={},
taskDefinition="test_ecs_task",
count=1,
startedBy="moto",
)
response["tasks"][0]["taskArn"].should.match(
f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:task/{test_cluster_name}/[a-z0-9-]+$"
)
@mock_ecs
def test_run_task_exceptions():
client = boto3.client("ecs", region_name="us-east-1")
@ -1849,7 +1960,7 @@ def test_describe_task_definition_by_family():
task["containerDefinitions"][0].should.equal(
dict(
container_definition,
**{"mountPoints": [], "portMappings": [], "volumesFrom": []}
**{"mountPoints": [], "portMappings": [], "volumesFrom": []},
)
)
task["taskDefinitionArn"].should.equal(