1from datetime import datetime
2
3from botocore.exceptions import ClientError
4import boto3
5import mock
6import sure  # noqa # pylint: disable=unused-import
7import json
8import os
9
10from moto.core import ACCOUNT_ID
11from moto.ec2 import utils as ec2_utils
12from uuid import UUID
13
14from moto import mock_ecs, mock_ec2, settings
15from moto.ecs.exceptions import (
16    ClusterNotFoundException,
17    ServiceNotFoundException,
18    InvalidParameterException,
19    TaskDefinitionNotFoundException,
20    RevisionNotFoundException,
21)
22import pytest
23from tests import EXAMPLE_AMI_ID
24from unittest import SkipTest
25
26
27@mock_ecs
28def test_create_cluster():
29    client = boto3.client("ecs", region_name="us-east-1")
30    response = client.create_cluster(clusterName="test_ecs_cluster")
31    response["cluster"]["clusterName"].should.equal("test_ecs_cluster")
32    response["cluster"]["clusterArn"].should.equal(
33        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
34    )
35    response["cluster"]["status"].should.equal("ACTIVE")
36    response["cluster"]["registeredContainerInstancesCount"].should.equal(0)
37    response["cluster"]["runningTasksCount"].should.equal(0)
38    response["cluster"]["pendingTasksCount"].should.equal(0)
39    response["cluster"]["activeServicesCount"].should.equal(0)
40
41
42@mock_ecs
43def test_list_clusters():
44    client = boto3.client("ecs", region_name="us-east-2")
45    _ = client.create_cluster(clusterName="test_cluster0")
46    _ = client.create_cluster(clusterName="test_cluster1")
47    response = client.list_clusters()
48    response["clusterArns"].should.contain(
49        "arn:aws:ecs:us-east-2:{}:cluster/test_cluster0".format(ACCOUNT_ID)
50    )
51    response["clusterArns"].should.contain(
52        "arn:aws:ecs:us-east-2:{}:cluster/test_cluster1".format(ACCOUNT_ID)
53    )
54
55
56@mock_ecs
57def test_describe_clusters():
58    client = boto3.client("ecs", region_name="us-east-1")
59    response = client.describe_clusters(clusters=["some-cluster"])
60    response["failures"].should.contain(
61        {
62            "arn": "arn:aws:ecs:us-east-1:{}:cluster/some-cluster".format(ACCOUNT_ID),
63            "reason": "MISSING",
64        }
65    )
66
67
68@mock_ecs
69def test_delete_cluster():
70    client = boto3.client("ecs", region_name="us-east-1")
71    _ = client.create_cluster(clusterName="test_ecs_cluster")
72    response = client.delete_cluster(cluster="test_ecs_cluster")
73    response["cluster"]["clusterName"].should.equal("test_ecs_cluster")
74    response["cluster"]["clusterArn"].should.equal(
75        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
76    )
77    response["cluster"]["status"].should.equal("ACTIVE")
78    response["cluster"]["registeredContainerInstancesCount"].should.equal(0)
79    response["cluster"]["runningTasksCount"].should.equal(0)
80    response["cluster"]["pendingTasksCount"].should.equal(0)
81    response["cluster"]["activeServicesCount"].should.equal(0)
82
83    response = client.list_clusters()
84    len(response["clusterArns"]).should.equal(0)
85
86
87@mock_ecs
88def test_delete_cluster_exceptions():
89    client = boto3.client("ecs", region_name="us-east-1")
90    client.delete_cluster.when.called_with(cluster="not_a_cluster").should.throw(
91        ClientError, ClusterNotFoundException().message
92    )
93
94
95@mock_ecs
96def test_register_task_definition():
97    client = boto3.client("ecs", region_name="us-east-1")
98    # Registering with minimal definition
99    definition = dict(
100        family="test_ecs_task",
101        containerDefinitions=[
102            {"name": "hello_world", "image": "hello-world:latest", "memory": 400,}
103        ],
104    )
105
106    response = client.register_task_definition(**definition)
107
108    response["taskDefinition"] = response["taskDefinition"]
109    response["taskDefinition"]["family"].should.equal("test_ecs_task")
110    response["taskDefinition"]["revision"].should.equal(1)
111    response["taskDefinition"]["taskDefinitionArn"].should.equal(
112        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
113    )
114    response["taskDefinition"]["networkMode"].should.equal("bridge")
115    response["taskDefinition"]["volumes"].should.equal([])
116    response["taskDefinition"]["placementConstraints"].should.equal([])
117    response["taskDefinition"]["compatibilities"].should.equal(["EC2"])
118    response["taskDefinition"].shouldnt.have.key("requiresCompatibilities")
119    response["taskDefinition"].shouldnt.have.key("cpu")
120    response["taskDefinition"].shouldnt.have.key("memory")
121
122    response["taskDefinition"]["containerDefinitions"][0]["name"].should.equal(
123        "hello_world"
124    )
125    response["taskDefinition"]["containerDefinitions"][0]["image"].should.equal(
126        "hello-world:latest"
127    )
128    response["taskDefinition"]["containerDefinitions"][0]["cpu"].should.equal(0)
129    response["taskDefinition"]["containerDefinitions"][0]["portMappings"].should.equal(
130        []
131    )
132    response["taskDefinition"]["containerDefinitions"][0]["essential"].should.equal(
133        True
134    )
135    response["taskDefinition"]["containerDefinitions"][0]["environment"].should.equal(
136        []
137    )
138    response["taskDefinition"]["containerDefinitions"][0]["mountPoints"].should.equal(
139        []
140    )
141    response["taskDefinition"]["containerDefinitions"][0]["volumesFrom"].should.equal(
142        []
143    )
144
145    # Registering again increments the revision
146    response = client.register_task_definition(**definition)
147
148    response["taskDefinition"]["revision"].should.equal(2)
149    response["taskDefinition"]["taskDefinitionArn"].should.equal(
150        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:2".format(ACCOUNT_ID)
151    )
152
153    # Registering with optional top-level params
154    definition["requiresCompatibilities"] = ["FARGATE"]
155    definition["taskRoleArn"] = "my-custom-task-role-arn"
156    definition["executionRoleArn"] = "my-custom-execution-role-arn"
157    response = client.register_task_definition(**definition)
158    response["taskDefinition"]["requiresCompatibilities"].should.equal(["FARGATE"])
159    response["taskDefinition"]["compatibilities"].should.equal(["EC2", "FARGATE"])
160    response["taskDefinition"]["networkMode"].should.equal("awsvpc")
161    response["taskDefinition"]["taskRoleArn"].should.equal("my-custom-task-role-arn")
162    response["taskDefinition"]["executionRoleArn"].should.equal(
163        "my-custom-execution-role-arn"
164    )
165
166    definition["requiresCompatibilities"] = ["EC2", "FARGATE"]
167    response = client.register_task_definition(**definition)
168    response["taskDefinition"]["requiresCompatibilities"].should.equal(
169        ["EC2", "FARGATE"]
170    )
171    response["taskDefinition"]["compatibilities"].should.equal(["EC2", "FARGATE"])
172    response["taskDefinition"]["networkMode"].should.equal("awsvpc")
173
174    definition["cpu"] = "512"
175    response = client.register_task_definition(**definition)
176    response["taskDefinition"]["cpu"].should.equal("512")
177
178    definition.update({"memory": "512"})
179    response = client.register_task_definition(**definition)
180    response["taskDefinition"]["memory"].should.equal("512")
181
182    # Registering with optional container params
183    definition["containerDefinitions"][0]["cpu"] = 512
184    response = client.register_task_definition(**definition)
185    response["taskDefinition"]["containerDefinitions"][0]["cpu"].should.equal(512)
186
187    definition["containerDefinitions"][0]["essential"] = False
188    response = client.register_task_definition(**definition)
189    response["taskDefinition"]["containerDefinitions"][0]["essential"].should.equal(
190        False
191    )
192
193    definition["containerDefinitions"][0]["environment"] = [
194        {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
195    ]
196    response = client.register_task_definition(**definition)
197    response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
198        "name"
199    ].should.equal("AWS_ACCESS_KEY_ID")
200    response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
201        "value"
202    ].should.equal("SOME_ACCESS_KEY")
203
204    definition["containerDefinitions"][0]["logConfiguration"] = {
205        "logDriver": "json-file"
206    }
207    response = client.register_task_definition(**definition)
208    response["taskDefinition"]["containerDefinitions"][0]["logConfiguration"][
209        "logDriver"
210    ].should.equal("json-file")
211
212
213@mock_ecs
214def test_list_task_definitions():
215    client = boto3.client("ecs", region_name="us-east-1")
216    _ = client.register_task_definition(
217        family="test_ecs_task",
218        containerDefinitions=[
219            {
220                "name": "hello_world",
221                "image": "docker/hello-world:latest",
222                "cpu": 1024,
223                "memory": 400,
224                "essential": True,
225                "environment": [
226                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
227                ],
228                "logConfiguration": {"logDriver": "json-file"},
229            }
230        ],
231    )
232    _ = client.register_task_definition(
233        family="test_ecs_task",
234        containerDefinitions=[
235            {
236                "name": "hello_world2",
237                "image": "docker/hello-world2:latest",
238                "cpu": 1024,
239                "memory": 400,
240                "essential": True,
241                "environment": [
242                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY2"}
243                ],
244                "logConfiguration": {"logDriver": "json-file"},
245            }
246        ],
247    )
248    response = client.list_task_definitions()
249    len(response["taskDefinitionArns"]).should.equal(2)
250    response["taskDefinitionArns"][0].should.equal(
251        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
252    )
253    response["taskDefinitionArns"][1].should.equal(
254        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:2".format(ACCOUNT_ID)
255    )
256
257
258@mock_ecs
259def test_list_task_definitions_with_family_prefix():
260    client = boto3.client("ecs", region_name="us-east-1")
261    _ = client.register_task_definition(
262        family="test_ecs_task_a",
263        containerDefinitions=[
264            {
265                "name": "hello_world",
266                "image": "docker/hello-world:latest",
267                "cpu": 1024,
268                "memory": 400,
269                "essential": True,
270                "environment": [
271                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
272                ],
273                "logConfiguration": {"logDriver": "json-file"},
274            }
275        ],
276    )
277    _ = client.register_task_definition(
278        family="test_ecs_task_a",
279        containerDefinitions=[
280            {
281                "name": "hello_world",
282                "image": "docker/hello-world:latest",
283                "cpu": 1024,
284                "memory": 400,
285                "essential": True,
286                "environment": [
287                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
288                ],
289                "logConfiguration": {"logDriver": "json-file"},
290            }
291        ],
292    )
293    _ = client.register_task_definition(
294        family="test_ecs_task_b",
295        containerDefinitions=[
296            {
297                "name": "hello_world2",
298                "image": "docker/hello-world2:latest",
299                "cpu": 1024,
300                "memory": 400,
301                "essential": True,
302                "environment": [
303                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY2"}
304                ],
305                "logConfiguration": {"logDriver": "json-file"},
306            }
307        ],
308    )
309    empty_response = client.list_task_definitions(familyPrefix="test_ecs_task")
310    len(empty_response["taskDefinitionArns"]).should.equal(0)
311    filtered_response = client.list_task_definitions(familyPrefix="test_ecs_task_a")
312    len(filtered_response["taskDefinitionArns"]).should.equal(2)
313    filtered_response["taskDefinitionArns"][0].should.equal(
314        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task_a:1".format(ACCOUNT_ID)
315    )
316    filtered_response["taskDefinitionArns"][1].should.equal(
317        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task_a:2".format(ACCOUNT_ID)
318    )
319
320
321@mock_ecs
322def test_describe_task_definitions():
323    client = boto3.client("ecs", region_name="us-east-1")
324    _ = client.register_task_definition(
325        family="test_ecs_task",
326        containerDefinitions=[
327            {
328                "name": "hello_world",
329                "image": "docker/hello-world:latest",
330                "cpu": 1024,
331                "memory": 400,
332                "essential": True,
333                "environment": [
334                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
335                ],
336                "logConfiguration": {"logDriver": "json-file"},
337            }
338        ],
339        tags=[{"key": "Name", "value": "test_ecs_task"}],
340    )
341    _ = client.register_task_definition(
342        family="test_ecs_task",
343        taskRoleArn="my-task-role-arn",
344        executionRoleArn="my-execution-role-arn",
345        containerDefinitions=[
346            {
347                "name": "hello_world2",
348                "image": "docker/hello-world2:latest",
349                "cpu": 1024,
350                "memory": 400,
351                "essential": True,
352                "environment": [
353                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY2"}
354                ],
355                "logConfiguration": {"logDriver": "json-file"},
356            }
357        ],
358    )
359    _ = client.register_task_definition(
360        family="test_ecs_task",
361        containerDefinitions=[
362            {
363                "name": "hello_world3",
364                "image": "docker/hello-world3:latest",
365                "cpu": 1024,
366                "memory": 400,
367                "essential": True,
368                "environment": [
369                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY3"}
370                ],
371                "logConfiguration": {"logDriver": "json-file"},
372            }
373        ],
374    )
375    response = client.describe_task_definition(taskDefinition="test_ecs_task")
376    response["taskDefinition"]["taskDefinitionArn"].should.equal(
377        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:3".format(ACCOUNT_ID)
378    )
379
380    response = client.describe_task_definition(taskDefinition="test_ecs_task:2")
381    response["taskDefinition"]["taskDefinitionArn"].should.equal(
382        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:2".format(ACCOUNT_ID)
383    )
384    response["taskDefinition"]["taskRoleArn"].should.equal("my-task-role-arn")
385    response["taskDefinition"]["executionRoleArn"].should.equal("my-execution-role-arn")
386
387    response = client.describe_task_definition(
388        taskDefinition="test_ecs_task:1", include=["TAGS"]
389    )
390    response["tags"].should.equal([{"key": "Name", "value": "test_ecs_task"}])
391
392
393@mock_ecs
394def test_deregister_task_definition_1():
395    client = boto3.client("ecs", region_name="us-east-1")
396    _ = client.register_task_definition(
397        family="test_ecs_task",
398        containerDefinitions=[
399            {
400                "name": "hello_world",
401                "image": "docker/hello-world:latest",
402                "cpu": 1024,
403                "memory": 400,
404                "essential": True,
405                "environment": [
406                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
407                ],
408                "logConfiguration": {"logDriver": "json-file"},
409            }
410        ],
411    )
412    response = client.deregister_task_definition(taskDefinition="test_ecs_task:1")
413    type(response["taskDefinition"]).should.be(dict)
414    response["taskDefinition"]["status"].should.equal("INACTIVE")
415    response["taskDefinition"]["taskDefinitionArn"].should.equal(
416        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
417    )
418    response["taskDefinition"]["containerDefinitions"][0]["name"].should.equal(
419        "hello_world"
420    )
421    response["taskDefinition"]["containerDefinitions"][0]["image"].should.equal(
422        "docker/hello-world:latest"
423    )
424    response["taskDefinition"]["containerDefinitions"][0]["cpu"].should.equal(1024)
425    response["taskDefinition"]["containerDefinitions"][0]["memory"].should.equal(400)
426    response["taskDefinition"]["containerDefinitions"][0]["essential"].should.equal(
427        True
428    )
429    response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
430        "name"
431    ].should.equal("AWS_ACCESS_KEY_ID")
432    response["taskDefinition"]["containerDefinitions"][0]["environment"][0][
433        "value"
434    ].should.equal("SOME_ACCESS_KEY")
435    response["taskDefinition"]["containerDefinitions"][0]["logConfiguration"][
436        "logDriver"
437    ].should.equal("json-file")
438
439
440@mock_ecs
441def test_deregister_task_definition_2():
442    client = boto3.client("ecs", region_name="us-east-1")
443    client.deregister_task_definition.when.called_with(
444        taskDefinition="fake_task"
445    ).should.throw(ClientError, RevisionNotFoundException().message)
446    client.deregister_task_definition.when.called_with(
447        taskDefinition="fake_task:foo"
448    ).should.throw(
449        ClientError,
450        InvalidParameterException("Invalid revision number. Number: foo").message,
451    )
452    client.deregister_task_definition.when.called_with(
453        taskDefinition="fake_task:1"
454    ).should.throw(ClientError, TaskDefinitionNotFoundException().message)
455
456
457@mock_ecs
458def test_create_service():
459    client = boto3.client("ecs", region_name="us-east-1")
460    _ = client.create_cluster(clusterName="test_ecs_cluster")
461    _ = client.register_task_definition(
462        family="test_ecs_task",
463        containerDefinitions=[
464            {
465                "name": "hello_world",
466                "image": "docker/hello-world:latest",
467                "cpu": 1024,
468                "memory": 400,
469                "essential": True,
470                "environment": [
471                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
472                ],
473                "logConfiguration": {"logDriver": "json-file"},
474            }
475        ],
476    )
477    response = client.create_service(
478        cluster="test_ecs_cluster",
479        serviceName="test_ecs_service",
480        taskDefinition="test_ecs_task",
481        desiredCount=2,
482    )
483    response["service"]["clusterArn"].should.equal(
484        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
485    )
486    response["service"]["desiredCount"].should.equal(2)
487    len(response["service"]["events"]).should.equal(0)
488    len(response["service"]["loadBalancers"]).should.equal(0)
489    response["service"]["pendingCount"].should.equal(0)
490    response["service"]["runningCount"].should.equal(0)
491    response["service"]["serviceArn"].should.equal(
492        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
493    )
494    response["service"]["serviceName"].should.equal("test_ecs_service")
495    response["service"]["status"].should.equal("ACTIVE")
496    response["service"]["taskDefinition"].should.equal(
497        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
498    )
499    response["service"]["schedulingStrategy"].should.equal("REPLICA")
500    response["service"]["launchType"].should.equal("EC2")
501
502
503@mock_ecs
504def test_create_service_errors():
505    # given
506    client = boto3.client("ecs", region_name="us-east-1")
507    _ = client.create_cluster(clusterName="test_ecs_cluster")
508    _ = client.register_task_definition(
509        family="test_ecs_task",
510        containerDefinitions=[
511            {
512                "name": "hello_world",
513                "image": "docker/hello-world:latest",
514                "cpu": 1024,
515                "memory": 400,
516                "essential": True,
517                "environment": [
518                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
519                ],
520                "logConfiguration": {"logDriver": "json-file"},
521            }
522        ],
523    )
524
525    # not existing launch type
526    # when
527    with pytest.raises(ClientError) as e:
528        client.create_service(
529            cluster="test_ecs_cluster",
530            serviceName="test_ecs_service",
531            taskDefinition="test_ecs_task",
532            desiredCount=2,
533            launchType="SOMETHING",
534        )
535
536    # then
537    ex = e.value
538    ex.operation_name.should.equal("CreateService")
539    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
540    ex.response["Error"]["Code"].should.contain("ClientException")
541    ex.response["Error"]["Message"].should.equal(
542        "launch type should be one of [EC2,FARGATE]"
543    )
544
545
546@mock_ecs
547def test_create_service_scheduling_strategy():
548    client = boto3.client("ecs", region_name="us-east-1")
549    _ = client.create_cluster(clusterName="test_ecs_cluster")
550    _ = client.register_task_definition(
551        family="test_ecs_task",
552        containerDefinitions=[
553            {
554                "name": "hello_world",
555                "image": "docker/hello-world:latest",
556                "cpu": 1024,
557                "memory": 400,
558                "essential": True,
559                "environment": [
560                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
561                ],
562                "logConfiguration": {"logDriver": "json-file"},
563            }
564        ],
565    )
566    response = client.create_service(
567        cluster="test_ecs_cluster",
568        serviceName="test_ecs_service",
569        taskDefinition="test_ecs_task",
570        desiredCount=2,
571        schedulingStrategy="DAEMON",
572    )
573    response["service"]["clusterArn"].should.equal(
574        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
575    )
576    response["service"]["desiredCount"].should.equal(2)
577    len(response["service"]["events"]).should.equal(0)
578    len(response["service"]["loadBalancers"]).should.equal(0)
579    response["service"]["pendingCount"].should.equal(0)
580    response["service"]["runningCount"].should.equal(0)
581    response["service"]["serviceArn"].should.equal(
582        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
583    )
584    response["service"]["serviceName"].should.equal("test_ecs_service")
585    response["service"]["status"].should.equal("ACTIVE")
586    response["service"]["taskDefinition"].should.equal(
587        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
588    )
589    response["service"]["schedulingStrategy"].should.equal("DAEMON")
590
591
592@mock_ecs
593def test_list_services():
594    client = boto3.client("ecs", region_name="us-east-1")
595    _ = client.create_cluster(clusterName="test_ecs_cluster")
596    _ = client.register_task_definition(
597        family="test_ecs_task",
598        containerDefinitions=[
599            {
600                "name": "hello_world",
601                "image": "docker/hello-world:latest",
602                "cpu": 1024,
603                "memory": 400,
604                "essential": True,
605                "environment": [
606                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
607                ],
608                "logConfiguration": {"logDriver": "json-file"},
609            }
610        ],
611    )
612    _ = client.create_service(
613        cluster="test_ecs_cluster",
614        serviceName="test_ecs_service1",
615        taskDefinition="test_ecs_task",
616        schedulingStrategy="REPLICA",
617        desiredCount=2,
618    )
619    _ = client.create_service(
620        cluster="test_ecs_cluster",
621        serviceName="test_ecs_service2",
622        taskDefinition="test_ecs_task",
623        schedulingStrategy="DAEMON",
624        desiredCount=2,
625    )
626    unfiltered_response = client.list_services(cluster="test_ecs_cluster")
627    len(unfiltered_response["serviceArns"]).should.equal(2)
628    unfiltered_response["serviceArns"][0].should.equal(
629        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
630    )
631    unfiltered_response["serviceArns"][1].should.equal(
632        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID)
633    )
634
635    filtered_response = client.list_services(
636        cluster="test_ecs_cluster", schedulingStrategy="REPLICA"
637    )
638    len(filtered_response["serviceArns"]).should.equal(1)
639    filtered_response["serviceArns"][0].should.equal(
640        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
641    )
642
643
644@mock_ecs
645def test_describe_services():
646    client = boto3.client("ecs", region_name="us-east-1")
647    _ = client.create_cluster(clusterName="test_ecs_cluster")
648    _ = client.register_task_definition(
649        family="test_ecs_task",
650        containerDefinitions=[
651            {
652                "name": "hello_world",
653                "image": "docker/hello-world:latest",
654                "cpu": 1024,
655                "memory": 400,
656                "essential": True,
657                "environment": [
658                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
659                ],
660                "logConfiguration": {"logDriver": "json-file"},
661            }
662        ],
663    )
664    _ = client.create_service(
665        cluster="test_ecs_cluster",
666        serviceName="test_ecs_service1",
667        taskDefinition="test_ecs_task",
668        desiredCount=2,
669        tags=[{"key": "Name", "value": "test_ecs_service1"}],
670    )
671    _ = client.create_service(
672        cluster="test_ecs_cluster",
673        serviceName="test_ecs_service2",
674        taskDefinition="test_ecs_task",
675        desiredCount=2,
676    )
677    _ = client.create_service(
678        cluster="test_ecs_cluster",
679        serviceName="test_ecs_service3",
680        taskDefinition="test_ecs_task",
681        desiredCount=2,
682    )
683    response = client.describe_services(
684        cluster="test_ecs_cluster",
685        services=[
686            "test_ecs_service1",
687            "arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID),
688        ],
689    )
690    len(response["services"]).should.equal(2)
691    response["services"][0]["serviceArn"].should.equal(
692        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
693    )
694    response["services"][0]["serviceName"].should.equal("test_ecs_service1")
695    response["services"][1]["serviceArn"].should.equal(
696        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID)
697    )
698    response["services"][1]["serviceName"].should.equal("test_ecs_service2")
699
700    response["services"][0]["deployments"][0]["desiredCount"].should.equal(2)
701    response["services"][0]["deployments"][0]["pendingCount"].should.equal(2)
702    response["services"][0]["deployments"][0]["runningCount"].should.equal(0)
703    response["services"][0]["deployments"][0]["status"].should.equal("PRIMARY")
704    response["services"][0]["deployments"][0]["launchType"].should.equal("EC2")
705    (
706        datetime.now()
707        - response["services"][0]["deployments"][0]["createdAt"].replace(tzinfo=None)
708    ).seconds.should.be.within(0, 10)
709    (
710        datetime.now()
711        - response["services"][0]["deployments"][0]["updatedAt"].replace(tzinfo=None)
712    ).seconds.should.be.within(0, 10)
713    response = client.describe_services(
714        cluster="test_ecs_cluster",
715        services=[
716            "test_ecs_service1",
717            "arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID),
718        ],
719        include=["TAGS"],
720    )
721    response["services"][0]["tags"].should.equal(
722        [{"key": "Name", "value": "test_ecs_service1"}]
723    )
724    response["services"][1]["tags"].should.equal([])
725    response["services"][0]["launchType"].should.equal("EC2")
726    response["services"][1]["launchType"].should.equal("EC2")
727
728
729@mock_ecs
730@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
731def test_describe_services_new_arn():
732    if settings.TEST_SERVER_MODE:
733        raise SkipTest("Cant set environment variables in server mode")
734    client = boto3.client("ecs", region_name="us-east-1")
735    _ = client.create_cluster(clusterName="test_ecs_cluster")
736    _ = client.register_task_definition(
737        family="test_ecs_task",
738        containerDefinitions=[
739            {"name": "hello_world", "image": "docker/hello-world:latest",}
740        ],
741    )
742    _ = client.create_service(
743        cluster="test_ecs_cluster",
744        serviceName="test_ecs_service1",
745        taskDefinition="test_ecs_task",
746        desiredCount=2,
747        tags=[{"key": "Name", "value": "test_ecs_service1"}],
748    )
749    response = client.describe_services(
750        cluster="test_ecs_cluster", services=["test_ecs_service1"]
751    )
752    response["services"][0]["serviceArn"].should.equal(
753        "arn:aws:ecs:us-east-1:{}:service/test_ecs_cluster/test_ecs_service1".format(
754            ACCOUNT_ID
755        )
756    )
757
758
759@mock_ecs
760def test_describe_services_scheduling_strategy():
761    client = boto3.client("ecs", region_name="us-east-1")
762    _ = client.create_cluster(clusterName="test_ecs_cluster")
763    _ = client.register_task_definition(
764        family="test_ecs_task",
765        containerDefinitions=[
766            {
767                "name": "hello_world",
768                "image": "docker/hello-world:latest",
769                "cpu": 1024,
770                "memory": 400,
771                "essential": True,
772                "environment": [
773                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
774                ],
775                "logConfiguration": {"logDriver": "json-file"},
776            }
777        ],
778    )
779    _ = client.create_service(
780        cluster="test_ecs_cluster",
781        serviceName="test_ecs_service1",
782        taskDefinition="test_ecs_task",
783        desiredCount=2,
784    )
785    _ = client.create_service(
786        cluster="test_ecs_cluster",
787        serviceName="test_ecs_service2",
788        taskDefinition="test_ecs_task",
789        desiredCount=2,
790        schedulingStrategy="DAEMON",
791    )
792    _ = client.create_service(
793        cluster="test_ecs_cluster",
794        serviceName="test_ecs_service3",
795        taskDefinition="test_ecs_task",
796        desiredCount=2,
797    )
798    response = client.describe_services(
799        cluster="test_ecs_cluster",
800        services=[
801            "test_ecs_service1",
802            "arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID),
803            "test_ecs_service3",
804        ],
805    )
806    len(response["services"]).should.equal(3)
807    response["services"][0]["serviceArn"].should.equal(
808        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service1".format(ACCOUNT_ID)
809    )
810    response["services"][0]["serviceName"].should.equal("test_ecs_service1")
811    response["services"][1]["serviceArn"].should.equal(
812        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service2".format(ACCOUNT_ID)
813    )
814    response["services"][1]["serviceName"].should.equal("test_ecs_service2")
815
816    response["services"][0]["deployments"][0]["desiredCount"].should.equal(2)
817    response["services"][0]["deployments"][0]["pendingCount"].should.equal(2)
818    response["services"][0]["deployments"][0]["runningCount"].should.equal(0)
819    response["services"][0]["deployments"][0]["status"].should.equal("PRIMARY")
820
821    response["services"][0]["schedulingStrategy"].should.equal("REPLICA")
822    response["services"][1]["schedulingStrategy"].should.equal("DAEMON")
823    response["services"][2]["schedulingStrategy"].should.equal("REPLICA")
824
825
826@mock_ecs
827def test_describe_services_error_unknown_cluster():
828    # given
829    client = boto3.client("ecs", region_name="eu-central-1")
830    cluster_name = "unknown"
831
832    # when
833    with pytest.raises(ClientError) as e:
834        client.describe_services(
835            cluster=cluster_name, services=["test"],
836        )
837
838    # then
839    ex = e.value
840    ex.operation_name.should.equal("DescribeServices")
841    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
842    ex.response["Error"]["Code"].should.contain("ClusterNotFoundException")
843    ex.response["Error"]["Message"].should.equal("Cluster not found.")
844
845
846@mock_ecs
847def test_describe_services_with_known_unknown_services():
848    # given
849    client = boto3.client("ecs", region_name="eu-central-1")
850    cluster_name = "test_cluster"
851    task_name = "test_task"
852    service_name = "test_service"
853    client.create_cluster(clusterName=cluster_name)
854    client.register_task_definition(
855        family=task_name,
856        containerDefinitions=[
857            {
858                "name": "hello_world",
859                "image": "docker/hello-world:latest",
860                "cpu": 256,
861                "memory": 512,
862                "essential": True,
863            }
864        ],
865    )
866    service_arn = client.create_service(
867        cluster=cluster_name,
868        serviceName=service_name,
869        taskDefinition=task_name,
870        desiredCount=1,
871    )["service"]["serviceArn"]
872
873    # when
874    response = client.describe_services(
875        cluster=cluster_name,
876        services=[
877            service_name,
878            "unknown",
879            service_arn,
880            "arn:aws:ecs:eu-central-1:{}:service/unknown-2".format(ACCOUNT_ID),
881        ],
882    )
883
884    # then
885    services = response["services"]
886    services.should.have.length_of(2)
887    [service["serviceArn"] for service in services].should.equal(
888        [service_arn, service_arn]
889    )
890
891    failures = response["failures"]
892    failures.should.have.length_of(2)
893    sorted(failures, key=lambda item: item["arn"]).should.equal(
894        [
895            {
896                "arn": "arn:aws:ecs:eu-central-1:{}:service/unknown".format(ACCOUNT_ID),
897                "reason": "MISSING",
898            },
899            {
900                "arn": "arn:aws:ecs:eu-central-1:{}:service/unknown-2".format(
901                    ACCOUNT_ID
902                ),
903                "reason": "MISSING",
904            },
905        ]
906    )
907
908
909@mock_ecs
910def test_update_service():
911    client = boto3.client("ecs", region_name="us-east-1")
912    _ = client.create_cluster(clusterName="test_ecs_cluster")
913    _ = client.register_task_definition(
914        family="test_ecs_task",
915        containerDefinitions=[
916            {
917                "name": "hello_world",
918                "image": "docker/hello-world:latest",
919                "cpu": 1024,
920                "memory": 400,
921                "essential": True,
922                "environment": [
923                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
924                ],
925                "logConfiguration": {"logDriver": "json-file"},
926            }
927        ],
928    )
929    response = client.create_service(
930        cluster="test_ecs_cluster",
931        serviceName="test_ecs_service",
932        taskDefinition="test_ecs_task",
933        desiredCount=2,
934    )
935    response["service"]["desiredCount"].should.equal(2)
936
937    response = client.update_service(
938        cluster="test_ecs_cluster",
939        service="test_ecs_service",
940        taskDefinition="test_ecs_task",
941        desiredCount=0,
942    )
943    response["service"]["desiredCount"].should.equal(0)
944    response["service"]["schedulingStrategy"].should.equal("REPLICA")
945
946    # Verify we can pass the ARNs of the cluster and service
947    response = client.update_service(
948        cluster=response["service"]["clusterArn"],
949        service=response["service"]["serviceArn"],
950        taskDefinition="test_ecs_task",
951        desiredCount=1,
952    )
953    response["service"]["desiredCount"].should.equal(1)
954
955
956@mock_ecs
957def test_update_missing_service():
958    client = boto3.client("ecs", region_name="us-east-1")
959    _ = client.create_cluster(clusterName="test_ecs_cluster")
960
961    client.update_service.when.called_with(
962        cluster="test_ecs_cluster",
963        service="test_ecs_service",
964        taskDefinition="test_ecs_task",
965        desiredCount=0,
966    ).should.throw(ClientError)
967
968
969@mock_ecs
970def test_delete_service():
971    client = boto3.client("ecs", region_name="us-east-1")
972    _ = client.create_cluster(clusterName="test_ecs_cluster")
973    _ = client.register_task_definition(
974        family="test_ecs_task",
975        containerDefinitions=[
976            {
977                "name": "hello_world",
978                "image": "docker/hello-world:latest",
979                "cpu": 1024,
980                "memory": 400,
981                "essential": True,
982                "environment": [
983                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
984                ],
985                "logConfiguration": {"logDriver": "json-file"},
986            }
987        ],
988    )
989    _ = client.create_service(
990        cluster="test_ecs_cluster",
991        serviceName="test_ecs_service",
992        taskDefinition="test_ecs_task",
993        desiredCount=2,
994    )
995    _ = client.update_service(
996        cluster="test_ecs_cluster", service="test_ecs_service", desiredCount=0
997    )
998    response = client.delete_service(
999        cluster="test_ecs_cluster", service="test_ecs_service"
1000    )
1001    response["service"]["clusterArn"].should.equal(
1002        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
1003    )
1004    response["service"]["desiredCount"].should.equal(0)
1005    len(response["service"]["events"]).should.equal(0)
1006    len(response["service"]["loadBalancers"]).should.equal(0)
1007    response["service"]["pendingCount"].should.equal(0)
1008    response["service"]["runningCount"].should.equal(0)
1009    response["service"]["serviceArn"].should.equal(
1010        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
1011    )
1012    response["service"]["serviceName"].should.equal("test_ecs_service")
1013    response["service"]["status"].should.equal("ACTIVE")
1014    response["service"]["schedulingStrategy"].should.equal("REPLICA")
1015    response["service"]["taskDefinition"].should.equal(
1016        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
1017    )
1018
1019
1020@mock_ecs
1021def test_delete_service_force():
1022    client = boto3.client("ecs", region_name="us-east-1")
1023    _ = client.create_cluster(clusterName="test_ecs_cluster")
1024    _ = client.register_task_definition(
1025        family="test_ecs_task",
1026        containerDefinitions=[
1027            {
1028                "name": "hello_world",
1029                "image": "docker/hello-world:latest",
1030                "cpu": 1024,
1031                "memory": 400,
1032                "essential": True,
1033                "environment": [
1034                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1035                ],
1036                "logConfiguration": {"logDriver": "json-file"},
1037            }
1038        ],
1039    )
1040    _ = client.create_service(
1041        cluster="test_ecs_cluster",
1042        serviceName="test_ecs_service",
1043        taskDefinition="test_ecs_task",
1044        desiredCount=2,
1045    )
1046    response = client.delete_service(
1047        cluster="test_ecs_cluster", service="test_ecs_service", force=True
1048    )
1049    response["service"]["clusterArn"].should.equal(
1050        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
1051    )
1052    len(response["service"]["events"]).should.equal(0)
1053    len(response["service"]["loadBalancers"]).should.equal(0)
1054    response["service"]["pendingCount"].should.equal(0)
1055    response["service"]["runningCount"].should.equal(0)
1056    response["service"]["serviceArn"].should.equal(
1057        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
1058    )
1059    response["service"]["serviceName"].should.equal("test_ecs_service")
1060    response["service"]["status"].should.equal("ACTIVE")
1061    response["service"]["schedulingStrategy"].should.equal("REPLICA")
1062    response["service"]["taskDefinition"].should.equal(
1063        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
1064    )
1065
1066
1067@mock_ecs
1068def test_delete_service_exceptions():
1069    client = boto3.client("ecs", region_name="us-east-1")
1070
1071    # Raises ClusterNotFoundException because "default" is not a cluster
1072    client.delete_service.when.called_with(service="not_as_service").should.throw(
1073        ClientError, ClusterNotFoundException().message
1074    )
1075
1076    _ = client.create_cluster()
1077    client.delete_service.when.called_with(service="not_as_service").should.throw(
1078        ClientError, ServiceNotFoundException().message
1079    )
1080
1081    _ = client.register_task_definition(
1082        family="test_ecs_task",
1083        containerDefinitions=[
1084            {
1085                "name": "hello_world",
1086                "image": "docker/hello-world:latest",
1087                "cpu": 1024,
1088                "memory": 400,
1089            }
1090        ],
1091    )
1092
1093    _ = client.create_service(
1094        serviceName="test_ecs_service", taskDefinition="test_ecs_task", desiredCount=1,
1095    )
1096
1097    client.delete_service.when.called_with(service="test_ecs_service").should.throw(
1098        ClientError,
1099        InvalidParameterException(
1100            "The service cannot be stopped while it is scaled above 0."
1101        ).message,
1102    )
1103
1104
1105@mock_ecs
1106def test_update_service_exceptions():
1107    client = boto3.client("ecs", region_name="us-east-1")
1108
1109    client.update_service.when.called_with(
1110        service="not_a_service", desiredCount=0
1111    ).should.throw(ClientError, ClusterNotFoundException().message)
1112
1113    _ = client.create_cluster()
1114
1115    client.update_service.when.called_with(
1116        service="not_a_service", desiredCount=0
1117    ).should.throw(ClientError, ServiceNotFoundException().message)
1118
1119
1120@mock_ec2
1121@mock_ecs
1122def test_register_container_instance():
1123    ecs_client = boto3.client("ecs", region_name="us-east-1")
1124    ec2 = boto3.resource("ec2", region_name="us-east-1")
1125
1126    test_cluster_name = "test_ecs_cluster"
1127
1128    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
1129
1130    test_instance = ec2.create_instances(
1131        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1132    )[0]
1133
1134    instance_id_document = json.dumps(
1135        ec2_utils.generate_instance_identity_document(test_instance)
1136    )
1137
1138    response = ecs_client.register_container_instance(
1139        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1140    )
1141
1142    response["containerInstance"]["ec2InstanceId"].should.equal(test_instance.id)
1143    full_arn = response["containerInstance"]["containerInstanceArn"]
1144    arn_part = full_arn.split("/")
1145    arn_part[0].should.equal(
1146        "arn:aws:ecs:us-east-1:{}:container-instance".format(ACCOUNT_ID)
1147    )
1148    arn_part[1].should.equal(str(UUID(arn_part[1])))
1149    response["containerInstance"]["status"].should.equal("ACTIVE")
1150    len(response["containerInstance"]["registeredResources"]).should.equal(4)
1151    len(response["containerInstance"]["remainingResources"]).should.equal(4)
1152    response["containerInstance"]["agentConnected"].should.equal(True)
1153    response["containerInstance"]["versionInfo"]["agentVersion"].should.equal("1.0.0")
1154    response["containerInstance"]["versionInfo"]["agentHash"].should.equal("4023248")
1155    response["containerInstance"]["versionInfo"]["dockerVersion"].should.equal(
1156        "DockerVersion: 1.5.0"
1157    )
1158
1159
1160@mock_ec2
1161@mock_ecs
1162@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
1163def test_register_container_instance_new_arn_format():
1164    if settings.TEST_SERVER_MODE:
1165        raise SkipTest("Cant set environment variables in server mode")
1166    ecs_client = boto3.client("ecs", region_name="us-east-1")
1167    ec2 = boto3.resource("ec2", region_name="us-east-1")
1168
1169    test_cluster_name = "test_ecs_cluster"
1170
1171    ecs_client.create_cluster(clusterName=test_cluster_name)
1172
1173    test_instance = ec2.create_instances(
1174        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1175    )[0]
1176
1177    instance_id_document = json.dumps(
1178        ec2_utils.generate_instance_identity_document(test_instance)
1179    )
1180
1181    response = ecs_client.register_container_instance(
1182        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1183    )
1184
1185    full_arn = response["containerInstance"]["containerInstanceArn"]
1186    full_arn.should.match(
1187        f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:container-instance/{test_cluster_name}/[a-z0-9-]+$"
1188    )
1189
1190
1191@mock_ec2
1192@mock_ecs
1193def test_deregister_container_instance():
1194    ecs_client = boto3.client("ecs", region_name="us-east-1")
1195    ec2 = boto3.resource("ec2", region_name="us-east-1")
1196
1197    test_cluster_name = "test_ecs_cluster"
1198
1199    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
1200
1201    test_instance = ec2.create_instances(
1202        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1203    )[0]
1204
1205    instance_id_document = json.dumps(
1206        ec2_utils.generate_instance_identity_document(test_instance)
1207    )
1208
1209    response = ecs_client.register_container_instance(
1210        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1211    )
1212    container_instance_id = response["containerInstance"]["containerInstanceArn"]
1213    response = ecs_client.deregister_container_instance(
1214        cluster=test_cluster_name, containerInstance=container_instance_id
1215    )
1216    container_instances_response = ecs_client.list_container_instances(
1217        cluster=test_cluster_name
1218    )
1219    len(container_instances_response["containerInstanceArns"]).should.equal(0)
1220
1221    response = ecs_client.register_container_instance(
1222        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1223    )
1224    container_instance_id = response["containerInstance"]["containerInstanceArn"]
1225    _ = ecs_client.register_task_definition(
1226        family="test_ecs_task",
1227        containerDefinitions=[
1228            {
1229                "name": "hello_world",
1230                "image": "docker/hello-world:latest",
1231                "cpu": 1024,
1232                "memory": 400,
1233                "essential": True,
1234                "environment": [
1235                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1236                ],
1237                "logConfiguration": {"logDriver": "json-file"},
1238            }
1239        ],
1240    )
1241
1242    ecs_client.start_task(
1243        cluster="test_ecs_cluster",
1244        taskDefinition="test_ecs_task",
1245        overrides={},
1246        containerInstances=[container_instance_id],
1247        startedBy="moto",
1248    )
1249    with pytest.raises(Exception):
1250        ecs_client.deregister_container_instance(
1251            cluster=test_cluster_name, containerInstance=container_instance_id
1252        )
1253    # TODO: Return correct error format
1254    # should.contain("Found running tasks on the instance")
1255
1256    container_instances_response = ecs_client.list_container_instances(
1257        cluster=test_cluster_name
1258    )
1259    len(container_instances_response["containerInstanceArns"]).should.equal(1)
1260    ecs_client.deregister_container_instance(
1261        cluster=test_cluster_name, containerInstance=container_instance_id, force=True
1262    )
1263    container_instances_response = ecs_client.list_container_instances(
1264        cluster=test_cluster_name
1265    )
1266    len(container_instances_response["containerInstanceArns"]).should.equal(0)
1267
1268
1269@mock_ec2
1270@mock_ecs
1271def test_list_container_instances():
1272    ecs_client = boto3.client("ecs", region_name="us-east-1")
1273    ec2 = boto3.resource("ec2", region_name="us-east-1")
1274
1275    test_cluster_name = "test_ecs_cluster"
1276    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
1277
1278    instance_to_create = 3
1279    test_instance_arns = []
1280    for _ in range(0, instance_to_create):
1281        test_instance = ec2.create_instances(
1282            ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1283        )[0]
1284
1285        instance_id_document = json.dumps(
1286            ec2_utils.generate_instance_identity_document(test_instance)
1287        )
1288
1289        response = ecs_client.register_container_instance(
1290            cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1291        )
1292
1293        test_instance_arns.append(response["containerInstance"]["containerInstanceArn"])
1294
1295    response = ecs_client.list_container_instances(cluster=test_cluster_name)
1296
1297    len(response["containerInstanceArns"]).should.equal(instance_to_create)
1298    for arn in test_instance_arns:
1299        response["containerInstanceArns"].should.contain(arn)
1300
1301
1302@mock_ec2
1303@mock_ecs
1304def test_describe_container_instances():
1305    ecs_client = boto3.client("ecs", region_name="us-east-1")
1306    ec2 = boto3.resource("ec2", region_name="us-east-1")
1307
1308    test_cluster_name = "test_ecs_cluster"
1309    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
1310
1311    instance_to_create = 3
1312    test_instance_arns = []
1313    for _ in range(0, instance_to_create):
1314        test_instance = ec2.create_instances(
1315            ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1316        )[0]
1317
1318        instance_id_document = json.dumps(
1319            ec2_utils.generate_instance_identity_document(test_instance)
1320        )
1321
1322        response = ecs_client.register_container_instance(
1323            cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1324        )
1325
1326        test_instance_arns.append(response["containerInstance"]["containerInstanceArn"])
1327
1328    test_instance_ids = list(map((lambda x: x.split("/")[1]), test_instance_arns))
1329    response = ecs_client.describe_container_instances(
1330        cluster=test_cluster_name, containerInstances=test_instance_ids
1331    )
1332    len(response["failures"]).should.equal(0)
1333    len(response["containerInstances"]).should.equal(instance_to_create)
1334    response_arns = [
1335        ci["containerInstanceArn"] for ci in response["containerInstances"]
1336    ]
1337    for arn in test_instance_arns:
1338        response_arns.should.contain(arn)
1339    for instance in response["containerInstances"]:
1340        instance.keys().should.contain("runningTasksCount")
1341        instance.keys().should.contain("pendingTasksCount")
1342        instance["registeredAt"].should.be.a("datetime.datetime")
1343
1344    with pytest.raises(ClientError) as e:
1345        ecs_client.describe_container_instances(
1346            cluster=test_cluster_name, containerInstances=[]
1347        )
1348    err = e.value.response["Error"]
1349    err["Code"].should.equal("ClientException")
1350    err["Message"].should.equal("Container Instances cannot be empty.")
1351
1352
1353@mock_ecs
1354def test_describe_container_instances_exceptions():
1355    client = boto3.client("ecs", region_name="us-east-1")
1356
1357    client.describe_container_instances.when.called_with(
1358        containerInstances=[]
1359    ).should.throw(ClientError, ClusterNotFoundException().message)
1360
1361    _ = client.create_cluster()
1362    client.describe_container_instances.when.called_with(
1363        containerInstances=[]
1364    ).should.throw(
1365        ClientError,
1366        InvalidParameterException("Container Instances cannot be empty.").message,
1367    )
1368
1369
1370@mock_ec2
1371@mock_ecs
1372def test_update_container_instances_state():
1373    ecs_client = boto3.client("ecs", region_name="us-east-1")
1374    ec2 = boto3.resource("ec2", region_name="us-east-1")
1375
1376    test_cluster_name = "test_ecs_cluster"
1377    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
1378
1379    instance_to_create = 3
1380    test_instance_arns = []
1381    for _ in range(0, instance_to_create):
1382        test_instance = ec2.create_instances(
1383            ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1384        )[0]
1385
1386        instance_id_document = json.dumps(
1387            ec2_utils.generate_instance_identity_document(test_instance)
1388        )
1389
1390        response = ecs_client.register_container_instance(
1391            cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1392        )
1393
1394        test_instance_arns.append(response["containerInstance"]["containerInstanceArn"])
1395
1396    test_instance_ids = list(map((lambda x: x.split("/")[1]), test_instance_arns))
1397    response = ecs_client.update_container_instances_state(
1398        cluster=test_cluster_name,
1399        containerInstances=test_instance_ids,
1400        status="DRAINING",
1401    )
1402    len(response["failures"]).should.equal(0)
1403    len(response["containerInstances"]).should.equal(instance_to_create)
1404    response_statuses = [ci["status"] for ci in response["containerInstances"]]
1405    for status in response_statuses:
1406        status.should.equal("DRAINING")
1407    response = ecs_client.update_container_instances_state(
1408        cluster=test_cluster_name,
1409        containerInstances=test_instance_ids,
1410        status="DRAINING",
1411    )
1412    len(response["failures"]).should.equal(0)
1413    len(response["containerInstances"]).should.equal(instance_to_create)
1414    response_statuses = [ci["status"] for ci in response["containerInstances"]]
1415    for status in response_statuses:
1416        status.should.equal("DRAINING")
1417    response = ecs_client.update_container_instances_state(
1418        cluster=test_cluster_name, containerInstances=test_instance_ids, status="ACTIVE"
1419    )
1420    len(response["failures"]).should.equal(0)
1421    len(response["containerInstances"]).should.equal(instance_to_create)
1422    response_statuses = [ci["status"] for ci in response["containerInstances"]]
1423    for status in response_statuses:
1424        status.should.equal("ACTIVE")
1425    ecs_client.update_container_instances_state.when.called_with(
1426        cluster=test_cluster_name,
1427        containerInstances=test_instance_ids,
1428        status="test_status",
1429    ).should.throw(Exception)
1430
1431
1432@mock_ec2
1433@mock_ecs
1434def test_update_container_instances_state_by_arn():
1435    ecs_client = boto3.client("ecs", region_name="us-east-1")
1436    ec2 = boto3.resource("ec2", region_name="us-east-1")
1437
1438    test_cluster_name = "test_ecs_cluster"
1439    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
1440
1441    instance_to_create = 3
1442    test_instance_arns = []
1443    for _ in range(0, instance_to_create):
1444        test_instance = ec2.create_instances(
1445            ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1446        )[0]
1447
1448        instance_id_document = json.dumps(
1449            ec2_utils.generate_instance_identity_document(test_instance)
1450        )
1451
1452        response = ecs_client.register_container_instance(
1453            cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1454        )
1455
1456        test_instance_arns.append(response["containerInstance"]["containerInstanceArn"])
1457
1458    response = ecs_client.update_container_instances_state(
1459        cluster=test_cluster_name,
1460        containerInstances=test_instance_arns,
1461        status="DRAINING",
1462    )
1463    len(response["failures"]).should.equal(0)
1464    len(response["containerInstances"]).should.equal(instance_to_create)
1465    response_statuses = [ci["status"] for ci in response["containerInstances"]]
1466    for status in response_statuses:
1467        status.should.equal("DRAINING")
1468    response = ecs_client.update_container_instances_state(
1469        cluster=test_cluster_name,
1470        containerInstances=test_instance_arns,
1471        status="DRAINING",
1472    )
1473    len(response["failures"]).should.equal(0)
1474    len(response["containerInstances"]).should.equal(instance_to_create)
1475    response_statuses = [ci["status"] for ci in response["containerInstances"]]
1476    for status in response_statuses:
1477        status.should.equal("DRAINING")
1478    response = ecs_client.update_container_instances_state(
1479        cluster=test_cluster_name,
1480        containerInstances=test_instance_arns,
1481        status="ACTIVE",
1482    )
1483    len(response["failures"]).should.equal(0)
1484    len(response["containerInstances"]).should.equal(instance_to_create)
1485    response_statuses = [ci["status"] for ci in response["containerInstances"]]
1486    for status in response_statuses:
1487        status.should.equal("ACTIVE")
1488    ecs_client.update_container_instances_state.when.called_with(
1489        cluster=test_cluster_name,
1490        containerInstances=test_instance_arns,
1491        status="test_status",
1492    ).should.throw(Exception)
1493
1494
1495@mock_ec2
1496@mock_ecs
1497def test_run_task():
1498    client = boto3.client("ecs", region_name="us-east-1")
1499    ec2 = boto3.resource("ec2", region_name="us-east-1")
1500
1501    test_cluster_name = "test_ecs_cluster"
1502
1503    _ = client.create_cluster(clusterName=test_cluster_name)
1504
1505    test_instance = ec2.create_instances(
1506        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1507    )[0]
1508
1509    instance_id_document = json.dumps(
1510        ec2_utils.generate_instance_identity_document(test_instance)
1511    )
1512
1513    response = client.register_container_instance(
1514        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1515    )
1516
1517    _ = client.register_task_definition(
1518        family="test_ecs_task",
1519        containerDefinitions=[
1520            {
1521                "name": "hello_world",
1522                "image": "docker/hello-world:latest",
1523                "cpu": 1024,
1524                "memory": 400,
1525                "essential": True,
1526                "environment": [
1527                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1528                ],
1529                "logConfiguration": {"logDriver": "json-file"},
1530            }
1531        ],
1532    )
1533    response = client.run_task(
1534        cluster="test_ecs_cluster",
1535        overrides={},
1536        taskDefinition="test_ecs_task",
1537        count=2,
1538        startedBy="moto",
1539        tags=[
1540            {"key": "tagKey0", "value": "tagValue0"},
1541            {"key": "tagKey1", "value": "tagValue1"},
1542        ],
1543    )
1544    len(response["tasks"]).should.equal(2)
1545    response["tasks"][0]["taskArn"].should.contain(
1546        "arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
1547    )
1548    response["tasks"][0]["clusterArn"].should.equal(
1549        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
1550    )
1551    response["tasks"][0]["taskDefinitionArn"].should.equal(
1552        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
1553    )
1554    response["tasks"][0]["containerInstanceArn"].should.contain(
1555        "arn:aws:ecs:us-east-1:{}:container-instance/".format(ACCOUNT_ID)
1556    )
1557    response["tasks"][0]["overrides"].should.equal({})
1558    response["tasks"][0]["lastStatus"].should.equal("RUNNING")
1559    response["tasks"][0]["desiredStatus"].should.equal("RUNNING")
1560    response["tasks"][0]["startedBy"].should.equal("moto")
1561    response["tasks"][0]["stoppedReason"].should.equal("")
1562    response["tasks"][0]["tags"][0].get("value").should.equal("tagValue0")
1563
1564
1565@mock_ec2
1566@mock_ecs
1567def test_run_task_default_cluster():
1568    client = boto3.client("ecs", region_name="us-east-1")
1569    ec2 = boto3.resource("ec2", region_name="us-east-1")
1570
1571    test_cluster_name = "default"
1572
1573    _ = client.create_cluster(clusterName=test_cluster_name)
1574
1575    test_instance = ec2.create_instances(
1576        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1577    )[0]
1578
1579    instance_id_document = json.dumps(
1580        ec2_utils.generate_instance_identity_document(test_instance)
1581    )
1582
1583    response = client.register_container_instance(
1584        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1585    )
1586
1587    _ = client.register_task_definition(
1588        family="test_ecs_task",
1589        containerDefinitions=[
1590            {
1591                "name": "hello_world",
1592                "image": "docker/hello-world:latest",
1593                "cpu": 1024,
1594                "memory": 400,
1595                "essential": True,
1596                "environment": [
1597                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1598                ],
1599                "logConfiguration": {"logDriver": "json-file"},
1600            }
1601        ],
1602    )
1603    response = client.run_task(
1604        launchType="FARGATE",
1605        overrides={},
1606        taskDefinition="test_ecs_task",
1607        count=2,
1608        startedBy="moto",
1609    )
1610    len(response["tasks"]).should.equal(2)
1611    response["tasks"][0]["taskArn"].should.match(
1612        "arn:aws:ecs:us-east-1:{}:task/[a-z0-9-]+$".format(ACCOUNT_ID)
1613    )
1614    response["tasks"][0]["clusterArn"].should.equal(
1615        "arn:aws:ecs:us-east-1:{}:cluster/default".format(ACCOUNT_ID)
1616    )
1617    response["tasks"][0]["taskDefinitionArn"].should.equal(
1618        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
1619    )
1620    response["tasks"][0]["containerInstanceArn"].should.contain(
1621        "arn:aws:ecs:us-east-1:{}:container-instance/".format(ACCOUNT_ID)
1622    )
1623    response["tasks"][0]["overrides"].should.equal({})
1624    response["tasks"][0]["lastStatus"].should.equal("RUNNING")
1625    response["tasks"][0]["desiredStatus"].should.equal("RUNNING")
1626    response["tasks"][0]["startedBy"].should.equal("moto")
1627    response["tasks"][0]["stoppedReason"].should.equal("")
1628
1629
1630@mock_ec2
1631@mock_ecs
1632@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
1633def test_run_task_default_cluster_new_arn_format():
1634    if settings.TEST_SERVER_MODE:
1635        raise SkipTest("Cant set environment variables in server mode")
1636    client = boto3.client("ecs", region_name="us-east-1")
1637    ec2 = boto3.resource("ec2", region_name="us-east-1")
1638
1639    test_cluster_name = "default"
1640
1641    client.create_cluster(clusterName=test_cluster_name)
1642
1643    test_instance = ec2.create_instances(
1644        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1645    )[0]
1646
1647    instance_id_document = json.dumps(
1648        ec2_utils.generate_instance_identity_document(test_instance)
1649    )
1650
1651    client.register_container_instance(
1652        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1653    )
1654
1655    client.register_task_definition(
1656        family="test_ecs_task",
1657        containerDefinitions=[
1658            {
1659                "name": "hello_world",
1660                "image": "docker/hello-world:latest",
1661                "cpu": 1024,
1662                "memory": 400,
1663            }
1664        ],
1665    )
1666    response = client.run_task(
1667        launchType="FARGATE",
1668        overrides={},
1669        taskDefinition="test_ecs_task",
1670        count=1,
1671        startedBy="moto",
1672    )
1673    response["tasks"][0]["taskArn"].should.match(
1674        f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:task/{test_cluster_name}/[a-z0-9-]+$"
1675    )
1676
1677
1678@mock_ecs
1679def test_run_task_exceptions():
1680    client = boto3.client("ecs", region_name="us-east-1")
1681    _ = client.register_task_definition(
1682        family="test_ecs_task",
1683        containerDefinitions=[
1684            {
1685                "name": "hello_world",
1686                "image": "docker/hello-world:latest",
1687                "cpu": 1024,
1688                "memory": 400,
1689            }
1690        ],
1691    )
1692
1693    client.run_task.when.called_with(
1694        cluster="not_a_cluster", taskDefinition="test_ecs_task"
1695    ).should.throw(ClientError, ClusterNotFoundException().message)
1696
1697
1698@mock_ec2
1699@mock_ecs
1700def test_start_task():
1701    client = boto3.client("ecs", region_name="us-east-1")
1702    ec2 = boto3.resource("ec2", region_name="us-east-1")
1703
1704    test_cluster_name = "test_ecs_cluster"
1705
1706    _ = client.create_cluster(clusterName=test_cluster_name)
1707
1708    test_instance = ec2.create_instances(
1709        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1710    )[0]
1711
1712    instance_id_document = json.dumps(
1713        ec2_utils.generate_instance_identity_document(test_instance)
1714    )
1715
1716    response = client.register_container_instance(
1717        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1718    )
1719
1720    container_instances = client.list_container_instances(cluster=test_cluster_name)
1721    container_instance_id = container_instances["containerInstanceArns"][0].split("/")[
1722        -1
1723    ]
1724
1725    _ = client.register_task_definition(
1726        family="test_ecs_task",
1727        containerDefinitions=[
1728            {
1729                "name": "hello_world",
1730                "image": "docker/hello-world:latest",
1731                "cpu": 1024,
1732                "memory": 400,
1733                "essential": True,
1734                "environment": [
1735                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1736                ],
1737                "logConfiguration": {"logDriver": "json-file"},
1738            }
1739        ],
1740    )
1741
1742    response = client.start_task(
1743        cluster="test_ecs_cluster",
1744        taskDefinition="test_ecs_task",
1745        overrides={},
1746        containerInstances=[container_instance_id],
1747        startedBy="moto",
1748    )
1749
1750    len(response["tasks"]).should.equal(1)
1751    response["tasks"][0]["taskArn"].should.contain(
1752        "arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
1753    )
1754    response["tasks"][0]["clusterArn"].should.equal(
1755        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
1756    )
1757    response["tasks"][0]["taskDefinitionArn"].should.equal(
1758        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
1759    )
1760    response["tasks"][0]["containerInstanceArn"].should.equal(
1761        "arn:aws:ecs:us-east-1:{0}:container-instance/{1}".format(
1762            ACCOUNT_ID, container_instance_id
1763        )
1764    )
1765    response["tasks"][0]["overrides"].should.equal({})
1766    response["tasks"][0]["lastStatus"].should.equal("RUNNING")
1767    response["tasks"][0]["desiredStatus"].should.equal("RUNNING")
1768    response["tasks"][0]["startedBy"].should.equal("moto")
1769    response["tasks"][0]["stoppedReason"].should.equal("")
1770
1771
1772@mock_ecs
1773def test_start_task_exceptions():
1774    client = boto3.client("ecs", region_name="us-east-1")
1775    _ = client.register_task_definition(
1776        family="test_ecs_task",
1777        containerDefinitions=[
1778            {
1779                "name": "hello_world",
1780                "image": "docker/hello-world:latest",
1781                "cpu": 1024,
1782                "memory": 400,
1783            }
1784        ],
1785    )
1786
1787    client.start_task.when.called_with(
1788        taskDefinition="test_ecs_task", containerInstances=["not_a_container_instance"]
1789    ).should.throw(ClientError, ClusterNotFoundException().message)
1790
1791    _ = client.create_cluster()
1792    client.start_task.when.called_with(
1793        taskDefinition="test_ecs_task", containerInstances=[]
1794    ).should.throw(
1795        ClientError, InvalidParameterException("Container Instances cannot be empty.")
1796    )
1797
1798
1799@mock_ec2
1800@mock_ecs
1801def test_list_tasks():
1802    client = boto3.client("ecs", region_name="us-east-1")
1803    ec2 = boto3.resource("ec2", region_name="us-east-1")
1804
1805    _ = client.create_cluster()
1806
1807    test_instance = ec2.create_instances(
1808        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1809    )[0]
1810
1811    instance_id_document = json.dumps(
1812        ec2_utils.generate_instance_identity_document(test_instance)
1813    )
1814
1815    _ = client.register_container_instance(
1816        instanceIdentityDocument=instance_id_document
1817    )
1818
1819    container_instances = client.list_container_instances()
1820    container_instance_id = container_instances["containerInstanceArns"][0].split("/")[
1821        -1
1822    ]
1823
1824    _ = client.register_task_definition(
1825        family="test_ecs_task",
1826        containerDefinitions=[
1827            {
1828                "name": "hello_world",
1829                "image": "docker/hello-world:latest",
1830                "cpu": 1024,
1831                "memory": 400,
1832                "essential": True,
1833                "environment": [
1834                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1835                ],
1836                "logConfiguration": {"logDriver": "json-file"},
1837            }
1838        ],
1839    )
1840
1841    _ = client.start_task(
1842        taskDefinition="test_ecs_task",
1843        overrides={},
1844        containerInstances=[container_instance_id],
1845        startedBy="foo",
1846    )
1847
1848    _ = client.start_task(
1849        taskDefinition="test_ecs_task",
1850        overrides={},
1851        containerInstances=[container_instance_id],
1852        startedBy="bar",
1853    )
1854
1855    assert len(client.list_tasks()["taskArns"]).should.equal(2)
1856    assert len(client.list_tasks(startedBy="foo")["taskArns"]).should.equal(1)
1857
1858
1859@mock_ecs
1860def test_list_tasks_exceptions():
1861    client = boto3.client("ecs", region_name="us-east-1")
1862    client.list_tasks.when.called_with(cluster="not_a_cluster").should.throw(
1863        ClientError, ClusterNotFoundException().message
1864    )
1865
1866
1867@mock_ec2
1868@mock_ecs
1869def test_describe_tasks():
1870    client = boto3.client("ecs", region_name="us-east-1")
1871    ec2 = boto3.resource("ec2", region_name="us-east-1")
1872
1873    test_cluster_name = "test_ecs_cluster"
1874
1875    _ = client.create_cluster(clusterName=test_cluster_name)
1876
1877    test_instance = ec2.create_instances(
1878        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1879    )[0]
1880
1881    instance_id_document = json.dumps(
1882        ec2_utils.generate_instance_identity_document(test_instance)
1883    )
1884
1885    response = client.register_container_instance(
1886        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1887    )
1888
1889    _ = client.register_task_definition(
1890        family="test_ecs_task",
1891        containerDefinitions=[
1892            {
1893                "name": "hello_world",
1894                "image": "docker/hello-world:latest",
1895                "cpu": 1024,
1896                "memory": 400,
1897                "essential": True,
1898                "environment": [
1899                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
1900                ],
1901                "logConfiguration": {"logDriver": "json-file"},
1902            }
1903        ],
1904    )
1905    tasks_arns = [
1906        task["taskArn"]
1907        for task in client.run_task(
1908            cluster="test_ecs_cluster",
1909            overrides={},
1910            taskDefinition="test_ecs_task",
1911            count=2,
1912            startedBy="moto",
1913        )["tasks"]
1914    ]
1915    response = client.describe_tasks(cluster="test_ecs_cluster", tasks=tasks_arns)
1916
1917    len(response["tasks"]).should.equal(2)
1918    set(
1919        [response["tasks"][0]["taskArn"], response["tasks"][1]["taskArn"]]
1920    ).should.equal(set(tasks_arns))
1921
1922    # Test we can pass task ids instead of ARNs
1923    response = client.describe_tasks(
1924        cluster="test_ecs_cluster", tasks=[tasks_arns[0].split("/")[-1]]
1925    )
1926    len(response["tasks"]).should.equal(1)
1927
1928
1929@mock_ecs
1930def test_describe_tasks_exceptions():
1931    client = boto3.client("ecs", region_name="us-east-1")
1932
1933    client.describe_tasks.when.called_with(tasks=[]).should.throw(
1934        ClientError, ClusterNotFoundException().message
1935    )
1936
1937    _ = client.create_cluster()
1938    client.describe_tasks.when.called_with(tasks=[]).should.throw(
1939        ClientError, InvalidParameterException("Tasks cannot be empty.").message
1940    )
1941
1942
1943@mock_ecs
1944def test_describe_task_definition_by_family():
1945    client = boto3.client("ecs", region_name="us-east-1")
1946    container_definition = {
1947        "name": "hello_world",
1948        "image": "docker/hello-world:latest",
1949        "cpu": 1024,
1950        "memory": 400,
1951        "essential": True,
1952        "environment": [{"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}],
1953        "logConfiguration": {"logDriver": "json-file"},
1954    }
1955    task_definition = client.register_task_definition(
1956        family="test_ecs_task", containerDefinitions=[container_definition]
1957    )
1958    family = task_definition["taskDefinition"]["family"]
1959    task = client.describe_task_definition(taskDefinition=family)["taskDefinition"]
1960    task["containerDefinitions"][0].should.equal(
1961        dict(
1962            container_definition,
1963            **{"mountPoints": [], "portMappings": [], "volumesFrom": []},
1964        )
1965    )
1966    task["taskDefinitionArn"].should.equal(
1967        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
1968    )
1969    task["volumes"].should.equal([])
1970    task["status"].should.equal("ACTIVE")
1971
1972
1973@mock_ec2
1974@mock_ecs
1975def test_stop_task():
1976    client = boto3.client("ecs", region_name="us-east-1")
1977    ec2 = boto3.resource("ec2", region_name="us-east-1")
1978
1979    test_cluster_name = "test_ecs_cluster"
1980
1981    _ = client.create_cluster(clusterName=test_cluster_name)
1982
1983    test_instance = ec2.create_instances(
1984        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
1985    )[0]
1986
1987    instance_id_document = json.dumps(
1988        ec2_utils.generate_instance_identity_document(test_instance)
1989    )
1990
1991    _ = client.register_container_instance(
1992        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
1993    )
1994
1995    _ = client.register_task_definition(
1996        family="test_ecs_task",
1997        containerDefinitions=[
1998            {
1999                "name": "hello_world",
2000                "image": "docker/hello-world:latest",
2001                "cpu": 1024,
2002                "memory": 400,
2003                "essential": True,
2004                "environment": [
2005                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2006                ],
2007                "logConfiguration": {"logDriver": "json-file"},
2008            }
2009        ],
2010    )
2011    run_response = client.run_task(
2012        cluster="test_ecs_cluster",
2013        overrides={},
2014        taskDefinition="test_ecs_task",
2015        count=1,
2016        startedBy="moto",
2017    )
2018    stop_response = client.stop_task(
2019        cluster="test_ecs_cluster",
2020        task=run_response["tasks"][0].get("taskArn"),
2021        reason="moto testing",
2022    )
2023
2024    stop_response["task"]["taskArn"].should.equal(
2025        run_response["tasks"][0].get("taskArn")
2026    )
2027    stop_response["task"]["lastStatus"].should.equal("STOPPED")
2028    stop_response["task"]["desiredStatus"].should.equal("STOPPED")
2029    stop_response["task"]["stoppedReason"].should.equal("moto testing")
2030
2031
2032@mock_ecs
2033def test_stop_task_exceptions():
2034    client = boto3.client("ecs", region_name="us-east-1")
2035
2036    client.stop_task.when.called_with(task="fake_task").should.throw(
2037        ClientError, ClusterNotFoundException().message
2038    )
2039
2040
2041@mock_ec2
2042@mock_ecs
2043def test_resource_reservation_and_release():
2044    client = boto3.client("ecs", region_name="us-east-1")
2045    ec2 = boto3.resource("ec2", region_name="us-east-1")
2046
2047    test_cluster_name = "test_ecs_cluster"
2048
2049    _ = client.create_cluster(clusterName=test_cluster_name)
2050
2051    test_instance = ec2.create_instances(
2052        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2053    )[0]
2054
2055    instance_id_document = json.dumps(
2056        ec2_utils.generate_instance_identity_document(test_instance)
2057    )
2058
2059    _ = client.register_container_instance(
2060        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2061    )
2062
2063    _ = client.register_task_definition(
2064        family="test_ecs_task",
2065        containerDefinitions=[
2066            {
2067                "name": "hello_world",
2068                "image": "docker/hello-world:latest",
2069                "cpu": 1024,
2070                "memory": 400,
2071                "essential": True,
2072                "environment": [
2073                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2074                ],
2075                "logConfiguration": {"logDriver": "json-file"},
2076                "portMappings": [{"hostPort": 80, "containerPort": 8080}],
2077            }
2078        ],
2079    )
2080    run_response = client.run_task(
2081        cluster="test_ecs_cluster",
2082        overrides={},
2083        taskDefinition="test_ecs_task",
2084        count=1,
2085        startedBy="moto",
2086    )
2087    container_instance_arn = run_response["tasks"][0].get("containerInstanceArn")
2088    container_instance_description = client.describe_container_instances(
2089        cluster="test_ecs_cluster", containerInstances=[container_instance_arn]
2090    )["containerInstances"][0]
2091    remaining_resources, registered_resources = _fetch_container_instance_resources(
2092        container_instance_description
2093    )
2094    remaining_resources["CPU"].should.equal(registered_resources["CPU"] - 1024)
2095    remaining_resources["MEMORY"].should.equal(registered_resources["MEMORY"] - 400)
2096    registered_resources["PORTS"].append("80")
2097    remaining_resources["PORTS"].should.equal(registered_resources["PORTS"])
2098    container_instance_description["runningTasksCount"].should.equal(1)
2099    client.stop_task(
2100        cluster="test_ecs_cluster",
2101        task=run_response["tasks"][0].get("taskArn"),
2102        reason="moto testing",
2103    )
2104    container_instance_description = client.describe_container_instances(
2105        cluster="test_ecs_cluster", containerInstances=[container_instance_arn]
2106    )["containerInstances"][0]
2107    remaining_resources, registered_resources = _fetch_container_instance_resources(
2108        container_instance_description
2109    )
2110    remaining_resources["CPU"].should.equal(registered_resources["CPU"])
2111    remaining_resources["MEMORY"].should.equal(registered_resources["MEMORY"])
2112    remaining_resources["PORTS"].should.equal(registered_resources["PORTS"])
2113    container_instance_description["runningTasksCount"].should.equal(0)
2114
2115
2116@mock_ec2
2117@mock_ecs
2118def test_resource_reservation_and_release_memory_reservation():
2119    client = boto3.client("ecs", region_name="us-east-1")
2120    ec2 = boto3.resource("ec2", region_name="us-east-1")
2121
2122    test_cluster_name = "test_ecs_cluster"
2123
2124    _ = client.create_cluster(clusterName=test_cluster_name)
2125
2126    test_instance = ec2.create_instances(
2127        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2128    )[0]
2129
2130    instance_id_document = json.dumps(
2131        ec2_utils.generate_instance_identity_document(test_instance)
2132    )
2133
2134    _ = client.register_container_instance(
2135        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2136    )
2137
2138    _ = client.register_task_definition(
2139        family="test_ecs_task",
2140        containerDefinitions=[
2141            {
2142                "name": "hello_world",
2143                "image": "docker/hello-world:latest",
2144                "memoryReservation": 400,
2145                "essential": True,
2146                "environment": [
2147                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2148                ],
2149                "logConfiguration": {"logDriver": "json-file"},
2150                "portMappings": [{"containerPort": 8080}],
2151            }
2152        ],
2153    )
2154    run_response = client.run_task(
2155        cluster="test_ecs_cluster",
2156        overrides={},
2157        taskDefinition="test_ecs_task",
2158        count=1,
2159        startedBy="moto",
2160    )
2161    container_instance_arn = run_response["tasks"][0].get("containerInstanceArn")
2162    container_instance_description = client.describe_container_instances(
2163        cluster="test_ecs_cluster", containerInstances=[container_instance_arn]
2164    )["containerInstances"][0]
2165    remaining_resources, registered_resources = _fetch_container_instance_resources(
2166        container_instance_description
2167    )
2168    remaining_resources["CPU"].should.equal(registered_resources["CPU"])
2169    remaining_resources["MEMORY"].should.equal(registered_resources["MEMORY"] - 400)
2170    remaining_resources["PORTS"].should.equal(registered_resources["PORTS"])
2171    container_instance_description["runningTasksCount"].should.equal(1)
2172    client.stop_task(
2173        cluster="test_ecs_cluster",
2174        task=run_response["tasks"][0].get("taskArn"),
2175        reason="moto testing",
2176    )
2177    container_instance_description = client.describe_container_instances(
2178        cluster="test_ecs_cluster", containerInstances=[container_instance_arn]
2179    )["containerInstances"][0]
2180    remaining_resources, registered_resources = _fetch_container_instance_resources(
2181        container_instance_description
2182    )
2183    remaining_resources["CPU"].should.equal(registered_resources["CPU"])
2184    remaining_resources["MEMORY"].should.equal(registered_resources["MEMORY"])
2185    remaining_resources["PORTS"].should.equal(registered_resources["PORTS"])
2186    container_instance_description["runningTasksCount"].should.equal(0)
2187
2188
2189@mock_ec2
2190@mock_ecs
2191def test_task_definitions_unable_to_be_placed():
2192    client = boto3.client("ecs", region_name="us-east-1")
2193    ec2 = boto3.resource("ec2", region_name="us-east-1")
2194
2195    test_cluster_name = "test_ecs_cluster"
2196
2197    _ = client.create_cluster(clusterName=test_cluster_name)
2198
2199    test_instance = ec2.create_instances(
2200        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2201    )[0]
2202
2203    instance_id_document = json.dumps(
2204        ec2_utils.generate_instance_identity_document(test_instance)
2205    )
2206
2207    response = client.register_container_instance(
2208        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2209    )
2210
2211    _ = client.register_task_definition(
2212        family="test_ecs_task",
2213        containerDefinitions=[
2214            {
2215                "name": "hello_world",
2216                "image": "docker/hello-world:latest",
2217                "cpu": 5000,
2218                "memory": 40000,
2219                "essential": True,
2220                "environment": [
2221                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2222                ],
2223                "logConfiguration": {"logDriver": "json-file"},
2224            }
2225        ],
2226    )
2227    response = client.run_task(
2228        cluster="test_ecs_cluster",
2229        overrides={},
2230        taskDefinition="test_ecs_task",
2231        count=2,
2232        startedBy="moto",
2233    )
2234    len(response["tasks"]).should.equal(0)
2235
2236
2237@mock_ec2
2238@mock_ecs
2239def test_task_definitions_with_port_clash():
2240    client = boto3.client("ecs", region_name="us-east-1")
2241    ec2 = boto3.resource("ec2", region_name="us-east-1")
2242
2243    test_cluster_name = "test_ecs_cluster"
2244
2245    _ = client.create_cluster(clusterName=test_cluster_name)
2246
2247    test_instance = ec2.create_instances(
2248        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2249    )[0]
2250
2251    instance_id_document = json.dumps(
2252        ec2_utils.generate_instance_identity_document(test_instance)
2253    )
2254
2255    response = client.register_container_instance(
2256        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2257    )
2258
2259    _ = client.register_task_definition(
2260        family="test_ecs_task",
2261        containerDefinitions=[
2262            {
2263                "name": "hello_world",
2264                "image": "docker/hello-world:latest",
2265                "cpu": 256,
2266                "memory": 512,
2267                "essential": True,
2268                "environment": [
2269                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2270                ],
2271                "logConfiguration": {"logDriver": "json-file"},
2272                "portMappings": [{"hostPort": 80, "containerPort": 8080}],
2273            }
2274        ],
2275    )
2276    response = client.run_task(
2277        cluster="test_ecs_cluster",
2278        overrides={},
2279        taskDefinition="test_ecs_task",
2280        count=2,
2281        startedBy="moto",
2282    )
2283    len(response["tasks"]).should.equal(1)
2284    response["tasks"][0]["taskArn"].should.contain(
2285        "arn:aws:ecs:us-east-1:{}:task/".format(ACCOUNT_ID)
2286    )
2287    response["tasks"][0]["clusterArn"].should.equal(
2288        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
2289    )
2290    response["tasks"][0]["taskDefinitionArn"].should.equal(
2291        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
2292    )
2293    response["tasks"][0]["containerInstanceArn"].should.contain(
2294        "arn:aws:ecs:us-east-1:{}:container-instance/".format(ACCOUNT_ID)
2295    )
2296    response["tasks"][0]["overrides"].should.equal({})
2297    response["tasks"][0]["lastStatus"].should.equal("RUNNING")
2298    response["tasks"][0]["desiredStatus"].should.equal("RUNNING")
2299    response["tasks"][0]["startedBy"].should.equal("moto")
2300    response["tasks"][0]["stoppedReason"].should.equal("")
2301
2302
2303@mock_ec2
2304@mock_ecs
2305def test_attributes():
2306    # Combined put, list delete attributes into the same test due to the amount of setup
2307    ecs_client = boto3.client("ecs", region_name="us-east-1")
2308    ec2 = boto3.resource("ec2", region_name="us-east-1")
2309
2310    test_cluster_name = "test_ecs_cluster"
2311
2312    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
2313
2314    instances = []
2315    test_instance = ec2.create_instances(
2316        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2317    )[0]
2318    instances.append(test_instance)
2319
2320    instance_id_document = json.dumps(
2321        ec2_utils.generate_instance_identity_document(test_instance)
2322    )
2323
2324    response = ecs_client.register_container_instance(
2325        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2326    )
2327
2328    response["containerInstance"]["ec2InstanceId"].should.equal(test_instance.id)
2329    full_arn1 = response["containerInstance"]["containerInstanceArn"]
2330
2331    test_instance = ec2.create_instances(
2332        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2333    )[0]
2334    instances.append(test_instance)
2335
2336    instance_id_document = json.dumps(
2337        ec2_utils.generate_instance_identity_document(test_instance)
2338    )
2339
2340    response = ecs_client.register_container_instance(
2341        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2342    )
2343
2344    response["containerInstance"]["ec2InstanceId"].should.equal(test_instance.id)
2345    full_arn2 = response["containerInstance"]["containerInstanceArn"]
2346    partial_arn2 = full_arn2.rsplit("/", 1)[-1]
2347
2348    full_arn2.should_not.equal(
2349        full_arn1
2350    )  # uuid1 isnt unique enough when the pc is fast ;-)
2351
2352    # Ok set instance 1 with 1 attribute, instance 2 with another, and all of them with a 3rd.
2353    ecs_client.put_attributes(
2354        cluster=test_cluster_name,
2355        attributes=[
2356            {"name": "env", "value": "prod"},
2357            {"name": "attr1", "value": "instance1", "targetId": full_arn1},
2358            {
2359                "name": "attr1",
2360                "value": "instance2",
2361                "targetId": partial_arn2,
2362                "targetType": "container-instance",
2363            },
2364        ],
2365    )
2366
2367    resp = ecs_client.list_attributes(
2368        cluster=test_cluster_name, targetType="container-instance"
2369    )
2370    attrs = resp["attributes"]
2371
2372    NUM_CUSTOM_ATTRIBUTES = 4  # 2 specific to individual machines and 1 global, going to both machines (2 + 1*2)
2373    NUM_DEFAULT_ATTRIBUTES = 4
2374    len(attrs).should.equal(
2375        NUM_CUSTOM_ATTRIBUTES + (NUM_DEFAULT_ATTRIBUTES * len(instances))
2376    )
2377
2378    # Tests that the attrs have been set properly
2379    len(list(filter(lambda item: item["name"] == "env", attrs))).should.equal(2)
2380    len(
2381        list(
2382            filter(
2383                lambda item: item["name"] == "attr1" and item["value"] == "instance1",
2384                attrs,
2385            )
2386        )
2387    ).should.equal(1)
2388
2389    ecs_client.delete_attributes(
2390        cluster=test_cluster_name,
2391        attributes=[
2392            {
2393                "name": "attr1",
2394                "value": "instance2",
2395                "targetId": partial_arn2,
2396                "targetType": "container-instance",
2397            }
2398        ],
2399    )
2400    NUM_CUSTOM_ATTRIBUTES -= 1
2401
2402    resp = ecs_client.list_attributes(
2403        cluster=test_cluster_name, targetType="container-instance"
2404    )
2405    attrs = resp["attributes"]
2406    len(attrs).should.equal(
2407        NUM_CUSTOM_ATTRIBUTES + (NUM_DEFAULT_ATTRIBUTES * len(instances))
2408    )
2409
2410
2411@mock_ecs
2412def test_poll_endpoint():
2413    # Combined put, list delete attributes into the same test due to the amount of setup
2414    ecs_client = boto3.client("ecs", region_name="us-east-1")
2415
2416    # Just a placeholder until someone actually wants useless data, just testing it doesnt raise an exception
2417    resp = ecs_client.discover_poll_endpoint(cluster="blah", containerInstance="blah")
2418    resp.should.contain("endpoint")
2419    resp.should.contain("telemetryEndpoint")
2420
2421
2422@mock_ecs
2423def test_list_task_definition_families():
2424    client = boto3.client("ecs", region_name="us-east-1")
2425    client.register_task_definition(
2426        family="test_ecs_task",
2427        containerDefinitions=[
2428            {
2429                "name": "hello_world",
2430                "image": "docker/hello-world:latest",
2431                "cpu": 1024,
2432                "memory": 400,
2433                "essential": True,
2434                "environment": [
2435                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2436                ],
2437                "logConfiguration": {"logDriver": "json-file"},
2438            }
2439        ],
2440    )
2441    client.register_task_definition(
2442        family="alt_test_ecs_task",
2443        containerDefinitions=[
2444            {
2445                "name": "hello_world",
2446                "image": "docker/hello-world:latest",
2447                "cpu": 1024,
2448                "memory": 400,
2449                "essential": True,
2450                "environment": [
2451                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2452                ],
2453                "logConfiguration": {"logDriver": "json-file"},
2454            }
2455        ],
2456    )
2457
2458    resp1 = client.list_task_definition_families()
2459    resp2 = client.list_task_definition_families(familyPrefix="alt")
2460
2461    len(resp1["families"]).should.equal(2)
2462    len(resp2["families"]).should.equal(1)
2463
2464
2465@mock_ec2
2466@mock_ecs
2467def test_default_container_instance_attributes():
2468    ecs_client = boto3.client("ecs", region_name="us-east-1")
2469    ec2 = boto3.resource("ec2", region_name="us-east-1")
2470
2471    test_cluster_name = "test_ecs_cluster"
2472
2473    # Create cluster and EC2 instance
2474    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
2475
2476    test_instance = ec2.create_instances(
2477        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2478    )[0]
2479
2480    instance_id_document = json.dumps(
2481        ec2_utils.generate_instance_identity_document(test_instance)
2482    )
2483
2484    # Register container instance
2485    response = ecs_client.register_container_instance(
2486        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2487    )
2488
2489    response["containerInstance"]["ec2InstanceId"].should.equal(test_instance.id)
2490
2491    default_attributes = response["containerInstance"]["attributes"]
2492    assert len(default_attributes) == 4
2493    expected_result = [
2494        {
2495            "name": "ecs.availability-zone",
2496            "value": test_instance.placement["AvailabilityZone"],
2497        },
2498        {"name": "ecs.ami-id", "value": test_instance.image_id},
2499        {"name": "ecs.instance-type", "value": test_instance.instance_type},
2500        {"name": "ecs.os-type", "value": test_instance.platform or "linux"},
2501    ]
2502    assert sorted(default_attributes, key=lambda item: item["name"]) == sorted(
2503        expected_result, key=lambda item: item["name"]
2504    )
2505
2506
2507@mock_ec2
2508@mock_ecs
2509def test_describe_container_instances_with_attributes():
2510    ecs_client = boto3.client("ecs", region_name="us-east-1")
2511    ec2 = boto3.resource("ec2", region_name="us-east-1")
2512
2513    test_cluster_name = "test_ecs_cluster"
2514
2515    # Create cluster and EC2 instance
2516    _ = ecs_client.create_cluster(clusterName=test_cluster_name)
2517
2518    test_instance = ec2.create_instances(
2519        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
2520    )[0]
2521
2522    instance_id_document = json.dumps(
2523        ec2_utils.generate_instance_identity_document(test_instance)
2524    )
2525
2526    # Register container instance
2527    response = ecs_client.register_container_instance(
2528        cluster=test_cluster_name, instanceIdentityDocument=instance_id_document
2529    )
2530
2531    response["containerInstance"]["ec2InstanceId"].should.equal(test_instance.id)
2532    full_arn = response["containerInstance"]["containerInstanceArn"]
2533    container_instance_id = full_arn.rsplit("/", 1)[-1]
2534    default_attributes = response["containerInstance"]["attributes"]
2535
2536    # Set attributes on container instance, one without a value
2537    attributes = [
2538        {"name": "env", "value": "prod"},
2539        {
2540            "name": "attr1",
2541            "value": "instance1",
2542            "targetId": container_instance_id,
2543            "targetType": "container-instance",
2544        },
2545        {"name": "attr_without_value"},
2546    ]
2547    ecs_client.put_attributes(cluster=test_cluster_name, attributes=attributes)
2548
2549    # Describe container instance, should have attributes previously set
2550    described_instance = ecs_client.describe_container_instances(
2551        cluster=test_cluster_name, containerInstances=[container_instance_id]
2552    )
2553
2554    assert len(described_instance["containerInstances"]) == 1
2555    assert isinstance(described_instance["containerInstances"][0]["attributes"], list)
2556
2557    # Remove additional info passed to put_attributes
2558    cleaned_attributes = []
2559    for attribute in attributes:
2560        attribute.pop("targetId", None)
2561        attribute.pop("targetType", None)
2562        cleaned_attributes.append(attribute)
2563    described_attributes = sorted(
2564        described_instance["containerInstances"][0]["attributes"],
2565        key=lambda item: item["name"],
2566    )
2567    expected_attributes = sorted(
2568        default_attributes + cleaned_attributes, key=lambda item: item["name"]
2569    )
2570    assert described_attributes == expected_attributes
2571
2572
2573def _fetch_container_instance_resources(container_instance_description):
2574    remaining_resources = {}
2575    registered_resources = {}
2576    remaining_resources_list = container_instance_description["remainingResources"]
2577    registered_resources_list = container_instance_description["registeredResources"]
2578    remaining_resources["CPU"] = [
2579        x["integerValue"] for x in remaining_resources_list if x["name"] == "CPU"
2580    ][0]
2581    remaining_resources["MEMORY"] = [
2582        x["integerValue"] for x in remaining_resources_list if x["name"] == "MEMORY"
2583    ][0]
2584    remaining_resources["PORTS"] = [
2585        x["stringSetValue"] for x in remaining_resources_list if x["name"] == "PORTS"
2586    ][0]
2587    registered_resources["CPU"] = [
2588        x["integerValue"] for x in registered_resources_list if x["name"] == "CPU"
2589    ][0]
2590    registered_resources["MEMORY"] = [
2591        x["integerValue"] for x in registered_resources_list if x["name"] == "MEMORY"
2592    ][0]
2593    registered_resources["PORTS"] = [
2594        x["stringSetValue"] for x in registered_resources_list if x["name"] == "PORTS"
2595    ][0]
2596    return remaining_resources, registered_resources
2597
2598
2599@mock_ecs
2600def test_create_service_load_balancing():
2601    client = boto3.client("ecs", region_name="us-east-1")
2602    client.create_cluster(clusterName="test_ecs_cluster")
2603    client.register_task_definition(
2604        family="test_ecs_task",
2605        containerDefinitions=[
2606            {
2607                "name": "hello_world",
2608                "image": "docker/hello-world:latest",
2609                "cpu": 1024,
2610                "memory": 400,
2611                "essential": True,
2612                "environment": [
2613                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2614                ],
2615                "logConfiguration": {"logDriver": "json-file"},
2616            }
2617        ],
2618    )
2619    response = client.create_service(
2620        cluster="test_ecs_cluster",
2621        serviceName="test_ecs_service",
2622        taskDefinition="test_ecs_task",
2623        desiredCount=2,
2624        loadBalancers=[
2625            {
2626                "targetGroupArn": "test_target_group_arn",
2627                "loadBalancerName": "test_load_balancer_name",
2628                "containerName": "test_container_name",
2629                "containerPort": 123,
2630            }
2631        ],
2632    )
2633    response["service"]["clusterArn"].should.equal(
2634        "arn:aws:ecs:us-east-1:{}:cluster/test_ecs_cluster".format(ACCOUNT_ID)
2635    )
2636    response["service"]["desiredCount"].should.equal(2)
2637    len(response["service"]["events"]).should.equal(0)
2638    len(response["service"]["loadBalancers"]).should.equal(1)
2639    response["service"]["loadBalancers"][0]["targetGroupArn"].should.equal(
2640        "test_target_group_arn"
2641    )
2642    response["service"]["loadBalancers"][0]["loadBalancerName"].should.equal(
2643        "test_load_balancer_name"
2644    )
2645    response["service"]["loadBalancers"][0]["containerName"].should.equal(
2646        "test_container_name"
2647    )
2648    response["service"]["loadBalancers"][0]["containerPort"].should.equal(123)
2649    response["service"]["pendingCount"].should.equal(0)
2650    response["service"]["runningCount"].should.equal(0)
2651    response["service"]["serviceArn"].should.equal(
2652        "arn:aws:ecs:us-east-1:{}:service/test_ecs_service".format(ACCOUNT_ID)
2653    )
2654    response["service"]["serviceName"].should.equal("test_ecs_service")
2655    response["service"]["status"].should.equal("ACTIVE")
2656    response["service"]["taskDefinition"].should.equal(
2657        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
2658    )
2659
2660
2661@mock_ecs
2662def test_list_tags_for_resource():
2663    client = boto3.client("ecs", region_name="us-east-1")
2664    response = client.register_task_definition(
2665        family="test_ecs_task",
2666        containerDefinitions=[
2667            {
2668                "name": "hello_world",
2669                "image": "docker/hello-world:latest",
2670                "cpu": 1024,
2671                "memory": 400,
2672                "essential": True,
2673                "environment": [
2674                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2675                ],
2676                "logConfiguration": {"logDriver": "json-file"},
2677            }
2678        ],
2679        tags=[
2680            {"key": "createdBy", "value": "moto-unittest"},
2681            {"key": "foo", "value": "bar"},
2682        ],
2683    )
2684    type(response["taskDefinition"]).should.be(dict)
2685    response["taskDefinition"]["revision"].should.equal(1)
2686    response["taskDefinition"]["taskDefinitionArn"].should.equal(
2687        "arn:aws:ecs:us-east-1:{}:task-definition/test_ecs_task:1".format(ACCOUNT_ID)
2688    )
2689
2690    task_definition_arn = response["taskDefinition"]["taskDefinitionArn"]
2691    response = client.list_tags_for_resource(resourceArn=task_definition_arn)
2692
2693    type(response["tags"]).should.be(list)
2694    response["tags"].should.equal(
2695        [{"key": "createdBy", "value": "moto-unittest"}, {"key": "foo", "value": "bar"}]
2696    )
2697
2698
2699@mock_ecs
2700def test_list_tags_exceptions():
2701    client = boto3.client("ecs", region_name="us-east-1")
2702    client.list_tags_for_resource.when.called_with(
2703        resourceArn="arn:aws:ecs:us-east-1:012345678910:service/fake_service:1"
2704    ).should.throw(ClientError, ServiceNotFoundException().message)
2705    client.list_tags_for_resource.when.called_with(
2706        resourceArn="arn:aws:ecs:us-east-1:012345678910:task-definition/fake_task:1"
2707    ).should.throw(ClientError, TaskDefinitionNotFoundException().message)
2708
2709
2710@mock_ecs
2711def test_list_tags_for_resource_ecs_service():
2712    client = boto3.client("ecs", region_name="us-east-1")
2713    _ = client.create_cluster(clusterName="test_ecs_cluster")
2714    _ = client.register_task_definition(
2715        family="test_ecs_task",
2716        containerDefinitions=[
2717            {
2718                "name": "hello_world",
2719                "image": "docker/hello-world:latest",
2720                "cpu": 1024,
2721                "memory": 400,
2722                "essential": True,
2723                "environment": [
2724                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2725                ],
2726                "logConfiguration": {"logDriver": "json-file"},
2727            }
2728        ],
2729    )
2730    response = client.create_service(
2731        cluster="test_ecs_cluster",
2732        serviceName="test_ecs_service",
2733        taskDefinition="test_ecs_task",
2734        desiredCount=2,
2735        tags=[
2736            {"key": "createdBy", "value": "moto-unittest"},
2737            {"key": "foo", "value": "bar"},
2738        ],
2739    )
2740    response = client.list_tags_for_resource(
2741        resourceArn=response["service"]["serviceArn"]
2742    )
2743    type(response["tags"]).should.be(list)
2744    response["tags"].should.equal(
2745        [{"key": "createdBy", "value": "moto-unittest"}, {"key": "foo", "value": "bar"}]
2746    )
2747
2748
2749@mock_ecs
2750def test_ecs_service_tag_resource():
2751    client = boto3.client("ecs", region_name="us-east-1")
2752    _ = client.create_cluster(clusterName="test_ecs_cluster")
2753    _ = client.register_task_definition(
2754        family="test_ecs_task",
2755        containerDefinitions=[
2756            {
2757                "name": "hello_world",
2758                "image": "docker/hello-world:latest",
2759                "cpu": 1024,
2760                "memory": 400,
2761                "essential": True,
2762                "environment": [
2763                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2764                ],
2765                "logConfiguration": {"logDriver": "json-file"},
2766            }
2767        ],
2768    )
2769    response = client.create_service(
2770        cluster="test_ecs_cluster",
2771        serviceName="test_ecs_service",
2772        taskDefinition="test_ecs_task",
2773        desiredCount=2,
2774    )
2775    client.tag_resource(
2776        resourceArn=response["service"]["serviceArn"],
2777        tags=[
2778            {"key": "createdBy", "value": "moto-unittest"},
2779            {"key": "foo", "value": "bar"},
2780        ],
2781    )
2782    response = client.list_tags_for_resource(
2783        resourceArn=response["service"]["serviceArn"]
2784    )
2785    type(response["tags"]).should.be(list)
2786    response["tags"].should.equal(
2787        [{"key": "createdBy", "value": "moto-unittest"}, {"key": "foo", "value": "bar"}]
2788    )
2789
2790
2791@mock_ecs
2792def test_ecs_service_tag_resource_overwrites_tag():
2793    client = boto3.client("ecs", region_name="us-east-1")
2794    _ = client.create_cluster(clusterName="test_ecs_cluster")
2795    _ = client.register_task_definition(
2796        family="test_ecs_task",
2797        containerDefinitions=[
2798            {
2799                "name": "hello_world",
2800                "image": "docker/hello-world:latest",
2801                "cpu": 1024,
2802                "memory": 400,
2803                "essential": True,
2804                "environment": [
2805                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2806                ],
2807                "logConfiguration": {"logDriver": "json-file"},
2808            }
2809        ],
2810    )
2811    response = client.create_service(
2812        cluster="test_ecs_cluster",
2813        serviceName="test_ecs_service",
2814        taskDefinition="test_ecs_task",
2815        desiredCount=2,
2816        tags=[{"key": "foo", "value": "bar"}],
2817    )
2818    client.tag_resource(
2819        resourceArn=response["service"]["serviceArn"],
2820        tags=[
2821            {"key": "createdBy", "value": "moto-unittest"},
2822            {"key": "foo", "value": "hello world"},
2823        ],
2824    )
2825    response = client.list_tags_for_resource(
2826        resourceArn=response["service"]["serviceArn"]
2827    )
2828    type(response["tags"]).should.be(list)
2829    response["tags"].should.equal(
2830        [
2831            {"key": "createdBy", "value": "moto-unittest"},
2832            {"key": "foo", "value": "hello world"},
2833        ]
2834    )
2835
2836
2837@mock_ecs
2838def test_ecs_service_untag_resource():
2839    client = boto3.client("ecs", region_name="us-east-1")
2840    _ = client.create_cluster(clusterName="test_ecs_cluster")
2841    _ = client.register_task_definition(
2842        family="test_ecs_task",
2843        containerDefinitions=[
2844            {
2845                "name": "hello_world",
2846                "image": "docker/hello-world:latest",
2847                "cpu": 1024,
2848                "memory": 400,
2849                "essential": True,
2850                "environment": [
2851                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2852                ],
2853                "logConfiguration": {"logDriver": "json-file"},
2854            }
2855        ],
2856    )
2857    response = client.create_service(
2858        cluster="test_ecs_cluster",
2859        serviceName="test_ecs_service",
2860        taskDefinition="test_ecs_task",
2861        desiredCount=2,
2862        tags=[{"key": "foo", "value": "bar"}],
2863    )
2864    client.untag_resource(
2865        resourceArn=response["service"]["serviceArn"], tagKeys=["foo"]
2866    )
2867    response = client.list_tags_for_resource(
2868        resourceArn=response["service"]["serviceArn"]
2869    )
2870    response["tags"].should.equal([])
2871
2872
2873@mock_ecs
2874def test_ecs_service_untag_resource_multiple_tags():
2875    client = boto3.client("ecs", region_name="us-east-1")
2876    _ = client.create_cluster(clusterName="test_ecs_cluster")
2877    _ = client.register_task_definition(
2878        family="test_ecs_task",
2879        containerDefinitions=[
2880            {
2881                "name": "hello_world",
2882                "image": "docker/hello-world:latest",
2883                "cpu": 1024,
2884                "memory": 400,
2885                "essential": True,
2886                "environment": [
2887                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2888                ],
2889                "logConfiguration": {"logDriver": "json-file"},
2890            }
2891        ],
2892    )
2893    response = client.create_service(
2894        cluster="test_ecs_cluster",
2895        serviceName="test_ecs_service",
2896        taskDefinition="test_ecs_task",
2897        desiredCount=2,
2898        tags=[
2899            {"key": "foo", "value": "bar"},
2900            {"key": "createdBy", "value": "moto-unittest"},
2901            {"key": "hello", "value": "world"},
2902        ],
2903    )
2904    client.untag_resource(
2905        resourceArn=response["service"]["serviceArn"], tagKeys=["foo", "createdBy"]
2906    )
2907    response = client.list_tags_for_resource(
2908        resourceArn=response["service"]["serviceArn"]
2909    )
2910    response["tags"].should.equal([{"key": "hello", "value": "world"}])
2911
2912
2913@mock_ecs
2914def test_ecs_task_definition_placement_constraints():
2915    client = boto3.client("ecs", region_name="us-east-1")
2916    response = client.register_task_definition(
2917        family="test_ecs_task",
2918        containerDefinitions=[
2919            {
2920                "name": "hello_world",
2921                "image": "docker/hello-world:latest",
2922                "cpu": 1024,
2923                "memory": 400,
2924                "essential": True,
2925                "environment": [
2926                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2927                ],
2928                "logConfiguration": {"logDriver": "json-file"},
2929            }
2930        ],
2931        networkMode="bridge",
2932        tags=[
2933            {"key": "createdBy", "value": "moto-unittest"},
2934            {"key": "foo", "value": "bar"},
2935        ],
2936        placementConstraints=[
2937            {"type": "memberOf", "expression": "attribute:ecs.instance-type =~ t2.*"}
2938        ],
2939    )
2940    type(response["taskDefinition"]["placementConstraints"]).should.be(list)
2941    response["taskDefinition"]["placementConstraints"].should.equal(
2942        [{"type": "memberOf", "expression": "attribute:ecs.instance-type =~ t2.*"}]
2943    )
2944
2945
2946@mock_ecs
2947def test_create_task_set():
2948    cluster_name = "test_ecs_cluster"
2949    service_name = "test_ecs_service"
2950    task_def_name = "test_ecs_task"
2951
2952    client = boto3.client("ecs", region_name="us-east-1")
2953    _ = client.create_cluster(clusterName=cluster_name)
2954    _ = client.register_task_definition(
2955        family="test_ecs_task",
2956        containerDefinitions=[
2957            {
2958                "name": "hello_world",
2959                "image": "docker/hello-world:latest",
2960                "cpu": 1024,
2961                "memory": 400,
2962                "essential": True,
2963                "environment": [
2964                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
2965                ],
2966                "logConfiguration": {"logDriver": "json-file"},
2967            }
2968        ],
2969    )
2970    _ = client.create_service(
2971        cluster=cluster_name,
2972        serviceName=service_name,
2973        taskDefinition=task_def_name,
2974        desiredCount=2,
2975        deploymentController={"type": "EXTERNAL"},
2976    )
2977    load_balancers = [
2978        {
2979            "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:01234567890:targetgroup/c26b93c1bc35466ba792d5b08fe6a5bc/ec39113f8831453a",
2980            "containerName": "hello_world",
2981            "containerPort": 8080,
2982        },
2983    ]
2984
2985    task_set = client.create_task_set(
2986        cluster=cluster_name,
2987        service=service_name,
2988        taskDefinition=task_def_name,
2989        loadBalancers=load_balancers,
2990    )["taskSet"]
2991
2992    cluster_arn = client.describe_clusters(clusters=[cluster_name])["clusters"][0][
2993        "clusterArn"
2994    ]
2995    service_arn = client.describe_services(
2996        cluster=cluster_name, services=[service_name]
2997    )["services"][0]["serviceArn"]
2998    task_set["clusterArn"].should.equal(cluster_arn)
2999    task_set["serviceArn"].should.equal(service_arn)
3000    task_set["taskDefinition"].should.match("{0}:1$".format(task_def_name))
3001    task_set["scale"].should.equal({"value": 100.0, "unit": "PERCENT"})
3002    task_set["loadBalancers"][0]["targetGroupArn"].should.equal(
3003        "arn:aws:elasticloadbalancing:us-east-1:01234567890:targetgroup/"
3004        "c26b93c1bc35466ba792d5b08fe6a5bc/ec39113f8831453a"
3005    )
3006    task_set["loadBalancers"][0]["containerPort"].should.equal(8080)
3007    task_set["loadBalancers"][0]["containerName"].should.equal("hello_world")
3008    task_set["launchType"].should.equal("EC2")
3009
3010
3011@mock_ecs
3012def test_create_task_set_errors():
3013    # given
3014    cluster_name = "test_ecs_cluster"
3015    service_name = "test_ecs_service"
3016    task_def_name = "test_ecs_task"
3017
3018    client = boto3.client("ecs", region_name="us-east-1")
3019    _ = client.create_cluster(clusterName=cluster_name)
3020    _ = client.register_task_definition(
3021        family="test_ecs_task",
3022        containerDefinitions=[
3023            {
3024                "name": "hello_world",
3025                "image": "docker/hello-world:latest",
3026                "cpu": 1024,
3027                "memory": 400,
3028                "essential": True,
3029                "environment": [
3030                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
3031                ],
3032                "logConfiguration": {"logDriver": "json-file"},
3033            }
3034        ],
3035    )
3036    _ = client.create_service(
3037        cluster=cluster_name,
3038        serviceName=service_name,
3039        taskDefinition=task_def_name,
3040        desiredCount=2,
3041        deploymentController={"type": "EXTERNAL"},
3042    )
3043
3044    # not existing launch type
3045    # when
3046    with pytest.raises(ClientError) as e:
3047        client.create_task_set(
3048            cluster=cluster_name,
3049            service=service_name,
3050            taskDefinition=task_def_name,
3051            launchType="SOMETHING",
3052        )
3053
3054    # then
3055    ex = e.value
3056    ex.operation_name.should.equal("CreateTaskSet")
3057    ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
3058    ex.response["Error"]["Code"].should.contain("ClientException")
3059    ex.response["Error"]["Message"].should.equal(
3060        "launch type should be one of [EC2,FARGATE]"
3061    )
3062
3063
3064@mock_ecs
3065def test_describe_task_sets():
3066    cluster_name = "test_ecs_cluster"
3067    service_name = "test_ecs_service"
3068    task_def_name = "test_ecs_task"
3069
3070    client = boto3.client("ecs", region_name="us-east-1")
3071    _ = client.create_cluster(clusterName=cluster_name)
3072    _ = client.register_task_definition(
3073        family=task_def_name,
3074        containerDefinitions=[
3075            {
3076                "name": "hello_world",
3077                "image": "docker/hello-world:latest",
3078                "cpu": 1024,
3079                "memory": 400,
3080                "essential": True,
3081                "environment": [
3082                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
3083                ],
3084                "logConfiguration": {"logDriver": "json-file"},
3085            }
3086        ],
3087    )
3088    _ = client.create_service(
3089        cluster=cluster_name,
3090        serviceName=service_name,
3091        taskDefinition=task_def_name,
3092        desiredCount=2,
3093        deploymentController={"type": "EXTERNAL"},
3094    )
3095
3096    load_balancers = [
3097        {
3098            "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:01234567890:targetgroup/c26b93c1bc35466ba792d5b08fe6a5bc/ec39113f8831453a",
3099            "containerName": "hello_world",
3100            "containerPort": 8080,
3101        }
3102    ]
3103
3104    _ = client.create_task_set(
3105        cluster=cluster_name,
3106        service=service_name,
3107        taskDefinition=task_def_name,
3108        loadBalancers=load_balancers,
3109    )
3110    task_sets = client.describe_task_sets(cluster=cluster_name, service=service_name)[
3111        "taskSets"
3112    ]
3113    assert "tags" not in task_sets[0]
3114
3115    task_sets = client.describe_task_sets(
3116        cluster=cluster_name, service=service_name, include=["TAGS"],
3117    )["taskSets"]
3118
3119    cluster_arn = client.describe_clusters(clusters=[cluster_name])["clusters"][0][
3120        "clusterArn"
3121    ]
3122
3123    service_arn = client.describe_services(
3124        cluster=cluster_name, services=[service_name]
3125    )["services"][0]["serviceArn"]
3126
3127    task_sets[0].should.have.key("tags")
3128    task_sets.should.have.length_of(1)
3129    task_sets[0]["taskDefinition"].should.match("{0}:1$".format(task_def_name))
3130    task_sets[0]["clusterArn"].should.equal(cluster_arn)
3131    task_sets[0]["serviceArn"].should.equal(service_arn)
3132    task_sets[0]["serviceArn"].should.match("{0}$".format(service_name))
3133    task_sets[0]["scale"].should.equal({"value": 100.0, "unit": "PERCENT"})
3134    task_sets[0]["taskSetArn"].should.match("{0}$".format(task_sets[0]["id"]))
3135    task_sets[0]["loadBalancers"][0]["targetGroupArn"].should.equal(
3136        "arn:aws:elasticloadbalancing:us-east-1:01234567890:targetgroup/"
3137        "c26b93c1bc35466ba792d5b08fe6a5bc/ec39113f8831453a"
3138    )
3139    task_sets[0]["loadBalancers"][0]["containerPort"].should.equal(8080)
3140    task_sets[0]["loadBalancers"][0]["containerName"].should.equal("hello_world")
3141    task_sets[0]["launchType"].should.equal("EC2")
3142
3143
3144@mock_ecs
3145def test_delete_task_set():
3146    cluster_name = "test_ecs_cluster"
3147    service_name = "test_ecs_service"
3148    task_def_name = "test_ecs_task"
3149
3150    client = boto3.client("ecs", region_name="us-east-1")
3151    _ = client.create_cluster(clusterName=cluster_name)
3152    _ = client.register_task_definition(
3153        family=task_def_name,
3154        containerDefinitions=[
3155            {
3156                "name": "hello_world",
3157                "image": "docker/hello-world:latest",
3158                "cpu": 1024,
3159                "memory": 400,
3160                "essential": True,
3161                "environment": [
3162                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
3163                ],
3164                "logConfiguration": {"logDriver": "json-file"},
3165            }
3166        ],
3167    )
3168    _ = client.create_service(
3169        cluster=cluster_name,
3170        serviceName=service_name,
3171        taskDefinition=task_def_name,
3172        desiredCount=2,
3173        deploymentController={"type": "EXTERNAL"},
3174    )
3175
3176    task_set = client.create_task_set(
3177        cluster=cluster_name, service=service_name, taskDefinition=task_def_name,
3178    )["taskSet"]
3179
3180    task_sets = client.describe_task_sets(
3181        cluster=cluster_name, service=service_name, taskSets=[task_set["taskSetArn"]],
3182    )["taskSets"]
3183
3184    assert len(task_sets) == 1
3185
3186    response = client.delete_task_set(
3187        cluster=cluster_name, service=service_name, taskSet=task_set["taskSetArn"],
3188    )
3189    assert response["taskSet"]["taskSetArn"] == task_set["taskSetArn"]
3190
3191    task_sets = client.describe_task_sets(
3192        cluster=cluster_name, service=service_name, taskSets=[task_set["taskSetArn"]],
3193    )["taskSets"]
3194
3195    assert len(task_sets) == 0
3196
3197    with pytest.raises(ClientError):
3198        _ = client.delete_task_set(
3199            cluster=cluster_name, service=service_name, taskSet=task_set["taskSetArn"],
3200        )
3201
3202
3203@mock_ecs
3204def test_update_service_primary_task_set():
3205    cluster_name = "test_ecs_cluster"
3206    service_name = "test_ecs_service"
3207    task_def_name = "test_ecs_task"
3208
3209    client = boto3.client("ecs", region_name="us-east-1")
3210    _ = client.create_cluster(clusterName=cluster_name)
3211    _ = client.register_task_definition(
3212        family="test_ecs_task",
3213        containerDefinitions=[
3214            {
3215                "name": "hello_world",
3216                "image": "docker/hello-world:latest",
3217                "cpu": 1024,
3218                "memory": 400,
3219                "essential": True,
3220                "environment": [
3221                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
3222                ],
3223                "logConfiguration": {"logDriver": "json-file"},
3224            }
3225        ],
3226    )
3227    _ = client.create_service(
3228        cluster=cluster_name,
3229        serviceName=service_name,
3230        desiredCount=2,
3231        deploymentController={"type": "EXTERNAL"},
3232    )
3233
3234    task_set = client.create_task_set(
3235        cluster=cluster_name, service=service_name, taskDefinition=task_def_name,
3236    )["taskSet"]
3237
3238    service = client.describe_services(cluster=cluster_name, services=[service_name],)[
3239        "services"
3240    ][0]
3241
3242    _ = client.update_service_primary_task_set(
3243        cluster=cluster_name,
3244        service=service_name,
3245        primaryTaskSet=task_set["taskSetArn"],
3246    )
3247
3248    service = client.describe_services(cluster=cluster_name, services=[service_name],)[
3249        "services"
3250    ][0]
3251    assert service["taskSets"][0]["status"] == "PRIMARY"
3252    assert service["taskDefinition"] == service["taskSets"][0]["taskDefinition"]
3253
3254    another_task_set = client.create_task_set(
3255        cluster=cluster_name, service=service_name, taskDefinition=task_def_name,
3256    )["taskSet"]
3257    service = client.describe_services(cluster=cluster_name, services=[service_name],)[
3258        "services"
3259    ][0]
3260    assert service["taskSets"][1]["status"] == "ACTIVE"
3261
3262    _ = client.update_service_primary_task_set(
3263        cluster=cluster_name,
3264        service=service_name,
3265        primaryTaskSet=another_task_set["taskSetArn"],
3266    )
3267    service = client.describe_services(cluster=cluster_name, services=[service_name],)[
3268        "services"
3269    ][0]
3270    assert service["taskSets"][0]["status"] == "ACTIVE"
3271    assert service["taskSets"][1]["status"] == "PRIMARY"
3272    assert service["taskDefinition"] == service["taskSets"][1]["taskDefinition"]
3273
3274
3275@mock_ecs
3276def test_update_task_set():
3277    cluster_name = "test_ecs_cluster"
3278    service_name = "test_ecs_service"
3279    task_def_name = "test_ecs_task"
3280
3281    client = boto3.client("ecs", region_name="us-east-1")
3282    _ = client.create_cluster(clusterName=cluster_name)
3283    _ = client.register_task_definition(
3284        family=task_def_name,
3285        containerDefinitions=[
3286            {
3287                "name": "hello_world",
3288                "image": "docker/hello-world:latest",
3289                "cpu": 1024,
3290                "memory": 400,
3291                "essential": True,
3292                "environment": [
3293                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
3294                ],
3295                "logConfiguration": {"logDriver": "json-file"},
3296            }
3297        ],
3298    )
3299    _ = client.create_service(
3300        cluster=cluster_name,
3301        serviceName=service_name,
3302        desiredCount=2,
3303        deploymentController={"type": "EXTERNAL"},
3304    )
3305
3306    task_set = client.create_task_set(
3307        cluster=cluster_name, service=service_name, taskDefinition=task_def_name,
3308    )["taskSet"]
3309
3310    another_task_set = client.create_task_set(
3311        cluster=cluster_name, service=service_name, taskDefinition=task_def_name,
3312    )["taskSet"]
3313    assert another_task_set["scale"]["unit"] == "PERCENT"
3314    assert another_task_set["scale"]["value"] == 100.0
3315
3316    client.update_task_set(
3317        cluster=cluster_name,
3318        service=service_name,
3319        taskSet=task_set["taskSetArn"],
3320        scale={"value": 25.0, "unit": "PERCENT"},
3321    )
3322
3323    updated_task_set = client.describe_task_sets(
3324        cluster=cluster_name, service=service_name, taskSets=[task_set["taskSetArn"]],
3325    )["taskSets"][0]
3326    assert updated_task_set["scale"]["value"] == 25.0
3327    assert updated_task_set["scale"]["unit"] == "PERCENT"
3328
3329
3330@mock_ec2
3331@mock_ecs
3332def test_list_tasks_with_filters():
3333    ecs = boto3.client("ecs", region_name="us-east-1")
3334    ec2 = boto3.resource("ec2", region_name="us-east-1")
3335
3336    _ = ecs.create_cluster(clusterName="test_cluster_1")
3337    _ = ecs.create_cluster(clusterName="test_cluster_2")
3338
3339    test_instance = ec2.create_instances(
3340        ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1
3341    )[0]
3342
3343    instance_id_document = json.dumps(
3344        ec2_utils.generate_instance_identity_document(test_instance)
3345    )
3346
3347    _ = ecs.register_container_instance(
3348        cluster="test_cluster_1", instanceIdentityDocument=instance_id_document
3349    )
3350    _ = ecs.register_container_instance(
3351        cluster="test_cluster_2", instanceIdentityDocument=instance_id_document
3352    )
3353
3354    container_instances = ecs.list_container_instances(cluster="test_cluster_1")
3355    container_id_1 = container_instances["containerInstanceArns"][0].split("/")[-1]
3356    container_instances = ecs.list_container_instances(cluster="test_cluster_2")
3357    container_id_2 = container_instances["containerInstanceArns"][0].split("/")[-1]
3358
3359    test_container_def = {
3360        "name": "hello_world",
3361        "image": "docker/hello-world:latest",
3362        "cpu": 1024,
3363        "memory": 400,
3364        "essential": True,
3365        "environment": [{"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}],
3366        "logConfiguration": {"logDriver": "json-file"},
3367    }
3368
3369    _ = ecs.register_task_definition(
3370        family="test_task_def_1", containerDefinitions=[test_container_def],
3371    )
3372
3373    _ = ecs.register_task_definition(
3374        family="test_task_def_2", containerDefinitions=[test_container_def],
3375    )
3376
3377    _ = ecs.start_task(
3378        cluster="test_cluster_1",
3379        taskDefinition="test_task_def_1",
3380        overrides={},
3381        containerInstances=[container_id_1],
3382        startedBy="foo",
3383    )
3384
3385    resp = ecs.start_task(
3386        cluster="test_cluster_2",
3387        taskDefinition="test_task_def_2",
3388        overrides={},
3389        containerInstances=[container_id_2],
3390        startedBy="foo",
3391    )
3392    task_to_stop = resp["tasks"][0]["taskArn"]
3393
3394    _ = ecs.start_task(
3395        cluster="test_cluster_1",
3396        taskDefinition="test_task_def_1",
3397        overrides={},
3398        containerInstances=[container_id_1],
3399        startedBy="bar",
3400    )
3401
3402    len(ecs.list_tasks(cluster="test_cluster_1")["taskArns"]).should.equal(2)
3403    len(ecs.list_tasks(cluster="test_cluster_2")["taskArns"]).should.equal(1)
3404
3405    len(
3406        ecs.list_tasks(cluster="test_cluster_1", containerInstance="bad-id")["taskArns"]
3407    ).should.equal(0)
3408    len(
3409        ecs.list_tasks(cluster="test_cluster_1", containerInstance=container_id_1)[
3410            "taskArns"
3411        ]
3412    ).should.equal(2)
3413    len(
3414        ecs.list_tasks(cluster="test_cluster_2", containerInstance=container_id_2)[
3415            "taskArns"
3416        ]
3417    ).should.equal(1)
3418
3419    len(
3420        ecs.list_tasks(cluster="test_cluster_1", family="non-existent-family")[
3421            "taskArns"
3422        ]
3423    ).should.equal(0)
3424    len(
3425        ecs.list_tasks(cluster="test_cluster_1", family="test_task_def_1")["taskArns"]
3426    ).should.equal(2)
3427    len(
3428        ecs.list_tasks(cluster="test_cluster_2", family="test_task_def_2")["taskArns"]
3429    ).should.equal(1)
3430
3431    len(
3432        ecs.list_tasks(cluster="test_cluster_1", startedBy="non-existent-entity")[
3433            "taskArns"
3434        ]
3435    ).should.equal(0)
3436    len(
3437        ecs.list_tasks(cluster="test_cluster_1", startedBy="foo")["taskArns"]
3438    ).should.equal(1)
3439    len(
3440        ecs.list_tasks(cluster="test_cluster_1", startedBy="bar")["taskArns"]
3441    ).should.equal(1)
3442    len(
3443        ecs.list_tasks(cluster="test_cluster_2", startedBy="foo")["taskArns"]
3444    ).should.equal(1)
3445
3446    len(
3447        ecs.list_tasks(cluster="test_cluster_1", desiredStatus="RUNNING")["taskArns"]
3448    ).should.equal(2)
3449    len(
3450        ecs.list_tasks(cluster="test_cluster_2", desiredStatus="RUNNING")["taskArns"]
3451    ).should.equal(1)
3452    _ = ecs.stop_task(cluster="test_cluster_2", task=task_to_stop, reason="for testing")
3453    len(
3454        ecs.list_tasks(cluster="test_cluster_1", desiredStatus="RUNNING")["taskArns"]
3455    ).should.equal(2)
3456    len(
3457        ecs.list_tasks(cluster="test_cluster_2", desiredStatus="STOPPED")["taskArns"]
3458    ).should.equal(1)
3459
3460    resp = ecs.list_tasks(cluster="test_cluster_1", startedBy="foo")
3461    len(resp["taskArns"]).should.equal(1)
3462
3463    resp = ecs.list_tasks(
3464        cluster="test_cluster_1", containerInstance=container_id_1, startedBy="bar"
3465    )
3466    len(resp["taskArns"]).should.equal(1)
3467