From a589da356d6bd75e059505b2a91e9e044148ac50 Mon Sep 17 00:00:00 2001
From: Kevin DeJong <kddejong@amazon.com>
Date: Wed, 29 Jan 2025 15:47:38 -0800
Subject: [PATCH] Put in fixes for ECS LogDriver configs (#3937)

---
 scripts/boto/update_schemas_from_boto.py      |   3 +-
 .../fargate_properties.json                   | 149 +++++++++++++-----
 .../extensions/all/aws_ecs_service/boto.json  |  14 --
 .../all/aws_ecs_taskdefinition/boto.json      |  14 --
 .../eu_central_1/aws-ecs-service.json         |  10 --
 .../us_east_1/aws-ecs-taskdefinition.json     |  10 --
 .../providers/us_west_2/aws-ecs-service.json  |  10 --
 .../ecs/test_ecs_task_fargate_properties.py   | 136 +++++++++++++++-
 8 files changed, 241 insertions(+), 105 deletions(-)

diff --git a/scripts/boto/update_schemas_from_boto.py b/scripts/boto/update_schemas_from_boto.py
index 829cd3f629..4cfea803bd 100755
--- a/scripts/boto/update_schemas_from_boto.py
+++ b/scripts/boto/update_schemas_from_boto.py
@@ -29,7 +29,8 @@
 ]
 
 exceptions = {
-    "ses": ["/definitions/EventDestination/properties/MatchingEventTypes/items"]
+    "ses": ["/definitions/EventDestination/properties/MatchingEventTypes/items"],
+    "ecs": ["/definitions/LogConfiguration/properties/LogDriver"],
 }
 
 
diff --git a/src/cfnlint/data/schemas/extensions/aws_ecs_taskdefinition/fargate_properties.json b/src/cfnlint/data/schemas/extensions/aws_ecs_taskdefinition/fargate_properties.json
index f0326beeb0..94885935c2 100644
--- a/src/cfnlint/data/schemas/extensions/aws_ecs_taskdefinition/fargate_properties.json
+++ b/src/cfnlint/data/schemas/extensions/aws_ecs_taskdefinition/fargate_properties.json
@@ -16,54 +16,119 @@
   ]
  },
  "then": {
-  "if": {
-   "properties": {
-    "Cpu": {
-     "type": [
-      "string",
-      "integer"
+  "allOf": [
+   {
+    "if": {
+     "properties": {
+      "NetworkMode": {
+       "type": "string"
+      }
+     },
+     "required": [
+      "NetworkMode"
+     ]
+    },
+    "required": [
+     "NetworkMode"
+    ],
+    "then": {
+     "properties": {
+      "NetworkMode": {
+       "enum": [
+        "awsvpc"
+       ],
+       "type": "string"
+      }
+     },
+     "required": [
+      "NetworkMode"
      ]
     }
    },
-   "required": [
-    "Cpu"
-   ]
-  },
-  "not": {
-   "required": [
-    "PlacementConstraints"
-   ]
-  },
-  "required": [
-   "Cpu",
-   "Memory"
-  ],
-  "then": {
-   "properties": {
-    "Cpu": {
-     "else": {
-      "pattern": "^(\\.25|\\.5|1|2|4|8|16)\\s*(?i)vCpu$"
-     },
-     "if": {
-      "pattern": "^\\d+$",
-      "type": [
-       "integer",
-       "string"
-      ]
+   {
+    "properties": {
+     "ContainerDefinitions": {
+      "items": {
+       "properties": {
+        "LogConfiguration": {
+         "if": {
+          "properties": {
+           "LogDriver": {
+            "type": "string"
+           }
+          },
+          "required": [
+           "LogDriver"
+          ]
+         },
+         "then": {
+          "properties": {
+           "LogDriver": {
+            "enum": [
+             "awslogs",
+             "splunk",
+             "awsfirelens"
+            ]
+           }
+          }
+         }
+        }
+       }
+      }
+     }
+    }
+   },
+   {
+    "if": {
+     "properties": {
+      "Cpu": {
+       "type": [
+        "string",
+        "integer"
+       ]
+      }
      },
-     "then": {
-      "enum": [
-       "256",
-       "512",
-       "1024",
-       "2048",
-       "4096",
-       "8192",
-       "16384"
-      ]
+     "required": [
+      "Cpu"
+     ]
+    },
+    "not": {
+     "required": [
+      "PlacementConstraints"
+     ]
+    },
+    "required": [
+     "Cpu",
+     "Memory"
+    ],
+    "then": {
+     "properties": {
+      "Cpu": {
+       "else": {
+        "pattern": "^(\\.25|\\.5|1|2|4|8|16)\\s*(?i)vCpu$"
+       },
+       "if": {
+        "pattern": "^\\d+$",
+        "type": [
+         "integer",
+         "string"
+        ]
+       },
+       "then": {
+        "enum": [
+         "256",
+         "512",
+         "1024",
+         "2048",
+         "4096",
+         "8192",
+         "16384"
+        ]
+       }
+      }
      }
     }
    }
-  }
+  ]
  }
 }
diff --git a/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_service/boto.json b/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_service/boto.json
index e6ad41c272..fd1d715eeb 100644
--- a/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_service/boto.json
+++ b/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_service/boto.json
@@ -9,20 +9,6 @@
   "path": "/definitions/Tag/properties/Value/pattern",
   "value": "^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$"
  },
- {
-  "op": "add",
-  "path": "/definitions/LogConfiguration/properties/LogDriver/enum",
-  "value": [
-   "awsfirelens",
-   "awslogs",
-   "fluentd",
-   "gelf",
-   "journald",
-   "json-file",
-   "splunk",
-   "syslog"
-  ]
- },
  {
   "op": "add",
   "path": "/definitions/EBSTagSpecification/properties/ResourceType/enum",
diff --git a/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_taskdefinition/boto.json b/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_taskdefinition/boto.json
index fb8d335eb8..f0973d86f5 100644
--- a/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_taskdefinition/boto.json
+++ b/src/cfnlint/data/schemas/patches/extensions/all/aws_ecs_taskdefinition/boto.json
@@ -71,20 +71,6 @@
    "stack"
   ]
  },
- {
-  "op": "add",
-  "path": "/definitions/LogConfiguration/properties/LogDriver/enum",
-  "value": [
-   "awsfirelens",
-   "awslogs",
-   "fluentd",
-   "gelf",
-   "journald",
-   "json-file",
-   "splunk",
-   "syslog"
-  ]
- },
  {
   "op": "add",
   "path": "/definitions/ResourceRequirement/properties/Type/enum",
diff --git a/src/cfnlint/data/schemas/providers/eu_central_1/aws-ecs-service.json b/src/cfnlint/data/schemas/providers/eu_central_1/aws-ecs-service.json
index 39c164ea67..7065156406 100644
--- a/src/cfnlint/data/schemas/providers/eu_central_1/aws-ecs-service.json
+++ b/src/cfnlint/data/schemas/providers/eu_central_1/aws-ecs-service.json
@@ -174,16 +174,6 @@
    "additionalProperties": false,
    "properties": {
     "LogDriver": {
-     "enum": [
-      "awsfirelens",
-      "awslogs",
-      "fluentd",
-      "gelf",
-      "journald",
-      "json-file",
-      "splunk",
-      "syslog"
-     ],
      "type": "string"
     },
     "Options": {
diff --git a/src/cfnlint/data/schemas/providers/us_east_1/aws-ecs-taskdefinition.json b/src/cfnlint/data/schemas/providers/us_east_1/aws-ecs-taskdefinition.json
index 8b9f473363..2db10e86bc 100644
--- a/src/cfnlint/data/schemas/providers/us_east_1/aws-ecs-taskdefinition.json
+++ b/src/cfnlint/data/schemas/providers/us_east_1/aws-ecs-taskdefinition.json
@@ -581,16 +581,6 @@
    "additionalProperties": false,
    "properties": {
     "LogDriver": {
-     "enum": [
-      "awsfirelens",
-      "awslogs",
-      "fluentd",
-      "gelf",
-      "journald",
-      "json-file",
-      "splunk",
-      "syslog"
-     ],
      "type": "string"
     },
     "Options": {
diff --git a/src/cfnlint/data/schemas/providers/us_west_2/aws-ecs-service.json b/src/cfnlint/data/schemas/providers/us_west_2/aws-ecs-service.json
index 39c164ea67..7065156406 100644
--- a/src/cfnlint/data/schemas/providers/us_west_2/aws-ecs-service.json
+++ b/src/cfnlint/data/schemas/providers/us_west_2/aws-ecs-service.json
@@ -174,16 +174,6 @@
    "additionalProperties": false,
    "properties": {
     "LogDriver": {
-     "enum": [
-      "awsfirelens",
-      "awslogs",
-      "fluentd",
-      "gelf",
-      "journald",
-      "json-file",
-      "splunk",
-      "syslog"
-     ],
      "type": "string"
     },
     "Options": {
diff --git a/test/unit/rules/resources/ecs/test_ecs_task_fargate_properties.py b/test/unit/rules/resources/ecs/test_ecs_task_fargate_properties.py
index b2935bd6b2..8d557e623c 100644
--- a/test/unit/rules/resources/ecs/test_ecs_task_fargate_properties.py
+++ b/test/unit/rules/resources/ecs/test_ecs_task_fargate_properties.py
@@ -22,6 +22,7 @@ def rule():
     [
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Cpu": 256,
                 "Memory": "512",
@@ -30,6 +31,7 @@ def rule():
         ),
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Cpu": ".25     vCpU",
                 "Memory": "512",
@@ -38,6 +40,7 @@ def rule():
         ),
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Cpu": 16384,
             },
@@ -47,12 +50,13 @@ def rule():
                     rule=TaskFargateProperties(),
                     path=deque([]),
                     validator="required",
-                    schema_path=deque(["then", "required"]),
+                    schema_path=deque(["then", "allOf", 2, "required"]),
                 )
             ],
         ),
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Memory": "512",
             },
@@ -62,12 +66,13 @@ def rule():
                     rule=TaskFargateProperties(),
                     path=deque([]),
                     validator="required",
-                    schema_path=deque(["then", "required"]),
+                    schema_path=deque(["then", "allOf", 2, "required"]),
                 )
             ],
         ),
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Memory": "512",
                 "Cpu": 256,
@@ -79,12 +84,13 @@ def rule():
                     rule=TaskFargateProperties(),
                     path=deque(["PlacementConstraints"]),
                     validator="not",
-                    schema_path=deque(["then", "not"]),
+                    schema_path=deque(["then", "allOf", 2, "not"]),
                 )
             ],
         ),
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Cpu": {"Ref": "MyParameter"},
                 "Memory": "512",
@@ -93,6 +99,7 @@ def rule():
         ),
         (
             {
+                "NetworkMode": "awsvpc",
                 "RequiresCompatibilities": ["FARGATE"],
                 "Cpu": 128,
                 "Memory": "512",
@@ -107,7 +114,128 @@ def rule():
                     path=deque(["Cpu"]),
                     validator="enum",
                     schema_path=deque(
-                        ["then", "then", "properties", "Cpu", "then", "enum"]
+                        [
+                            "then",
+                            "allOf",
+                            2,
+                            "then",
+                            "properties",
+                            "Cpu",
+                            "then",
+                            "enum",
+                        ]
+                    ),
+                )
+            ],
+        ),
+        (
+            {
+                "RequiresCompatibilities": ["FARGATE"],
+                "Cpu": 256,
+                "Memory": "512",
+                "NetworkMode": {"Ref": "MyParameter"},
+            },
+            [],
+        ),
+        (
+            {
+                "RequiresCompatibilities": ["FARGATE"],
+                "Cpu": 256,
+                "Memory": "512",
+            },
+            [
+                ValidationError(
+                    ("'NetworkMode' is a required property"),
+                    rule=TaskFargateProperties(),
+                    path=deque([]),
+                    validator="required",
+                    schema_path=deque(["then", "allOf", 0, "required"]),
+                )
+            ],
+        ),
+        (
+            {
+                "RequiresCompatibilities": ["FARGATE"],
+                "Cpu": 256,
+                "Memory": "512",
+                "NetworkMode": "awsvpc",
+                "ContainerDefinitions": [
+                    {
+                        "LogConfiguration": {
+                            "LogDriver": "awslogs",
+                            "Options": {
+                                "awslogs-group": "log-group",
+                                "awslogs-region": "us-east-1",
+                                "awslogs-stream-prefix": "ecs",
+                            },
+                        }
+                    }
+                ],
+            },
+            [],
+        ),
+        (
+            {
+                "RequiresCompatibilities": ["FARGATE"],
+                "Cpu": 256,
+                "Memory": "512",
+                "NetworkMode": "awsvpc",
+                "ContainerDefinitions": [
+                    {
+                        "LogConfiguration": {
+                            "LogDriver": {"Ref": "MyParameter"},
+                            "Options": {
+                                "awslogs-group": "log-group",
+                                "awslogs-region": "us-east-1",
+                                "awslogs-stream-prefix": "ecs",
+                            },
+                        }
+                    }
+                ],
+            },
+            [],
+        ),
+        (
+            {
+                "RequiresCompatibilities": ["FARGATE"],
+                "Cpu": 256,
+                "Memory": "512",
+                "NetworkMode": "awsvpc",
+                "ContainerDefinitions": [
+                    {
+                        "LogConfiguration": {
+                            "LogDriver": "sumologic",
+                            "Options": {
+                                "sumo-url": "aurl",
+                                "sumo-source-category": "access",
+                            },
+                        }
+                    }
+                ],
+            },
+            [
+                ValidationError(
+                    ("'sumologic' is not one of ['awslogs', 'splunk', 'awsfirelens']"),
+                    rule=TaskFargateProperties(),
+                    path=deque(
+                        ["ContainerDefinitions", 0, "LogConfiguration", "LogDriver"]
+                    ),
+                    validator="enum",
+                    schema_path=deque(
+                        [
+                            "then",
+                            "allOf",
+                            1,
+                            "properties",
+                            "ContainerDefinitions",
+                            "items",
+                            "properties",
+                            "LogConfiguration",
+                            "then",
+                            "properties",
+                            "LogDriver",
+                            "enum",
+                        ]
                     ),
                 )
             ],