ECS - Enable option to use new ARN format (#4450)
This commit is contained in:
parent
125936d269
commit
7f0ef4a0cc
@ -7,6 +7,7 @@ from random import random, randint
|
|||||||
import pytz
|
import pytz
|
||||||
from boto3 import Session
|
from boto3 import Session
|
||||||
|
|
||||||
|
from moto import settings
|
||||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel, ACCOUNT_ID
|
from moto.core import BaseBackend, BaseModel, CloudFormationModel, ACCOUNT_ID
|
||||||
from moto.core.exceptions import JsonRESTError
|
from moto.core.exceptions import JsonRESTError
|
||||||
from moto.core.utils import unix_time, pascal_to_camelcase, remap_nested_keys
|
from moto.core.utils import unix_time, pascal_to_camelcase, remap_nested_keys
|
||||||
@ -272,10 +273,9 @@ class Task(BaseObject):
|
|||||||
started_by="",
|
started_by="",
|
||||||
tags=[],
|
tags=[],
|
||||||
):
|
):
|
||||||
|
self.id = str(uuid.uuid4())
|
||||||
|
self.cluster_name = cluster.name
|
||||||
self.cluster_arn = cluster.arn
|
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.container_instance_arn = container_instance_arn
|
||||||
self.last_status = "RUNNING"
|
self.last_status = "RUNNING"
|
||||||
self.desired_status = "RUNNING"
|
self.desired_status = "RUNNING"
|
||||||
@ -286,10 +286,20 @@ class Task(BaseObject):
|
|||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.stopped_reason = ""
|
self.stopped_reason = ""
|
||||||
self.resource_requirements = resource_requirements
|
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
|
@property
|
||||||
def response_object(self):
|
def response_object(self):
|
||||||
response_object = self.gen_response_object()
|
response_object = self.gen_response_object()
|
||||||
|
response_object["taskArn"] = self.task_arn
|
||||||
return response_object
|
return response_object
|
||||||
|
|
||||||
|
|
||||||
@ -307,10 +317,8 @@ class Service(BaseObject, CloudFormationModel):
|
|||||||
launch_type=None,
|
launch_type=None,
|
||||||
service_registries=None,
|
service_registries=None,
|
||||||
):
|
):
|
||||||
|
self.cluster_name = cluster.name
|
||||||
self.cluster_arn = cluster.arn
|
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.name = service_name
|
||||||
self.status = "ACTIVE"
|
self.status = "ACTIVE"
|
||||||
self.running_count = 0
|
self.running_count = 0
|
||||||
@ -346,6 +354,15 @@ class Service(BaseObject, CloudFormationModel):
|
|||||||
)
|
)
|
||||||
self.tags = tags if tags is not None else []
|
self.tags = tags if tags is not None else []
|
||||||
self.pending_count = 0
|
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
|
@property
|
||||||
def physical_resource_id(self):
|
def physical_resource_id(self):
|
||||||
@ -354,7 +371,7 @@ class Service(BaseObject, CloudFormationModel):
|
|||||||
@property
|
@property
|
||||||
def response_object(self):
|
def response_object(self):
|
||||||
response_object = self.gen_response_object()
|
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["serviceName"] = self.name
|
||||||
response_object["serviceArn"] = self.arn
|
response_object["serviceArn"] = self.arn
|
||||||
response_object["schedulingStrategy"] = self.scheduling_strategy
|
response_object["schedulingStrategy"] = self.scheduling_strategy
|
||||||
@ -450,7 +467,7 @@ class Service(BaseObject, CloudFormationModel):
|
|||||||
|
|
||||||
|
|
||||||
class ContainerInstance(BaseObject):
|
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.ec2_instance_id = ec2_instance_id
|
||||||
self.agent_connected = True
|
self.agent_connected = True
|
||||||
self.status = "ACTIVE"
|
self.status = "ACTIVE"
|
||||||
@ -486,9 +503,6 @@ class ContainerInstance(BaseObject):
|
|||||||
"type": "STRINGSET",
|
"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.pending_tasks_count = 0
|
||||||
self.remaining_resources = [
|
self.remaining_resources = [
|
||||||
{
|
{
|
||||||
@ -539,10 +553,22 @@ class ContainerInstance(BaseObject):
|
|||||||
else "linux", # options are windows and linux, linux is default
|
else "linux", # options are windows and linux, linux is default
|
||||||
}
|
}
|
||||||
self.registered_at = datetime.now(pytz.utc)
|
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
|
@property
|
||||||
def response_object(self):
|
def response_object(self):
|
||||||
response_object = self.gen_response_object()
|
response_object = self.gen_response_object()
|
||||||
|
response_object["containerInstanceArn"] = self.container_instance_arn
|
||||||
response_object["attributes"] = [
|
response_object["attributes"] = [
|
||||||
self._format_attribute(name, value)
|
self._format_attribute(name, value)
|
||||||
for name, value in response_object["attributes"].items()
|
for name, value in response_object["attributes"].items()
|
||||||
@ -1198,7 +1224,9 @@ class EC2ContainerServiceBackend(BaseBackend):
|
|||||||
cluster_name = cluster_str.split("/")[-1]
|
cluster_name = cluster_str.split("/")[-1]
|
||||||
if cluster_name not in self.clusters:
|
if cluster_name not in self.clusters:
|
||||||
raise Exception("{0} is not a cluster".format(cluster_name))
|
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):
|
if not self.container_instances.get(cluster_name):
|
||||||
self.container_instances[cluster_name] = {}
|
self.container_instances[cluster_name] = {}
|
||||||
container_instance_id = container_instance.container_instance_arn.split("/")[-1]
|
container_instance_id = container_instance.container_instance_arn.split("/")[-1]
|
||||||
|
@ -32,3 +32,7 @@ def get_s3_default_key_buffer_size():
|
|||||||
"MOTO_S3_DEFAULT_KEY_BUFFER_SIZE", S3_UPLOAD_PART_MIN_SIZE - 1024
|
"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"
|
||||||
|
@ -2,15 +2,16 @@ from datetime import datetime
|
|||||||
|
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
import boto3
|
import boto3
|
||||||
|
import mock
|
||||||
import sure # noqa # pylint: disable=unused-import
|
import sure # noqa # pylint: disable=unused-import
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
from moto.core import ACCOUNT_ID
|
from moto.core import ACCOUNT_ID
|
||||||
from moto.ec2 import utils as ec2_utils
|
from moto.ec2 import utils as ec2_utils
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from moto import mock_ecs
|
from moto import mock_ecs, mock_ec2, settings
|
||||||
from moto import mock_ec2
|
|
||||||
from moto.ecs.exceptions import (
|
from moto.ecs.exceptions import (
|
||||||
ClusterNotFoundException,
|
ClusterNotFoundException,
|
||||||
ServiceNotFoundException,
|
ServiceNotFoundException,
|
||||||
@ -20,6 +21,7 @@ from moto.ecs.exceptions import (
|
|||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
from tests import EXAMPLE_AMI_ID
|
from tests import EXAMPLE_AMI_ID
|
||||||
|
from unittest import SkipTest
|
||||||
|
|
||||||
|
|
||||||
@mock_ecs
|
@mock_ecs
|
||||||
@ -724,6 +726,36 @@ def test_describe_services():
|
|||||||
response["services"][1]["launchType"].should.equal("EC2")
|
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
|
@mock_ecs
|
||||||
def test_describe_services_scheduling_strategy():
|
def test_describe_services_scheduling_strategy():
|
||||||
client = boto3.client("ecs", region_name="us-east-1")
|
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_ec2
|
||||||
@mock_ecs
|
@mock_ecs
|
||||||
def test_deregister_container_instance():
|
def test_deregister_container_instance():
|
||||||
@ -1545,8 +1608,8 @@ def test_run_task_default_cluster():
|
|||||||
startedBy="moto",
|
startedBy="moto",
|
||||||
)
|
)
|
||||||
len(response["tasks"]).should.equal(2)
|
len(response["tasks"]).should.equal(2)
|
||||||
response["tasks"][0]["taskArn"].should.contain(
|
response["tasks"][0]["taskArn"].should.match(
|
||||||
"arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
|
"arn:aws:ecs:us-east-1:{}:task/[a-z0-9-]+$".format(ACCOUNT_ID)
|
||||||
)
|
)
|
||||||
response["tasks"][0]["clusterArn"].should.equal(
|
response["tasks"][0]["clusterArn"].should.equal(
|
||||||
"arn:aws:ecs:us-east-1:{}:cluster/default".format(ACCOUNT_ID)
|
"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("")
|
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
|
@mock_ecs
|
||||||
def test_run_task_exceptions():
|
def test_run_task_exceptions():
|
||||||
client = boto3.client("ecs", region_name="us-east-1")
|
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(
|
task["containerDefinitions"][0].should.equal(
|
||||||
dict(
|
dict(
|
||||||
container_definition,
|
container_definition,
|
||||||
**{"mountPoints": [], "portMappings": [], "volumesFrom": []}
|
**{"mountPoints": [], "portMappings": [], "volumesFrom": []},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
task["taskDefinitionArn"].should.equal(
|
task["taskDefinitionArn"].should.equal(
|
||||||
|
Loading…
Reference in New Issue
Block a user