Add failures output to ecs.describe_services (#3641)

* Add failures output to ecs.describe_services

* Fix autoscaling tests
This commit is contained in:
Anton Grübel 2021-02-01 13:19:46 +01:00 committed by GitHub
parent fe9f1dfe14
commit 0211e9d78d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 261 additions and 175 deletions

View File

@ -114,7 +114,7 @@ class ApplicationAutoscalingBackend(BaseBackend):
for the specified resource ID.
"""
resource_type, cluster, service = r_id.split("/")
result = self.ecs_backend.describe_services(cluster, [service])
result, _ = self.ecs_backend.describe_services(cluster, [service])
if len(result) != 1:
raise AWSValidationException("ECS service doesn't exist: {}".format(r_id))
return True

View File

@ -45,7 +45,7 @@ class ClusterNotFoundException(JsonRESTError):
def __init__(self):
super(ClusterNotFoundException, self).__init__(
error_type="ClientException", message="Cluster not found",
error_type="ClusterNotFoundException", message="Cluster not found.",
)

View File

@ -9,7 +9,7 @@ from random import random, randint
import pytz
from boto3 import Session
from moto.core import BaseBackend, BaseModel, CloudFormationModel
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
from moto.ec2 import ec2_backends
@ -49,8 +49,8 @@ class BaseObject(BaseModel):
class Cluster(BaseObject, CloudFormationModel):
def __init__(self, cluster_name, region_name):
self.active_services_count = 0
self.arn = "arn:aws:ecs:{0}:012345678910:cluster/{1}".format(
region_name, cluster_name
self.arn = "arn:aws:ecs:{0}:{1}:cluster/{2}".format(
region_name, ACCOUNT_ID, cluster_name
)
self.name = cluster_name
self.pending_tasks_count = 0
@ -132,8 +132,8 @@ class TaskDefinition(BaseObject, CloudFormationModel):
):
self.family = family
self.revision = revision
self.arn = "arn:aws:ecs:{0}:012345678910:task-definition/{1}:{2}".format(
region_name, family, revision
self.arn = "arn:aws:ecs:{0}:{1}:task-definition/{2}:{3}".format(
region_name, ACCOUNT_ID, family, revision
)
default_container_definition = {
@ -266,8 +266,8 @@ class Task(BaseObject):
started_by="",
):
self.cluster_arn = cluster.arn
self.task_arn = "arn:aws:ecs:{0}:012345678910:task/{1}".format(
cluster.region_name, str(uuid.uuid4())
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"
@ -299,8 +299,8 @@ class Service(BaseObject, CloudFormationModel):
launch_type=None,
):
self.cluster_arn = cluster.arn
self.arn = "arn:aws:ecs:{0}:012345678910:service/{1}".format(
cluster.region_name, service_name
self.arn = "arn:aws:ecs:{0}:{1}:service/{2}".format(
cluster.region_name, ACCOUNT_ID, service_name
)
self.name = service_name
self.status = "ACTIVE"
@ -476,8 +476,8 @@ class ContainerInstance(BaseObject):
"type": "STRINGSET",
},
]
self.container_instance_arn = "arn:aws:ecs:{0}:012345678910:container-instance/{1}".format(
region_name, str(uuid.uuid4())
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 = [
@ -553,8 +553,8 @@ class ContainerInstance(BaseObject):
class ClusterFailure(BaseObject):
def __init__(self, reason, cluster_name, region_name):
self.reason = reason
self.arn = "arn:aws:ecs:{0}:012345678910:cluster/{1}".format(
region_name, cluster_name
self.arn = "arn:aws:ecs:{0}:{1}:cluster/{2}".format(
region_name, ACCOUNT_ID, cluster_name
)
@property
@ -568,8 +568,8 @@ class ClusterFailure(BaseObject):
class ContainerInstanceFailure(BaseObject):
def __init__(self, reason, container_instance_id, region_name):
self.reason = reason
self.arn = "arn:aws:ecs:{0}:012345678910:container-instance/{1}".format(
region_name, container_instance_id
self.arn = "arn:aws:ecs:{0}:{1}:container-instance/{2}".format(
region_name, ACCOUNT_ID, container_instance_id
)
@property
@ -623,8 +623,8 @@ class TaskSet(BaseObject):
cluster_name = self.cluster.split("/")[-1]
service_name = self.service.split("/")[-1]
self.task_set_arn = "arn:aws:ecs:{0}:012345678910:task-set/{1}/{2}/{3}".format(
region_name, cluster_name, service_name, self.id
self.task_set_arn = "arn:aws:ecs:{0}:{1}:task-set/{2}/{3}/{4}".format(
region_name, ACCOUNT_ID, cluster_name, service_name, self.id
)
@property
@ -663,6 +663,16 @@ class EC2ContainerServiceBackend(BaseBackend):
self.__dict__ = {}
self.__init__(region_name)
def _get_cluster(self, name):
# short name or full ARN of the cluster
cluster_name = name.split("/")[-1]
cluster = self.clusters.get(cluster_name)
if not cluster:
raise ClusterNotFoundException
return cluster
def describe_task_definition(self, task_definition_str):
task_definition_name = task_definition_str.split("/")[-1]
if ":" in task_definition_name:
@ -709,11 +719,9 @@ class EC2ContainerServiceBackend(BaseBackend):
return list_clusters, failures
def delete_cluster(self, cluster_str):
cluster_name = cluster_str.split("/")[-1]
if cluster_name in self.clusters:
return self.clusters.pop(cluster_name)
else:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
return self.clusters.pop(cluster.name)
def register_task_definition(
self,
@ -783,24 +791,21 @@ class EC2ContainerServiceBackend(BaseBackend):
raise TaskDefinitionNotFoundException
def run_task(self, cluster_str, task_definition_str, count, overrides, started_by):
cluster_name = cluster_str.split("/")[-1]
if cluster_name in self.clusters:
cluster = self.clusters[cluster_name]
else:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
task_definition = self.describe_task_definition(task_definition_str)
if cluster_name not in self.tasks:
self.tasks[cluster_name] = {}
if cluster.name not in self.tasks:
self.tasks[cluster.name] = {}
tasks = []
container_instances = list(
self.container_instances.get(cluster_name, {}).keys()
self.container_instances.get(cluster.name, {}).keys()
)
if not container_instances:
raise Exception("No instances found in cluster {}".format(cluster_name))
raise Exception("No instances found in cluster {}".format(cluster.name))
active_container_instances = [
x
for x in container_instances
if self.container_instances[cluster_name][x].status == "ACTIVE"
if self.container_instances[cluster.name][x].status == "ACTIVE"
]
resource_requirements = self._calculate_task_resource_requirements(
task_definition
@ -808,7 +813,7 @@ class EC2ContainerServiceBackend(BaseBackend):
# TODO: return event about unable to place task if not able to place enough tasks to meet count
placed_count = 0
for container_instance in active_container_instances:
container_instance = self.container_instances[cluster_name][
container_instance = self.container_instances[cluster.name][
container_instance
]
container_instance_arn = container_instance.container_instance_arn
@ -830,7 +835,7 @@ class EC2ContainerServiceBackend(BaseBackend):
container_instance, resource_requirements
)
tasks.append(task)
self.tasks[cluster_name][task.task_arn] = task
self.tasks[cluster.name][task.task_arn] = task
placed_count += 1
if placed_count == count:
return tasks
@ -914,14 +919,11 @@ class EC2ContainerServiceBackend(BaseBackend):
overrides,
started_by,
):
cluster_name = cluster_str.split("/")[-1]
if cluster_name in self.clusters:
cluster = self.clusters[cluster_name]
else:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
task_definition = self.describe_task_definition(task_definition_str)
if cluster_name not in self.tasks:
self.tasks[cluster_name] = {}
if cluster.name not in self.tasks:
self.tasks[cluster.name] = {}
tasks = []
if not container_instances:
raise InvalidParameterException("Container Instances cannot be empty.")
@ -931,7 +933,7 @@ class EC2ContainerServiceBackend(BaseBackend):
task_definition
)
for container_instance_id in container_instance_ids:
container_instance = self.container_instances[cluster_name][
container_instance = self.container_instances[cluster.name][
container_instance_id
]
task = Task(
@ -946,15 +948,12 @@ class EC2ContainerServiceBackend(BaseBackend):
self.update_container_instance_resources(
container_instance, resource_requirements
)
self.tasks[cluster_name][task.task_arn] = task
self.tasks[cluster.name][task.task_arn] = task
return tasks
def describe_tasks(self, cluster_str, tasks):
cluster_name = cluster_str.split("/")[-1]
if cluster_name in self.clusters:
cluster = self.clusters[cluster_name]
else:
raise ClusterNotFoundException
self._get_cluster(cluster_str)
if not tasks:
raise InvalidParameterException("Tasks cannot be empty.")
response = []
@ -983,11 +982,10 @@ class EC2ContainerServiceBackend(BaseBackend):
for arn, task in tasks.items():
filtered_tasks.append(task)
if cluster_str:
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
filtered_tasks = list(
filter(lambda t: cluster_name in t.cluster_arn, filtered_tasks)
filter(lambda t: cluster.name in t.cluster_arn, filtered_tasks)
)
if container_instance:
@ -1025,18 +1023,16 @@ class EC2ContainerServiceBackend(BaseBackend):
return [t.task_arn for t in filtered_tasks]
def stop_task(self, cluster_str, task_str, reason):
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
task_id = task_str.split("/")[-1]
tasks = self.tasks.get(cluster_name, None)
tasks = self.tasks.get(cluster.name, None)
if not tasks:
raise Exception("Cluster {} has no registered tasks".format(cluster_name))
raise Exception("Cluster {} has no registered tasks".format(cluster.name))
for task in tasks.keys():
if task.endswith(task_id):
container_instance_arn = tasks[task].container_instance_arn
container_instance = self.container_instances[cluster_name][
container_instance = self.container_instances[cluster.name][
container_instance_arn.split("/")[-1]
]
self.update_container_instance_resources(
@ -1047,7 +1043,7 @@ class EC2ContainerServiceBackend(BaseBackend):
tasks[task].stopped_reason = reason
return tasks[task]
raise Exception(
"Could not find task {} on cluster {}".format(task_str, cluster_name)
"Could not find task {} on cluster {}".format(task_str, cluster.name)
)
def create_service(
@ -1062,11 +1058,8 @@ class EC2ContainerServiceBackend(BaseBackend):
deployment_controller=None,
launch_type=None,
):
cluster_name = cluster_str.split("/")[-1]
if cluster_name in self.clusters:
cluster = self.clusters[cluster_name]
else:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
if task_definition_str is not None:
task_definition = self.describe_task_definition(task_definition_str)
else:
@ -1090,7 +1083,7 @@ class EC2ContainerServiceBackend(BaseBackend):
deployment_controller,
launch_type,
)
cluster_service_pair = "{0}:{1}".format(cluster_name, service_name)
cluster_service_pair = "{0}:{1}".format(cluster.name, service_name)
self.services[cluster_service_pair] = service
return service
@ -1110,31 +1103,42 @@ class EC2ContainerServiceBackend(BaseBackend):
return sorted(service_arns)
def describe_services(self, cluster_str, service_names_or_arns):
cluster_name = cluster_str.split("/")[-1]
cluster = self._get_cluster(cluster_str)
result = []
failures = []
for existing_service_name, existing_service_obj in sorted(
self.services.items()
):
for requested_name_or_arn in service_names_or_arns:
cluster_service_pair = "{0}:{1}".format(
cluster_name, requested_name_or_arn
cluster.name, requested_name_or_arn
)
if (
cluster_service_pair == existing_service_name
or existing_service_obj.arn == requested_name_or_arn
):
result.append(existing_service_obj)
else:
service_name = requested_name_or_arn.split("/")[-1]
failures.append(
{
"arn": "arn:aws:ecs:eu-central-1:{0}:service/{1}".format(
ACCOUNT_ID, service_name
),
"reason": "MISSING",
}
)
return result
return result, failures
def update_service(
self, cluster_str, service_str, task_definition_str, desired_count
):
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
service_name = service_str.split("/")[-1]
cluster_service_pair = "{0}:{1}".format(cluster_name, service_name)
cluster_service_pair = "{0}:{1}".format(cluster.name, service_name)
if cluster_service_pair in self.services:
if task_definition_str is not None:
self.describe_task_definition(task_definition_str)
@ -1148,9 +1152,8 @@ class EC2ContainerServiceBackend(BaseBackend):
raise ServiceNotFoundException
def delete_service(self, cluster_name, service_name, force):
cluster_service_pair = "{0}:{1}".format(cluster_name, service_name)
if cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_name)
cluster_service_pair = "{0}:{1}".format(cluster.name, service_name)
if cluster_service_pair in self.services:
service = self.services[cluster_service_pair]
@ -1188,16 +1191,15 @@ class EC2ContainerServiceBackend(BaseBackend):
return sorted(container_instances)
def describe_container_instances(self, cluster_str, list_container_instance_ids):
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
if not list_container_instance_ids:
raise InvalidParameterException("Container Instances cannot be empty.")
failures = []
container_instance_objects = []
for container_instance_id in list_container_instance_ids:
container_instance_id = container_instance_id.split("/")[-1]
container_instance = self.container_instances[cluster_name].get(
container_instance = self.container_instances[cluster.name].get(
container_instance_id, None
)
if container_instance is not None:
@ -1214,9 +1216,8 @@ class EC2ContainerServiceBackend(BaseBackend):
def update_container_instances_state(
self, cluster_str, list_container_instance_ids, status
):
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_str)
status = status.upper()
if status not in ["ACTIVE", "DRAINING"]:
raise InvalidParameterException(
@ -1228,7 +1229,7 @@ class EC2ContainerServiceBackend(BaseBackend):
x.split("/")[-1] for x in list_container_instance_ids
]
for container_instance_id in list_container_instance_ids:
container_instance = self.container_instances[cluster_name].get(
container_instance = self.container_instances[cluster.name].get(
container_instance_id, None
)
if container_instance is not None:
@ -1267,12 +1268,11 @@ class EC2ContainerServiceBackend(BaseBackend):
container_instance.running_tasks_count += resource_multiplier * 1
def deregister_container_instance(self, cluster_str, container_instance_str, force):
cluster = self._get_cluster(cluster_str)
failures = []
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
container_instance_id = container_instance_str.split("/")[-1]
container_instance = self.container_instances[cluster_name].get(
container_instance = self.container_instances[cluster.name].get(
container_instance_id
)
if container_instance is None:
@ -1287,26 +1287,24 @@ class EC2ContainerServiceBackend(BaseBackend):
self.container_instances["orphaned"][
container_instance_id
] = container_instance
del self.container_instances[cluster_name][container_instance_id]
del self.container_instances[cluster.name][container_instance_id]
self._respond_to_cluster_state_update(cluster_str)
return container_instance, failures
def _respond_to_cluster_state_update(self, cluster_str):
cluster_name = cluster_str.split("/")[-1]
if cluster_name not in self.clusters:
raise ClusterNotFoundException
self._get_cluster(cluster_str)
pass
def put_attributes(self, cluster_name, attributes=None):
if cluster_name is None or cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_name)
if attributes is None:
raise InvalidParameterException("attributes can not be empty")
for attr in attributes:
self._put_attribute(
cluster_name,
cluster.name,
attr["name"],
attr.get("value"),
attr.get("targetId"),
@ -1385,8 +1383,7 @@ class EC2ContainerServiceBackend(BaseBackend):
return filter(lambda x: all(f(x) for f in filters), all_attrs)
def delete_attributes(self, cluster_name, attributes=None):
if cluster_name is None or cluster_name not in self.clusters:
raise ClusterNotFoundException
cluster = self._get_cluster(cluster_name)
if attributes is None:
raise JsonRESTError(
@ -1395,7 +1392,7 @@ class EC2ContainerServiceBackend(BaseBackend):
for attr in attributes:
self._delete_attribute(
cluster_name,
cluster.name,
attr["name"],
attr.get("value"),
attr.get("targetId"),
@ -1522,7 +1519,7 @@ class EC2ContainerServiceBackend(BaseBackend):
def create_task_set(
self,
service,
cluster,
cluster_str,
task_definition,
external_id=None,
network_configuration=None,
@ -1543,7 +1540,7 @@ class EC2ContainerServiceBackend(BaseBackend):
task_set = TaskSet(
service,
cluster,
cluster_str,
task_definition,
self.region_name,
external_id=external_id,
@ -1558,17 +1555,15 @@ class EC2ContainerServiceBackend(BaseBackend):
tags=tags,
)
cluster_name = cluster.split("/")[-1]
service_name = service.split("/")[-1]
service_obj = self.services.get("{0}:{1}".format(cluster_name, service_name))
cluster_obj = self._get_cluster(cluster_str)
service_obj = self.services.get(
"{0}:{1}".format(cluster_obj.name, service_name)
)
if not service_obj:
raise ServiceNotFoundException
cluster_obj = self.clusters.get(cluster_name)
if not cluster_obj:
raise ClusterNotFoundException
task_set.task_definition = self.describe_task_definition(task_definition).arn
task_set.service_arn = service_obj.arn
task_set.cluster_arn = cluster_obj.arn
@ -1578,22 +1573,19 @@ class EC2ContainerServiceBackend(BaseBackend):
return task_set
def describe_task_sets(self, cluster, service, task_sets=None, include=None):
def describe_task_sets(self, cluster_str, service, task_sets=None, include=None):
task_sets = task_sets or []
include = include or []
cluster_name = cluster.split("/")[-1]
cluster_obj = self._get_cluster(cluster_str)
service_name = service.split("/")[-1]
service_key = "{0}:{1}".format(cluster_name, service_name)
service_key = "{0}:{1}".format(cluster_obj.name, service_name)
service_obj = self.services.get(service_key)
if not service_obj:
raise ServiceNotFoundException
cluster_obj = self.clusters.get(cluster_name)
if not cluster_obj:
raise ClusterNotFoundException
task_set_results = []
if task_sets:
for task_set in service_obj.task_sets:
@ -1642,7 +1634,8 @@ class EC2ContainerServiceBackend(BaseBackend):
cluster_name, service_name, task_sets=[primary_task_set]
)[0]
service_obj = self.describe_services(cluster, [service])[0]
services, _ = self.describe_services(cluster, [service])
service_obj = services[0]
service_obj.load_balancers = task_set_obj.load_balancers
service_obj.task_definition = task_set_obj.task_definition

View File

@ -201,10 +201,12 @@ class EC2ContainerServiceResponse(BaseResponse):
def describe_services(self):
cluster_str = self._get_param("cluster", "default")
service_names = self._get_param("services")
services = self.ecs_backend.describe_services(cluster_str, service_names)
services, failures = self.ecs_backend.describe_services(
cluster_str, service_names
)
resp = {
"services": [service.response_object for service in services],
"failures": [],
"failures": failures,
}
if "TAGS" in self._get_param("include", []):
for i, service in enumerate(services):

View File

@ -1,6 +1,8 @@
import boto3
from moto import mock_cloudformation, mock_ecs, mock_autoscaling, mock_s3
import json
from moto.core import ACCOUNT_ID
from tests import EXAMPLE_AMI_ID
depends_on_template_list = {
@ -110,7 +112,9 @@ def test_create_stack_with_depends_on():
ecs = boto3.client("ecs", region_name="us-east-1")
cluster_arn = ecs.list_clusters()["clusterArns"][0]
assert cluster_arn == "arn:aws:ecs:us-east-1:012345678910:cluster/test-cluster"
assert cluster_arn == "arn:aws:ecs:us-east-1:{}:cluster/test-cluster".format(
ACCOUNT_ID
)
@mock_cloudformation

View File

@ -5,6 +5,8 @@ from botocore.exceptions import ClientError
import boto3
import sure # noqa
import json
from moto.core import ACCOUNT_ID
from moto.ec2 import utils as ec2_utils
from uuid import UUID
@ -27,7 +29,7 @@ def test_create_cluster():
response = client.create_cluster(clusterName="test_ecs_cluster")
response["cluster"]["clusterName"].should.equal("test_ecs_cluster")
response["cluster"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["cluster"]["status"].should.equal("ACTIVE")
response["cluster"]["registeredContainerInstancesCount"].should.equal(0)
@ -43,10 +45,10 @@ def test_list_clusters():
_ = client.create_cluster(clusterName="test_cluster1")
response = client.list_clusters()
response["clusterArns"].should.contain(
"arn:aws:ecs:us-east-2:012345678910:cluster/test_cluster0"
"arn:aws:ecs:us-east-2:{}:cluster/test_cluster0".format(ACCOUNT_ID)
)
response["clusterArns"].should.contain(
"arn:aws:ecs:us-east-2:012345678910:cluster/test_cluster1"
"arn:aws:ecs:us-east-2:{}:cluster/test_cluster1".format(ACCOUNT_ID)
)
@ -56,7 +58,7 @@ def test_describe_clusters():
response = client.describe_clusters(clusters=["some-cluster"])
response["failures"].should.contain(
{
"arn": "arn:aws:ecs:us-east-1:012345678910:cluster/some-cluster",
"arn": "arn:aws:ecs:us-east-1:{}:cluster/some-cluster".format(ACCOUNT_ID),
"reason": "MISSING",
}
)
@ -69,7 +71,7 @@ def test_delete_cluster():
response = client.delete_cluster(cluster="test_ecs_cluster")
response["cluster"]["clusterName"].should.equal("test_ecs_cluster")
response["cluster"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["cluster"]["status"].should.equal("ACTIVE")
response["cluster"]["registeredContainerInstancesCount"].should.equal(0)
@ -106,7 +108,7 @@ def test_register_task_definition():
response["taskDefinition"]["family"].should.equal("test_ecs_task")
response["taskDefinition"]["revision"].should.equal(1)
response["taskDefinition"]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["taskDefinition"]["networkMode"].should.equal("bridge")
response["taskDefinition"]["volumes"].should.equal([])
@ -144,7 +146,7 @@ def test_register_task_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"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:2".format(ACCOUNT_ID)
)
# Registering with optional top-level params
@ -239,10 +241,10 @@ def test_list_task_definitions():
response = client.list_task_definitions()
len(response["taskDefinitionArns"]).should.equal(2)
response["taskDefinitionArns"][0].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["taskDefinitionArns"][1].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:2"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:2".format(ACCOUNT_ID)
)
@ -302,10 +304,10 @@ def test_list_task_definitions_with_family_prefix():
filtered_response = client.list_task_definitions(familyPrefix="test_ecs_task_a")
len(filtered_response["taskDefinitionArns"]).should.equal(2)
filtered_response["taskDefinitionArns"][0].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task_a:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task_a:1".format(ACCOUNT_ID)
)
filtered_response["taskDefinitionArns"][1].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task_a:2"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task_a:2".format(ACCOUNT_ID)
)
@ -363,12 +365,12 @@ def test_describe_task_definition():
)
response = client.describe_task_definition(taskDefinition="test_ecs_task")
response["taskDefinition"]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:3"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:3".format(ACCOUNT_ID)
)
response = client.describe_task_definition(taskDefinition="test_ecs_task:2")
response["taskDefinition"]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:2"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:2".format(ACCOUNT_ID)
)
response = client.describe_task_definition(
@ -467,7 +469,7 @@ def test_create_service():
desiredCount=2,
)
response["service"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["service"]["desiredCount"].should.equal(2)
len(response["service"]["events"]).should.equal(0)
@ -475,12 +477,12 @@ def test_create_service():
response["service"]["pendingCount"].should.equal(0)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
)
response["service"]["serviceName"].should.equal("test_ecs_service")
response["service"]["status"].should.equal("ACTIVE")
response["service"]["taskDefinition"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["service"]["schedulingStrategy"].should.equal("REPLICA")
response["service"]["launchType"].should.equal("EC2")
@ -557,7 +559,7 @@ def test_create_service_scheduling_strategy():
schedulingStrategy="DAEMON",
)
response["service"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["service"]["desiredCount"].should.equal(2)
len(response["service"]["events"]).should.equal(0)
@ -565,12 +567,12 @@ def test_create_service_scheduling_strategy():
response["service"]["pendingCount"].should.equal(0)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
)
response["service"]["serviceName"].should.equal("test_ecs_service")
response["service"]["status"].should.equal("ACTIVE")
response["service"]["taskDefinition"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["service"]["schedulingStrategy"].should.equal("DAEMON")
@ -612,10 +614,10 @@ def test_list_services():
unfiltered_response = client.list_services(cluster="test_ecs_cluster")
len(unfiltered_response["serviceArns"]).should.equal(2)
unfiltered_response["serviceArns"][0].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service1"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
)
unfiltered_response["serviceArns"][1].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service2"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID)
)
filtered_response = client.list_services(
@ -623,7 +625,7 @@ def test_list_services():
)
len(filtered_response["serviceArns"]).should.equal(1)
filtered_response["serviceArns"][0].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service1"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
)
@ -670,16 +672,16 @@ def test_describe_services():
cluster="test_ecs_cluster",
services=[
"test_ecs_service1",
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service2",
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID),
],
)
len(response["services"]).should.equal(2)
response["services"][0]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service1"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
)
response["services"][0]["serviceName"].should.equal("test_ecs_service1")
response["services"][1]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service2"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID)
)
response["services"][1]["serviceName"].should.equal("test_ecs_service2")
@ -700,7 +702,7 @@ def test_describe_services():
cluster="test_ecs_cluster",
services=[
"test_ecs_service1",
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service2",
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID),
],
include=["TAGS"],
)
@ -755,17 +757,17 @@ def test_describe_services_scheduling_strategy():
cluster="test_ecs_cluster",
services=[
"test_ecs_service1",
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service2",
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID),
"test_ecs_service3",
],
)
len(response["services"]).should.equal(3)
response["services"][0]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service1"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
)
response["services"][0]["serviceName"].should.equal("test_ecs_service1")
response["services"][1]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service2"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID)
)
response["services"][1]["serviceName"].should.equal("test_ecs_service2")
@ -779,6 +781,89 @@ def test_describe_services_scheduling_strategy():
response["services"][2]["schedulingStrategy"].should.equal("REPLICA")
@mock_ecs
def test_describe_services_error_unknown_cluster():
# given
client = boto3.client("ecs", region_name="eu-central-1")
cluster_name = "unknown"
# when
with pytest.raises(ClientError) as e:
client.describe_services(
cluster=cluster_name, services=["test"],
)
# then
ex = e.value
ex.operation_name.should.equal("DescribeServices")
ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
ex.response["Error"]["Code"].should.contain("ClusterNotFoundException")
ex.response["Error"]["Message"].should.equal("Cluster not found.")
@mock_ecs
def test_describe_services_with_known_unknown_services():
# given
client = boto3.client("ecs", region_name="eu-central-1")
cluster_name = "test_cluster"
task_name = "test_task"
service_name = "test_service"
client.create_cluster(clusterName=cluster_name)
client.register_task_definition(
family=task_name,
containerDefinitions=[
{
"name": "hello_world",
"image": "docker/hello-world:latest",
"cpu": 256,
"memory": 512,
"essential": True,
}
],
)
service_arn = client.create_service(
cluster=cluster_name,
serviceName=service_name,
taskDefinition=task_name,
desiredCount=1,
)["service"]["serviceArn"]
# when
response = client.describe_services(
cluster=cluster_name,
services=[
service_name,
"unknown",
service_arn,
"arn:aws:ecs:eu-central-1:{}:service/unknown-2".format(ACCOUNT_ID),
],
)
# then
services = response["services"]
services.should.have.length_of(2)
[service["serviceArn"] for service in services].should.equal(
[service_arn, service_arn]
)
failures = response["failures"]
failures.should.have.length_of(2)
sorted(failures, key=lambda item: item["arn"]).should.equal(
[
{
"arn": "arn:aws:ecs:eu-central-1:{}:service/unknown".format(ACCOUNT_ID),
"reason": "MISSING",
},
{
"arn": "arn:aws:ecs:eu-central-1:{}:service/unknown-2".format(
ACCOUNT_ID
),
"reason": "MISSING",
},
]
)
@mock_ecs
def test_update_service():
client = boto3.client("ecs", region_name="us-east-1")
@ -872,7 +957,7 @@ def test_delete_service():
cluster="test_ecs_cluster", service="test_ecs_service"
)
response["service"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["service"]["desiredCount"].should.equal(0)
len(response["service"]["events"]).should.equal(0)
@ -880,13 +965,13 @@ def test_delete_service():
response["service"]["pendingCount"].should.equal(0)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
)
response["service"]["serviceName"].should.equal("test_ecs_service")
response["service"]["status"].should.equal("ACTIVE")
response["service"]["schedulingStrategy"].should.equal("REPLICA")
response["service"]["taskDefinition"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
@ -920,20 +1005,20 @@ def test_delete_service_force():
cluster="test_ecs_cluster", service="test_ecs_service", force=True
)
response["service"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
len(response["service"]["events"]).should.equal(0)
len(response["service"]["loadBalancers"]).should.equal(0)
response["service"]["pendingCount"].should.equal(0)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
)
response["service"]["serviceName"].should.equal("test_ecs_service")
response["service"]["status"].should.equal("ACTIVE")
response["service"]["schedulingStrategy"].should.equal("REPLICA")
response["service"]["taskDefinition"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
@ -1015,7 +1100,9 @@ def test_register_container_instance():
response["containerInstance"]["ec2InstanceId"].should.equal(test_instance.id)
full_arn = response["containerInstance"]["containerInstanceArn"]
arn_part = full_arn.split("/")
arn_part[0].should.equal("arn:aws:ecs:us-east-1:012345678910:container-instance")
arn_part[0].should.equal(
"arn:aws:ecs:us-east-1:{}:container-instance".format(ACCOUNT_ID)
)
arn_part[1].should.equal(str(UUID(arn_part[1])))
response["containerInstance"]["status"].should.equal("ACTIVE")
len(response["containerInstance"]["registeredResources"]).should.equal(4)
@ -1373,16 +1460,16 @@ def test_run_task():
)
len(response["tasks"]).should.equal(2)
response["tasks"][0]["taskArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:task/"
"arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
)
response["tasks"][0]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["tasks"][0]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["tasks"][0]["containerInstanceArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:container-instance/"
"arn:aws:ecs:us-east-1:{}:container-instance/".format(ACCOUNT_ID)
)
response["tasks"][0]["overrides"].should.equal({})
response["tasks"][0]["lastStatus"].should.equal("RUNNING")
@ -1438,16 +1525,16 @@ def test_run_task_default_cluster():
)
len(response["tasks"]).should.equal(2)
response["tasks"][0]["taskArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:task/"
"arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
)
response["tasks"][0]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/default"
"arn:aws:ecs:us-east-1:{}:cluster/default".format(ACCOUNT_ID)
)
response["tasks"][0]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["tasks"][0]["containerInstanceArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:container-instance/"
"arn:aws:ecs:us-east-1:{}:container-instance/".format(ACCOUNT_ID)
)
response["tasks"][0]["overrides"].should.equal({})
response["tasks"][0]["lastStatus"].should.equal("RUNNING")
@ -1530,17 +1617,17 @@ def test_start_task():
len(response["tasks"]).should.equal(1)
response["tasks"][0]["taskArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:task/"
"arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
)
response["tasks"][0]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["tasks"][0]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["tasks"][0]["containerInstanceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:container-instance/{0}".format(
container_instance_id
"arn:aws:ecs:us-east-1:{0}:container-instance/{1}".format(
ACCOUNT_ID, container_instance_id
)
)
response["tasks"][0]["overrides"].should.equal({})
@ -2057,16 +2144,16 @@ def test_task_definitions_with_port_clash():
)
len(response["tasks"]).should.equal(1)
response["tasks"][0]["taskArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:task/"
"arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
)
response["tasks"][0]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["tasks"][0]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
response["tasks"][0]["containerInstanceArn"].should.contain(
"arn:aws:ecs:us-east-1:012345678910:container-instance/"
"arn:aws:ecs:us-east-1:{}:container-instance/".format(ACCOUNT_ID)
)
response["tasks"][0]["overrides"].should.equal({})
response["tasks"][0]["lastStatus"].should.equal("RUNNING")
@ -2408,7 +2495,7 @@ def test_create_service_load_balancing():
],
)
response["service"]["clusterArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:cluster/test_ecs_cluster"
"arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
)
response["service"]["desiredCount"].should.equal(2)
len(response["service"]["events"]).should.equal(0)
@ -2426,12 +2513,12 @@ def test_create_service_load_balancing():
response["service"]["pendingCount"].should.equal(0)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:service/test_ecs_service"
"arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
)
response["service"]["serviceName"].should.equal("test_ecs_service")
response["service"]["status"].should.equal("ACTIVE")
response["service"]["taskDefinition"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
@ -2461,7 +2548,7 @@ def test_list_tags_for_resource():
type(response["taskDefinition"]).should.be(dict)
response["taskDefinition"]["revision"].should.equal(1)
response["taskDefinition"]["taskDefinitionArn"].should.equal(
"arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1"
"arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
)
task_definition_arn = response["taskDefinition"]["taskDefinitionArn"]