diff --git a/moto/ecs/exceptions.py b/moto/ecs/exceptions.py index 72129224e..a9c6f4a71 100644 --- a/moto/ecs/exceptions.py +++ b/moto/ecs/exceptions.py @@ -5,11 +5,9 @@ from moto.core.exceptions import RESTError, JsonRESTError class ServiceNotFoundException(RESTError): code = 400 - def __init__(self, service_name): + def __init__(self): super(ServiceNotFoundException, self).__init__( - error_type="ServiceNotFoundException", - message="The service {0} does not exist".format(service_name), - template="error_json", + error_type="ServiceNotFoundException", message="Service not found." ) @@ -23,6 +21,15 @@ class TaskDefinitionNotFoundException(JsonRESTError): ) +class RevisionNotFoundException(JsonRESTError): + code = 400 + + def __init__(self): + super(RevisionNotFoundException, self).__init__( + error_type="ClientException", message="Revision is missing.", + ) + + class TaskSetNotFoundException(JsonRESTError): code = 400 @@ -40,3 +47,12 @@ class ClusterNotFoundException(JsonRESTError): super(ClusterNotFoundException, self).__init__( error_type="ClientException", message="Cluster not found", ) + + +class InvalidParameterException(JsonRESTError): + code = 400 + + def __init__(self, message): + super(InvalidParameterException, self).__init__( + error_type="ClientException", message=message, + ) diff --git a/moto/ecs/models.py b/moto/ecs/models.py index a4522660e..bb353d8a5 100644 --- a/moto/ecs/models.py +++ b/moto/ecs/models.py @@ -18,6 +18,8 @@ from .exceptions import ( TaskDefinitionNotFoundException, TaskSetNotFoundException, ClusterNotFoundException, + InvalidParameterException, + RevisionNotFoundException, ) @@ -668,7 +670,7 @@ class EC2ContainerServiceBackend(BaseBackend): if cluster_name in self.clusters: return self.clusters.pop(cluster_name) else: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException def register_task_definition( self, @@ -713,25 +715,30 @@ class EC2ContainerServiceBackend(BaseBackend): def deregister_task_definition(self, task_definition_str): task_definition_name = task_definition_str.split("/")[-1] - family, revision = task_definition_name.split(":") - revision = int(revision) + try: + family, revision = task_definition_name.split(":") + except ValueError: + raise RevisionNotFoundException + try: + revision = int(revision) + except ValueError: + raise InvalidParameterException( + "Invalid revision number. Number: " + revision + ) if ( family in self.task_definitions and revision in self.task_definitions[family] ): return self.task_definitions[family].pop(revision) else: - raise Exception("{0} is not a task_definition".format(task_definition_name)) + raise TaskDefinitionNotFoundException def run_task(self, cluster_str, task_definition_str, count, overrides, started_by): - if cluster_str: - cluster_name = cluster_str.split("/")[-1] - else: - cluster_name = "default" + cluster_name = cluster_str.split("/")[-1] if cluster_name in self.clusters: cluster = self.clusters[cluster_name] else: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException task_definition = self.describe_task_definition(task_definition_str) if cluster_name not in self.tasks: self.tasks[cluster_name] = {} @@ -862,13 +869,13 @@ class EC2ContainerServiceBackend(BaseBackend): if cluster_name in self.clusters: cluster = self.clusters[cluster_name] else: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException task_definition = self.describe_task_definition(task_definition_str) if cluster_name not in self.tasks: self.tasks[cluster_name] = {} tasks = [] if not container_instances: - raise Exception("No container instance list provided") + raise InvalidParameterException("Container Instances cannot be empty.") container_instance_ids = [x.split("/")[-1] for x in container_instances] resource_requirements = self._calculate_task_resource_requirements( @@ -898,9 +905,9 @@ class EC2ContainerServiceBackend(BaseBackend): if cluster_name in self.clusters: cluster = self.clusters[cluster_name] else: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException if not tasks: - raise Exception("tasks cannot be empty") + raise InvalidParameterException("Tasks cannot be empty.") response = [] for cluster, cluster_tasks in self.tasks.items(): for task_arn, task in cluster_tasks.items(): @@ -929,7 +936,7 @@ class EC2ContainerServiceBackend(BaseBackend): if cluster_str: cluster_name = cluster_str.split("/")[-1] if cluster_name not in self.clusters: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException filtered_tasks = list( filter(lambda t: cluster_name in t.cluster_arn, filtered_tasks) ) @@ -971,10 +978,8 @@ class EC2ContainerServiceBackend(BaseBackend): def stop_task(self, cluster_str, task_str, reason): cluster_name = cluster_str.split("/")[-1] if cluster_name not in self.clusters: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException - if not task_str: - raise Exception("A task ID or ARN is required") task_id = task_str.split("/")[-1] tasks = self.tasks.get(cluster_name, None) if not tasks: @@ -1011,7 +1016,7 @@ class EC2ContainerServiceBackend(BaseBackend): if cluster_name in self.clusters: cluster = self.clusters[cluster_name] else: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException if task_definition_str is not None: task_definition = self.describe_task_definition(task_definition_str) else: @@ -1069,6 +1074,8 @@ class EC2ContainerServiceBackend(BaseBackend): 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 service_name = service_str.split("/")[-1] cluster_service_pair = "{0}:{1}".format(cluster_name, service_name) if cluster_service_pair in self.services: @@ -1081,22 +1088,23 @@ class EC2ContainerServiceBackend(BaseBackend): self.services[cluster_service_pair].desired_count = desired_count return self.services[cluster_service_pair] else: - raise ServiceNotFoundException(service_name) + raise ServiceNotFoundException def delete_service(self, cluster_name, service_name): cluster_service_pair = "{0}:{1}".format(cluster_name, service_name) + if cluster_name not in self.clusters: + raise ClusterNotFoundException + if cluster_service_pair in self.services: service = self.services[cluster_service_pair] if service.desired_count > 0: - raise Exception("Service must have desiredCount=0") + raise InvalidParameterException( + "The service cannot be stopped while it is scaled above 0." + ) else: return self.services.pop(cluster_service_pair) else: - raise Exception( - "cluster {0} or service {1} does not exist".format( - cluster_name, service_name - ) - ) + raise ServiceNotFoundException def register_container_instance(self, cluster_str, ec2_instance_id): cluster_name = cluster_str.split("/")[-1] @@ -1125,11 +1133,9 @@ class EC2ContainerServiceBackend(BaseBackend): 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 Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException if not list_container_instance_ids: - raise JsonRESTError( - "InvalidParameterException", "Container instance cannot be empty" - ) + raise InvalidParameterException("Container Instances cannot be empty.") failures = [] container_instance_objects = [] for container_instance_id in list_container_instance_ids: @@ -1153,12 +1159,11 @@ 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)) + raise ClusterNotFoundException status = status.upper() if status not in ["ACTIVE", "DRAINING"]: - raise Exception( - "An error occurred (InvalidParameterException) when calling the UpdateContainerInstancesState operation: \ - Container instances status should be one of [ACTIVE,DRAINING]" + raise InvalidParameterException( + "Container instance status should be one of [ACTIVE, DRAINING]" ) failures = [] container_instance_objects = [] @@ -1208,7 +1213,7 @@ class EC2ContainerServiceBackend(BaseBackend): failures = [] cluster_name = cluster_str.split("/")[-1] if cluster_name not in self.clusters: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException container_instance_id = container_instance_str.split("/")[-1] container_instance = self.container_instances[cluster_name].get( container_instance_id @@ -1232,7 +1237,7 @@ class EC2ContainerServiceBackend(BaseBackend): def _respond_to_cluster_state_update(self, cluster_str): cluster_name = cluster_str.split("/")[-1] if cluster_name not in self.clusters: - raise Exception("{0} is not a cluster".format(cluster_name)) + raise ClusterNotFoundException pass def put_attributes(self, cluster_name, attributes=None): @@ -1240,9 +1245,7 @@ class EC2ContainerServiceBackend(BaseBackend): raise ClusterNotFoundException if attributes is None: - raise JsonRESTError( - "InvalidParameterException", "attributes value is required" - ) + raise InvalidParameterException("attributes can not be empty") for attr in attributes: self._put_attribute( @@ -1413,7 +1416,7 @@ class EC2ContainerServiceBackend(BaseBackend): if service.arn == resource_arn: return service.tags else: - raise ServiceNotFoundException(service_name=parsed_arn["id"]) + raise ServiceNotFoundException raise NotImplementedError() def _get_last_task_definition_revision_id(self, family): @@ -1430,7 +1433,7 @@ class EC2ContainerServiceBackend(BaseBackend): service.tags = self._merge_tags(service.tags, tags) return {} else: - raise ServiceNotFoundException(service_name=parsed_arn["id"]) + raise ServiceNotFoundException raise NotImplementedError() def _merge_tags(self, existing_tags, new_tags): @@ -1456,7 +1459,7 @@ class EC2ContainerServiceBackend(BaseBackend): ] return {} else: - raise ServiceNotFoundException(service_name=parsed_arn["id"]) + raise ServiceNotFoundException raise NotImplementedError() def create_task_set( @@ -1497,7 +1500,7 @@ class EC2ContainerServiceBackend(BaseBackend): service_obj = self.services.get("{0}:{1}".format(cluster_name, service_name)) if not service_obj: - raise ServiceNotFoundException(service_name=service_name) + raise ServiceNotFoundException cluster_obj = self.clusters.get(cluster_name) if not cluster_obj: @@ -1522,7 +1525,7 @@ class EC2ContainerServiceBackend(BaseBackend): service_obj = self.services.get(service_key) if not service_obj: - raise ServiceNotFoundException(service_name=service_name) + raise ServiceNotFoundException cluster_obj = self.clusters.get(cluster_name) if not cluster_obj: diff --git a/moto/ecs/responses.py b/moto/ecs/responses.py index 15d2f0c4b..1c7e459c4 100644 --- a/moto/ecs/responses.py +++ b/moto/ecs/responses.py @@ -100,7 +100,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"taskDefinition": task_definition.response_object}) def run_task(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") overrides = self._get_param("overrides") task_definition_str = self._get_param("taskDefinition") count = self._get_int_param("count") @@ -113,7 +113,7 @@ class EC2ContainerServiceResponse(BaseResponse): ) def describe_tasks(self): - cluster = self._get_param("cluster") + cluster = self._get_param("cluster", "default") tasks = self._get_param("tasks") data = self.ecs_backend.describe_tasks(cluster, tasks) return json.dumps( @@ -121,7 +121,7 @@ class EC2ContainerServiceResponse(BaseResponse): ) def start_task(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") overrides = self._get_param("overrides") task_definition_str = self._get_param("taskDefinition") container_instances = self._get_param("containerInstances") @@ -134,7 +134,7 @@ class EC2ContainerServiceResponse(BaseResponse): ) def list_tasks(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") container_instance = self._get_param("containerInstance") family = self._get_param("family") started_by = self._get_param("startedBy") @@ -151,14 +151,14 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"taskArns": task_arns}) def stop_task(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") task = self._get_param("task") reason = self._get_param("reason") task = self.ecs_backend.stop_task(cluster_str, task, reason) return json.dumps({"task": task.response_object}) def create_service(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") service_name = self._get_param("serviceName") task_definition_str = self._get_param("taskDefinition") desired_count = self._get_int_param("desiredCount") @@ -179,7 +179,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"service": service.response_object}) def list_services(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") scheduling_strategy = self._get_param("schedulingStrategy") service_arns = self.ecs_backend.list_services(cluster_str, scheduling_strategy) return json.dumps( @@ -191,7 +191,7 @@ class EC2ContainerServiceResponse(BaseResponse): ) def describe_services(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") service_names = self._get_param("services") services = self.ecs_backend.describe_services(cluster_str, service_names) resp = { @@ -206,7 +206,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps(resp) def update_service(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") service_name = self._get_param("service") task_definition = self._get_param("taskDefinition") desired_count = self._get_int_param("desiredCount") @@ -217,12 +217,12 @@ class EC2ContainerServiceResponse(BaseResponse): def delete_service(self): service_name = self._get_param("service") - cluster_name = self._get_param("cluster") + cluster_name = self._get_param("cluster", "default") service = self.ecs_backend.delete_service(cluster_name, service_name) return json.dumps({"service": service.response_object}) def register_container_instance(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") instance_identity_document_str = self._get_param("instanceIdentityDocument") instance_identity_document = json.loads(instance_identity_document_str) ec2_instance_id = instance_identity_document["instanceId"] @@ -232,9 +232,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"containerInstance": container_instance.response_object}) def deregister_container_instance(self): - cluster_str = self._get_param("cluster") - if not cluster_str: - cluster_str = "default" + cluster_str = self._get_param("cluster", "default") container_instance_str = self._get_param("containerInstance") force = self._get_param("force") container_instance, failures = self.ecs_backend.deregister_container_instance( @@ -243,12 +241,12 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"containerInstance": container_instance.response_object}) def list_container_instances(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") container_instance_arns = self.ecs_backend.list_container_instances(cluster_str) return json.dumps({"containerInstanceArns": container_instance_arns}) def describe_container_instances(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") list_container_instance_arns = self._get_param("containerInstances") container_instances, failures = self.ecs_backend.describe_container_instances( cluster_str, list_container_instance_arns @@ -263,7 +261,7 @@ class EC2ContainerServiceResponse(BaseResponse): ) def update_container_instances_state(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") list_container_instance_arns = self._get_param("containerInstances") status_str = self._get_param("status") ( @@ -312,7 +310,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"attributes": formatted_results}) def delete_attributes(self): - cluster_name = self._get_param("cluster") + cluster_name = self._get_param("cluster", "default") attributes = self._get_param("attributes") self.ecs_backend.delete_attributes(cluster_name, attributes) @@ -359,7 +357,7 @@ class EC2ContainerServiceResponse(BaseResponse): def create_task_set(self): service_str = self._get_param("service") - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") task_definition = self._get_param("taskDefinition") external_id = self._get_param("externalId") network_configuration = self._get_param("networkConfiguration") @@ -389,7 +387,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"taskSet": task_set.response_object}) def describe_task_sets(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") service_str = self._get_param("service") task_sets = self._get_param("taskSets") include = self._get_param("include", []) @@ -414,7 +412,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"taskSet": task_set.response_object}) def update_task_set(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") service_str = self._get_param("service") task_set = self._get_param("taskSet") scale = self._get_param("scale") @@ -425,7 +423,7 @@ class EC2ContainerServiceResponse(BaseResponse): return json.dumps({"taskSet": task_set.response_object}) def update_service_primary_task_set(self): - cluster_str = self._get_param("cluster") + cluster_str = self._get_param("cluster", "default") service_str = self._get_param("service") primary_task_set = self._get_param("primaryTaskSet") diff --git a/tests/test_ecs/test_ecs_boto3.py b/tests/test_ecs/test_ecs_boto3.py index 8b6b27987..1649b1753 100644 --- a/tests/test_ecs/test_ecs_boto3.py +++ b/tests/test_ecs/test_ecs_boto3.py @@ -10,6 +10,13 @@ from uuid import UUID from moto import mock_ecs from moto import mock_ec2 +from moto.ecs.exceptions import ( + ClusterNotFoundException, + ServiceNotFoundException, + InvalidParameterException, + TaskDefinitionNotFoundException, + RevisionNotFoundException, +) import pytest @@ -73,6 +80,14 @@ def test_delete_cluster(): len(response["clusterArns"]).should.equal(0) +@mock_ecs +def test_delete_cluster_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + client.delete_cluster.when.called_with(cluster="not_a_cluster").should.throw( + ClientError, ClusterNotFoundException().message + ) + + @mock_ecs def test_register_task_definition(): client = boto3.client("ecs", region_name="us-east-1") @@ -347,6 +362,23 @@ def test_deregister_task_definition(): ].should.equal("json-file") +@mock_ecs +def test_deregister_task_definition(): + client = boto3.client("ecs", region_name="us-east-1") + client.deregister_task_definition.when.called_with( + taskDefinition="fake_task" + ).should.throw(ClientError, RevisionNotFoundException().message) + client.deregister_task_definition.when.called_with( + taskDefinition="fake_task:foo" + ).should.throw( + ClientError, + InvalidParameterException("Invalid revision number. Number: foo").message, + ) + client.deregister_task_definition.when.called_with( + taskDefinition="fake_task:1" + ).should.throw(ClientError, TaskDefinitionNotFoundException().message) + + @mock_ecs def test_create_service(): client = boto3.client("ecs", region_name="us-east-1") @@ -751,17 +783,56 @@ def test_delete_service(): @mock_ecs -def test_update_non_existent_service(): +def test_delete_service_exceptions(): client = boto3.client("ecs", region_name="us-east-1") - try: - client.update_service( - cluster="my-clustet", service="my-service", desiredCount=0 - ) - except ClientError as exc: - error_code = exc.response["Error"]["Code"] - error_code.should.equal("ServiceNotFoundException") - else: - raise Exception("Didn't raise ClientError") + + # Raises ClusterNotFoundException because "default" is not a cluster + client.delete_service.when.called_with(service="not_as_service").should.throw( + ClientError, ClusterNotFoundException().message + ) + + _ = client.create_cluster() + client.delete_service.when.called_with(service="not_as_service").should.throw( + ClientError, ServiceNotFoundException().message + ) + + _ = client.register_task_definition( + family="test_ecs_task", + containerDefinitions=[ + { + "name": "hello_world", + "image": "docker/hello-world:latest", + "cpu": 1024, + "memory": 400, + } + ], + ) + + _ = client.create_service( + serviceName="test_ecs_service", taskDefinition="test_ecs_task", desiredCount=1, + ) + + client.delete_service.when.called_with(service="test_ecs_service").should.throw( + ClientError, + InvalidParameterException( + "The service cannot be stopped while it is scaled above 0." + ).message, + ) + + +@mock_ecs +def test_update_service_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + + client.update_service.when.called_with( + service="not_a_service", desiredCount=0 + ).should.throw(ClientError, ClusterNotFoundException().message) + + _ = client.create_cluster() + + client.update_service.when.called_with( + service="not_a_service", desiredCount=0 + ).should.throw(ClientError, ServiceNotFoundException().message) @mock_ec2 @@ -958,6 +1029,23 @@ def test_describe_container_instances(): ) +@mock_ecs +def test_describe_container_instances_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + + client.describe_container_instances.when.called_with( + containerInstances=[] + ).should.throw(ClientError, ClusterNotFoundException().message) + + _ = client.create_cluster() + client.describe_container_instances.when.called_with( + containerInstances=[] + ).should.throw( + ClientError, + InvalidParameterException("Container Instances cannot be empty.").message, + ) + + @mock_ec2 @mock_ecs def test_update_container_instances_state(): @@ -1213,6 +1301,26 @@ def test_run_task_default_cluster(): response["tasks"][0]["stoppedReason"].should.equal("") +@mock_ecs +def test_run_task_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + _ = client.register_task_definition( + family="test_ecs_task", + containerDefinitions=[ + { + "name": "hello_world", + "image": "docker/hello-world:latest", + "cpu": 1024, + "memory": 400, + } + ], + ) + + client.run_task.when.called_with( + cluster="not_a_cluster", taskDefinition="test_ecs_task" + ).should.throw(ClientError, ClusterNotFoundException().message) + + @mock_ec2 @mock_ecs def test_start_task(): @@ -1287,15 +1395,40 @@ def test_start_task(): response["tasks"][0]["stoppedReason"].should.equal("") +@mock_ecs +def test_start_task_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + _ = client.register_task_definition( + family="test_ecs_task", + containerDefinitions=[ + { + "name": "hello_world", + "image": "docker/hello-world:latest", + "cpu": 1024, + "memory": 400, + } + ], + ) + + client.start_task.when.called_with( + taskDefinition="test_ecs_task", containerInstances=["not_a_container_instance"] + ).should.throw(ClientError, ClusterNotFoundException().message) + + _ = client.create_cluster() + client.start_task.when.called_with( + taskDefinition="test_ecs_task", containerInstances=[] + ).should.throw( + ClientError, InvalidParameterException("Container Instances cannot be empty.") + ) + + @mock_ec2 @mock_ecs def test_list_tasks(): client = boto3.client("ecs", region_name="us-east-1") ec2 = boto3.resource("ec2", region_name="us-east-1") - test_cluster_name = "test_ecs_cluster" - - _ = client.create_cluster(clusterName=test_cluster_name) + _ = client.create_cluster() test_instance = ec2.create_instances( ImageId="ami-1234abcd", MinCount=1, MaxCount=1 @@ -1306,10 +1439,10 @@ def test_list_tasks(): ) _ = client.register_container_instance( - cluster=test_cluster_name, instanceIdentityDocument=instance_id_document + instanceIdentityDocument=instance_id_document ) - container_instances = client.list_container_instances(cluster=test_cluster_name) + container_instances = client.list_container_instances() container_instance_id = container_instances["containerInstanceArns"][0].split("/")[ -1 ] @@ -1332,7 +1465,6 @@ def test_list_tasks(): ) _ = client.start_task( - cluster="test_ecs_cluster", taskDefinition="test_ecs_task", overrides={}, containerInstances=[container_instance_id], @@ -1340,7 +1472,6 @@ def test_list_tasks(): ) _ = client.start_task( - cluster="test_ecs_cluster", taskDefinition="test_ecs_task", overrides={}, containerInstances=[container_instance_id], @@ -1348,12 +1479,17 @@ def test_list_tasks(): ) assert len(client.list_tasks()["taskArns"]).should.equal(2) - assert len(client.list_tasks(cluster="test_ecs_cluster")["taskArns"]).should.equal( - 2 - ) assert len(client.list_tasks(startedBy="foo")["taskArns"]).should.equal(1) +@mock_ecs +def test_list_tasks_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + client.list_tasks.when.called_with(cluster="not_a_cluster").should.throw( + ClientError, ClusterNotFoundException().message + ) + + @mock_ec2 @mock_ecs def test_describe_tasks(): @@ -1416,6 +1552,20 @@ def test_describe_tasks(): len(response["tasks"]).should.equal(1) +@mock_ecs +def test_describe_tasks_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + + client.describe_tasks.when.called_with(tasks=[]).should.throw( + ClientError, ClusterNotFoundException().message + ) + + _ = client.create_cluster() + client.describe_tasks.when.called_with(tasks=[]).should.throw( + ClientError, InvalidParameterException("Tasks cannot be empty.").message + ) + + @mock_ecs def describe_task_definition(): client = boto3.client("ecs", region_name="us-east-1") @@ -1499,6 +1649,15 @@ def test_stop_task(): stop_response["task"]["stoppedReason"].should.equal("moto testing") +@mock_ecs +def test_stop_task_exceptions(): + client = boto3.client("ecs", region_name="us-east-1") + + client.stop_task.when.called_with(task="fake_task").should.throw( + ClientError, ClusterNotFoundException().message + ) + + @mock_ec2 @mock_ecs def test_resource_reservation_and_release(): @@ -2160,13 +2319,14 @@ def test_list_tags_for_resource(): @mock_ecs -def test_list_tags_for_resource_unknown(): +def test_list_tags_exceptions(): client = boto3.client("ecs", region_name="us-east-1") - task_definition_arn = "arn:aws:ecs:us-east-1:012345678910:task-definition/unknown:1" - try: - client.list_tags_for_resource(resourceArn=task_definition_arn) - except ClientError as err: - err.response["Error"]["Code"].should.equal("ClientException") + client.list_tags_for_resource.when.called_with( + resourceArn="arn:aws:ecs:us-east-1:012345678910:service/fake_service:1" + ).should.throw(ClientError, ServiceNotFoundException().message) + client.list_tags_for_resource.when.called_with( + resourceArn="arn:aws:ecs:us-east-1:012345678910:task-definition/fake_task:1" + ).should.throw(ClientError, TaskDefinitionNotFoundException().message) @mock_ecs @@ -2208,16 +2368,6 @@ def test_list_tags_for_resource_ecs_service(): ) -@mock_ecs -def test_list_tags_for_resource_unknown_service(): - client = boto3.client("ecs", region_name="us-east-1") - service_arn = "arn:aws:ecs:us-east-1:012345678910:service/unknown:1" - try: - client.list_tags_for_resource(resourceArn=service_arn) - except ClientError as err: - err.response["Error"]["Code"].should.equal("ServiceNotFoundException") - - @mock_ecs def test_ecs_service_tag_resource(): client = boto3.client("ecs", region_name="us-east-1") @@ -2816,30 +2966,68 @@ def test_list_tasks_with_filters(): startedBy="bar", ) - len(ecs.list_tasks()["taskArns"]).should.equal(3) - len(ecs.list_tasks(cluster="test_cluster_1")["taskArns"]).should.equal(2) len(ecs.list_tasks(cluster="test_cluster_2")["taskArns"]).should.equal(1) - len(ecs.list_tasks(containerInstance="bad-id")["taskArns"]).should.equal(0) - len(ecs.list_tasks(containerInstance=container_id_1)["taskArns"]).should.equal(2) - len(ecs.list_tasks(containerInstance=container_id_2)["taskArns"]).should.equal(1) + len( + ecs.list_tasks(cluster="test_cluster_1", containerInstance="bad-id")["taskArns"] + ).should.equal(0) + len( + ecs.list_tasks(cluster="test_cluster_1", containerInstance=container_id_1)[ + "taskArns" + ] + ).should.equal(2) + len( + ecs.list_tasks(cluster="test_cluster_2", containerInstance=container_id_2)[ + "taskArns" + ] + ).should.equal(1) - len(ecs.list_tasks(family="non-existent-family")["taskArns"]).should.equal(0) - len(ecs.list_tasks(family="test_task_def_1")["taskArns"]).should.equal(2) - len(ecs.list_tasks(family="test_task_def_2")["taskArns"]).should.equal(1) + len( + ecs.list_tasks(cluster="test_cluster_1", family="non-existent-family")[ + "taskArns" + ] + ).should.equal(0) + len( + ecs.list_tasks(cluster="test_cluster_1", family="test_task_def_1")["taskArns"] + ).should.equal(2) + len( + ecs.list_tasks(cluster="test_cluster_2", family="test_task_def_2")["taskArns"] + ).should.equal(1) - len(ecs.list_tasks(startedBy="non-existent-entity")["taskArns"]).should.equal(0) - len(ecs.list_tasks(startedBy="foo")["taskArns"]).should.equal(2) - len(ecs.list_tasks(startedBy="bar")["taskArns"]).should.equal(1) + len( + ecs.list_tasks(cluster="test_cluster_1", startedBy="non-existent-entity")[ + "taskArns" + ] + ).should.equal(0) + len( + ecs.list_tasks(cluster="test_cluster_1", startedBy="foo")["taskArns"] + ).should.equal(1) + len( + ecs.list_tasks(cluster="test_cluster_1", startedBy="bar")["taskArns"] + ).should.equal(1) + len( + ecs.list_tasks(cluster="test_cluster_2", startedBy="foo")["taskArns"] + ).should.equal(1) - len(ecs.list_tasks(desiredStatus="RUNNING")["taskArns"]).should.equal(3) + len( + ecs.list_tasks(cluster="test_cluster_1", desiredStatus="RUNNING")["taskArns"] + ).should.equal(2) + len( + ecs.list_tasks(cluster="test_cluster_2", desiredStatus="RUNNING")["taskArns"] + ).should.equal(1) _ = ecs.stop_task(cluster="test_cluster_2", task=task_to_stop, reason="for testing") - len(ecs.list_tasks(desiredStatus="RUNNING")["taskArns"]).should.equal(2) - len(ecs.list_tasks(desiredStatus="STOPPED")["taskArns"]).should.equal(1) + len( + ecs.list_tasks(cluster="test_cluster_1", desiredStatus="RUNNING")["taskArns"] + ).should.equal(2) + len( + ecs.list_tasks(cluster="test_cluster_2", desiredStatus="STOPPED")["taskArns"] + ).should.equal(1) resp = ecs.list_tasks(cluster="test_cluster_1", startedBy="foo") len(resp["taskArns"]).should.equal(1) - resp = ecs.list_tasks(containerInstance=container_id_1, startedBy="bar") + resp = ecs.list_tasks( + cluster="test_cluster_1", containerInstance=container_id_1, startedBy="bar" + ) len(resp["taskArns"]).should.equal(1)