From 3e9e0a8696630c9368adf012aff1fb919e398164 Mon Sep 17 00:00:00 2001
From: Charles Holmes <88447527+CharlesHolmes@users.noreply.github.com>
Date: Fri, 10 May 2024 14:12:06 -0500
Subject: [PATCH] fix(ecs): require task pidMode for Linux-based Fargate tasks,
 not host (#30020)

### Issue # (if applicable)

Closes #29995.

### Reason for this change

Only the `task` option is allowed for [`pidMode`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-pidmode) on Linux-based Fargate tasks.

### Description of changes

This PR builds on the changes introduced in #29670 but fixes the handling of `pidMode` so that it matches the behavior allowed by CloudFormation and described in the [AWS User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#cfn-ecs-taskdefinition-pidmode).

### Description of how you validated changes

Updated the existing tests so that `task` is the only allowable `pidMode` setting if a Fargate task's OS is Linux-based.

### Checklist
- [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md)

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
---
 .../aws-ecs-integ-runtime.template.json       |  2 +-
 .../aws-ecs/test/fargate/integ.runtime.ts     |  2 +-
 packages/aws-cdk-lib/aws-ecs/README.md        |  5 +--
 .../aws-ecs/lib/base/task-definition.ts       | 13 +++++---
 .../lib/fargate/fargate-task-definition.ts    | 16 ++++++---
 .../aws-ecs/lib/runtime-platform.ts           | 11 +++++--
 .../test/fargate/fargate-service.test.ts      |  2 +-
 .../fargate/fargate-task-definition.test.ts   | 33 +++++++++++++++----
 8 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json
index 0108491f8601d..d28247b3480d7 100644
--- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json
+++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.js.snapshot/aws-ecs-integ-runtime.template.json
@@ -570,7 +570,7 @@
     "Family": "awsecsintegruntimeTaskDefGraviton28E28B263",
     "Memory": "1024",
     "NetworkMode": "awsvpc",
-    "PidMode": "host",
+    "PidMode": "task",
     "RequiresCompatibilities": [
      "FARGATE"
     ],
diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts
index 8f224e13e86cd..385ca2e8cdcd2 100644
--- a/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts
+++ b/packages/@aws-cdk-testing/framework-integ/test/aws-ecs/test/fargate/integ.runtime.ts
@@ -27,7 +27,7 @@ const taskDefinitiongraviton2 = new ecs.FargateTaskDefinition(stack, 'TaskDefGra
   },
   cpu: 256,
   memoryLimitMiB: 1024,
-  pidMode: ecs.PidMode.HOST,
+  pidMode: ecs.PidMode.TASK,
 });
 
 taskDefinitionwindows.addContainer('windowsservercore', {
diff --git a/packages/aws-cdk-lib/aws-ecs/README.md b/packages/aws-cdk-lib/aws-ecs/README.md
index 3dcad8549d0a0..f0669e3adcc66 100644
--- a/packages/aws-cdk-lib/aws-ecs/README.md
+++ b/packages/aws-cdk-lib/aws-ecs/README.md
@@ -372,12 +372,13 @@ const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
   },
   memoryLimitMiB: 512,
   cpu: 256,
-  pidMode: ecs.PidMode.HOST,
+  pidMode: ecs.PidMode.TASK,
 });
 ```
 
 **Note:** `pidMode` is only supported for tasks that are hosted on AWS Fargate if the tasks are using platform version 1.4.0
-or later (Linux). This isn't supported for Windows containers on Fargate.
+or later (Linux). Only the `task` option is supported for Linux containers. `pidMode` isn't supported for Windows containers on Fargate.
+If `pidMode` is specified for a Fargate task, then `runtimePlatform.operatingSystemFamily` must also be specified.
 
 To add containers to a task definition, call `addContainer()`:
 
diff --git a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts
index c079f1aae02a3..7ab075ae777ce 100644
--- a/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts
+++ b/packages/aws-cdk-lib/aws-ecs/lib/base/task-definition.ts
@@ -194,8 +194,11 @@ export interface TaskDefinitionProps extends CommonTaskDefinitionProps {
    * The process namespace to use for the containers in the task.
    *
    * Only supported for tasks that are hosted on AWS Fargate if the tasks
-   * are using platform version 1.4.0 or later (Linux).
-   * Not supported in Windows containers.
+   * are using platform version 1.4.0 or later (Linux). Only the TASK option
+   * is supported for Linux-based Fargate containers. Not supported in Windows
+   * containers. If pidMode is specified for a Fargate task, then
+   * runtimePlatform.operatingSystemFamily must also be specified.  For more
+   * information, see [Task Definition Parameters](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_definition_pidmode).
    *
    * @default - PidMode used by the task is not specified
    */
@@ -378,8 +381,10 @@ export class TaskDefinition extends TaskDefinitionBase {
    * The process namespace to use for the containers in the task.
    *
    * Only supported for tasks that are hosted on AWS Fargate if the tasks
-   * are using platform version 1.4.0 or later (Linux).
-   * Not supported in Windows containers.
+   * are using platform version 1.4.0 or later (Linux). Not supported in
+   * Windows containers. If pidMode is specified for a Fargate task,
+   * then runtimePlatform.operatingSystemFamily must also be specified.  For more
+   * information, see [Task Definition Parameters](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_definition_pidmode).
    */
   public readonly pidMode?: PidMode;
 
diff --git a/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts b/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts
index 34d0bed643aa2..e6bc7876daa04 100644
--- a/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts
+++ b/packages/aws-cdk-lib/aws-ecs/lib/fargate/fargate-task-definition.ts
@@ -83,8 +83,11 @@ export interface FargateTaskDefinitionProps extends CommonTaskDefinitionProps {
    * The process namespace to use for the containers in the task.
    *
    * Only supported for tasks that are hosted on AWS Fargate if the tasks
-   * are using platform version 1.4.0 or later (Linux).
-   * Not supported in Windows containers.
+   * are using platform version 1.4.0 or later (Linux).  Only the TASK option
+   * is supported for Linux-based Fargate containers. Not supported in
+   * Windows containers. If pidMode is specified for a Fargate task, then
+   * runtimePlatform.operatingSystemFamily must also be specified.  For more
+   * information, see [Task Definition Parameters](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_definition_pidmode).
    *
    * @default - PidMode used by the task is not specified
    */
@@ -168,11 +171,16 @@ export class FargateTaskDefinition extends TaskDefinition implements IFargateTas
     }
 
     if (props.pidMode) {
+      if (!props.runtimePlatform?.operatingSystemFamily) {
+        throw new Error('Specifying \'pidMode\' requires that operating system family also be provided.');
+      }
       if (props.runtimePlatform?.operatingSystemFamily?.isWindows()) {
         throw new Error('\'pidMode\' is not supported for Windows containers.');
       }
-      if (!Token.isUnresolved(props.pidMode) && props.pidMode !== PidMode.HOST) {
-        throw new Error(`\'pidMode\' can only be set to \'${PidMode.HOST}\' for Fargate containers, got: \'${props.pidMode}\'.`);
+      if (!Token.isUnresolved(props.pidMode)
+          && props.runtimePlatform?.operatingSystemFamily?.isLinux()
+          && props.pidMode !== PidMode.TASK) {
+        throw new Error(`\'pidMode\' can only be set to \'${PidMode.TASK}\' for Linux Fargate containers, got: \'${props.pidMode}\'.`);
       }
     }
 
diff --git a/packages/aws-cdk-lib/aws-ecs/lib/runtime-platform.ts b/packages/aws-cdk-lib/aws-ecs/lib/runtime-platform.ts
index 250e64a1f9ca4..e5fb42f475d08 100644
--- a/packages/aws-cdk-lib/aws-ecs/lib/runtime-platform.ts
+++ b/packages/aws-cdk-lib/aws-ecs/lib/runtime-platform.ts
@@ -90,10 +90,17 @@ export class OperatingSystemFamily {
   private constructor(public readonly _operatingSystemFamily: string) { }
 
   /**
-   * Returns true if the operating system family is Windows
+   * Indicates whether the operating system family is Windows
    */
   public isWindows(): boolean {
-    return this._operatingSystemFamily?.toLowerCase().startsWith('windows') ? true : false;
+    return this._operatingSystemFamily?.toLowerCase().startsWith('windows');
+  }
+
+  /**
+   * Indicates whether the operating system family is Linux
+   */
+  public isLinux(): boolean {
+    return this._operatingSystemFamily?.toLowerCase().startsWith('linux');
   }
 }
 
diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts
index 0ecd027501019..1775d5ed2eb6e 100644
--- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts
+++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-service.test.ts
@@ -758,7 +758,7 @@ describe('fargate service', () => {
         },
         memoryLimitMiB: 512,
         cpu: 256,
-        pidMode: ecs.PidMode.HOST,
+        pidMode: ecs.PidMode.TASK,
       });
 
       // WHEN
diff --git a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts
index def9ba61aaea8..be93c76e2b65e 100644
--- a/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts
+++ b/packages/aws-cdk-lib/aws-ecs/test/fargate/fargate-task-definition.test.ts
@@ -60,7 +60,7 @@ describe('fargate task definition', () => {
           cpuArchitecture: ecs.CpuArchitecture.X86_64,
           operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
         },
-        pidMode: ecs.PidMode.HOST,
+        pidMode: ecs.PidMode.TASK,
       });
 
       taskDefinition.addVolume({
@@ -85,7 +85,7 @@ describe('fargate task definition', () => {
         Family: 'myApp',
         Memory: '1024',
         NetworkMode: 'awsvpc',
-        PidMode: 'host',
+        PidMode: 'task',
         RequiresCompatibilities: [
           ecs.LaunchType.FARGATE,
         ],
@@ -164,6 +164,24 @@ describe('fargate task definition', () => {
       // THEN
     });
 
+    test('throws when pidMode is specified without an operating system family', () => {
+      // GIVEN
+      const stack = new cdk.Stack();
+
+      // WHEN
+      // THEN
+      expect(() => {
+        new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', {
+          pidMode: ecs.PidMode.TASK,
+          runtimePlatform: {
+            cpuArchitecture: ecs.CpuArchitecture.X86_64,
+          },
+          cpu: 1024,
+          memoryLimitMiB: 2048,
+        });
+      }).toThrow(/Specifying 'pidMode' requires that operating system family also be provided./);
+    });
+
     test('throws when pidMode is specified on Windows', () => {
       // GIVEN
       const stack = new cdk.Stack();
@@ -172,7 +190,7 @@ describe('fargate task definition', () => {
       // THEN
       expect(() => {
         new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', {
-          pidMode: ecs.PidMode.HOST,
+          pidMode: ecs.PidMode.TASK,
           runtimePlatform: {
             operatingSystemFamily: ecs.OperatingSystemFamily.WINDOWS_SERVER_2019_CORE,
             cpuArchitecture: ecs.CpuArchitecture.X86_64,
@@ -183,7 +201,7 @@ describe('fargate task definition', () => {
       }).toThrow(/'pidMode' is not supported for Windows containers./);
     });
 
-    test('throws when pidMode is not host', () => {
+    test('throws when pidMode is not task', () => {
       // GIVEN
       const stack = new cdk.Stack();
 
@@ -191,9 +209,12 @@ describe('fargate task definition', () => {
       // THEN
       expect(() => {
         new ecs.FargateTaskDefinition(stack, 'FargateTaskDef', {
-          pidMode: ecs.PidMode.TASK,
+          pidMode: ecs.PidMode.HOST,
+          runtimePlatform: {
+            operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
+          },
         });
-      }).toThrow(/'pidMode' can only be set to 'host' for Fargate containers, got: 'task'./);
+      }).toThrow(/'pidMode' can only be set to 'task' for Linux Fargate containers, got: 'host'./);
     });
   });
   describe('When configuredAtLaunch in the Volume', ()=> {