Repository: getmoto/moto

Issue:
ecs.create_cluster and ecs.describe_clusters do not handle tags correctly
Facing similar to this issue but for create_cluster() and describe_cluster() #3138 

First call creates the cluster:
```
ecs_client.create_cluster(
        clusterName="test_cluster",
        tags=[{
            'key': 'tagName',
            'value': 'TavValue'
        }]
)
```
Expected tags in return of the second call:
```
describe_cluster = ecs.describe_clusters(
        clusters=clusters['clusterArns'],
        include=[
            'TAGS',
        ]
    )
```
Actual return of describe_cluster
```
{'clusters': [{'clusterArn': 'arn:aws:ecs:eu-central-1:123456789012:cluster/test_cluster', 'clusterName': 'test_cluster', 'status': 'ACTIVE', 'registeredContainerInstancesCount': 0, 'runningTasksCount': 0, 'pendingTasksCount': 0, 'activeServicesCount': 0}], 'failures': [], 'ResponseMetadata': {'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
```


Discussion:

Resolving patch:
diff --git a/moto/ecs/models.py b/moto/ecs/models.py
--- a/moto/ecs/models.py
+++ b/moto/ecs/models.py
@@ -12,6 +12,7 @@
 from moto.core.exceptions import JsonRESTError
 from moto.core.utils import unix_time, pascal_to_camelcase, remap_nested_keys
 from moto.ec2 import ec2_backends
+from moto.utilities.tagging_service import TaggingService
 from .exceptions import (
     EcsClientException,
     ServiceNotFoundException,
@@ -719,6 +720,9 @@ def __init__(self, region_name):
         self.container_instances = {}
         self.task_sets = {}
         self.region_name = region_name
+        self.tagger = TaggingService(
+            tag_name="tags", key_name="key", value_name="value"
+        )
 
     def reset(self):
         region_name = self.region_name
@@ -759,9 +763,11 @@ def describe_task_definition(self, task_definition_str):
         else:
             raise Exception("{0} is not a task_definition".format(task_definition_name))
 
-    def create_cluster(self, cluster_name):
+    def create_cluster(self, cluster_name, tags=None):
         cluster = Cluster(cluster_name, self.region_name)
         self.clusters[cluster_name] = cluster
+        if tags:
+            self.tagger.tag_resource(cluster.arn, tags)
         return cluster
 
     def list_clusters(self):
@@ -770,7 +776,10 @@ def list_clusters(self):
         """
         return [cluster.arn for cluster in self.clusters.values()]
 
-    def describe_clusters(self, list_clusters_name=None):
+    def describe_clusters(self, list_clusters_name=None, include=None):
+        """
+        Only include=TAGS is currently supported.
+        """
         list_clusters = []
         failures = []
         if list_clusters_name is None:
@@ -785,6 +794,14 @@ def describe_clusters(self, list_clusters_name=None):
                     failures.append(
                         ClusterFailure("MISSING", cluster_name, self.region_name)
                     )
+
+        if "TAGS" in (include or []):
+            for cluster in list_clusters:
+                cluster_arn = cluster["clusterArn"]
+                if self.tagger.has_tags(cluster_arn):
+                    cluster_tags = self.tagger.list_tags_for_resource(cluster_arn)
+                    cluster.update(cluster_tags)
+
         return list_clusters, failures
 
     def delete_cluster(self, cluster_str):
diff --git a/moto/ecs/responses.py b/moto/ecs/responses.py
--- a/moto/ecs/responses.py
+++ b/moto/ecs/responses.py
@@ -27,9 +27,10 @@ def _get_param(self, param, if_none=None):
 
     def create_cluster(self):
         cluster_name = self._get_param("clusterName")
+        tags = self._get_param("tags")
         if cluster_name is None:
             cluster_name = "default"
-        cluster = self.ecs_backend.create_cluster(cluster_name)
+        cluster = self.ecs_backend.create_cluster(cluster_name, tags)
         return json.dumps({"cluster": cluster.response_object})
 
     def list_clusters(self):
@@ -42,8 +43,9 @@ def list_clusters(self):
         )
 
     def describe_clusters(self):
-        list_clusters_name = self._get_param("clusters")
-        clusters, failures = self.ecs_backend.describe_clusters(list_clusters_name)
+        names = self._get_param("clusters")
+        include = self._get_param("include")
+        clusters, failures = self.ecs_backend.describe_clusters(names, include)
         return json.dumps(
             {
                 "clusters": clusters,


Test patch:
diff --git a/tests/test_ecs/test_ecs_boto3.py b/tests/test_ecs/test_ecs_boto3.py
--- a/tests/test_ecs/test_ecs_boto3.py
+++ b/tests/test_ecs/test_ecs_boto3.py
@@ -55,6 +55,37 @@ def test_list_clusters():
 
 @mock_ecs
 def test_describe_clusters():
+    client = boto3.client("ecs", region_name="us-east-1")
+    tag_list = [{"key": "tagName", "value": "TagValue"}]
+    _ = client.create_cluster(clusterName="c_with_tags", tags=tag_list)
+    _ = client.create_cluster(clusterName="c_without")
+    clusters = client.describe_clusters(clusters=["c_with_tags"], include=["TAGS",])[
+        "clusters"
+    ]
+    clusters.should.have.length_of(1)
+    cluster = clusters[0]
+    cluster["clusterName"].should.equal("c_with_tags")
+    cluster.should.have.key("tags")
+    cluster["tags"].should.equal(tag_list)
+
+    clusters = client.describe_clusters(clusters=["c_without"], include=["TAGS",])[
+        "clusters"
+    ]
+    clusters.should.have.length_of(1)
+    cluster = clusters[0]
+    cluster["clusterName"].should.equal("c_without")
+    cluster.shouldnt.have.key("tags")
+
+    clusters = client.describe_clusters(clusters=["c_with_tags", "c_without"])[
+        "clusters"
+    ]
+    clusters.should.have.length_of(2)
+    clusters[0].shouldnt.have.key("tags")
+    clusters[1].shouldnt.have.key("tags")
+
+
+@mock_ecs
+def test_describe_clusters_missing():
     client = boto3.client("ecs", region_name="us-east-1")
     response = client.describe_clusters(clusters=["some-cluster"])
     response["failures"].should.contain(