Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(cli): cannot hotswap ECS task definitions containing certain intr…
…insics (aws#26404) ## Reason for this change Currently an ECS task definition cannot be hotswapped in many cases, for example when it contains references to certain AWS resources as environment variables. This PR removes the limitation and make hotswaps possible in much more various situations. Specifically, if there is any token that is not part of the following, we cannot hotswap a task definition: 1. Ref functions, which will be resolved as physical resource ids 2. GetAtt functions for some resources and attributes (manually supported one by one, [code](https://github.com/aws/aws-cdk/blob/5ccc56975c323ea19fd0917def51184e13f440d9/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts#L352)) 3. several simple CFn functions (join, split, select, sub) 4. parameters like AWS::AccountId, Region, Partition, or UrlSuffix Although it is not supported much, using `GetAtt` function in a task definition is very common (imagine you reference other resource's property as an environment variable). This PR allows to hotswap a task definition even if it contains these tokens. ## Solution To hotswap a task definition, we need to construct a task definition to call `registerTaskDefinition` API. For this, we have to [evaluate](https://github.com/aws/aws-cdk/blob/5ccc56975c323ea19fd0917def51184e13f440d9/packages/aws-cdk/lib/api/evaluate-cloudformation-template.ts#L134) CloudFormation template locally to resolve all the intrinsics in the template. However, some intrinsics such as `Fn::GetAtt` is not fully supported by CDK CLI because we have to manually support them for each AWS resource type. The reason why some task definitions are unhotswappable is that there are such intrinsics in the template and the CDK fails to evaluate it locally. So the basic idea to overcome this limitation in this PR is that we don't try to evaluate it locally, but we fetch the latest task definition already deployed and get the required values from it. Here's how we can implement the idea. ### How we determine if changes to a task definition can be hotswapped In the hotswap process, we have to decide whether the change can be hotswapped or not. Now we can hotswap the task definition if 1. there are only changes in `ContainerDefinitions` field, and all the fields in the task definition can be evaluated locally. (original behavior) OR, 2. there are only changes in `ContainerDefinitions` field, and all the **updated** field can be evaluated locally (added in this PR). The first condition can actually be included in the second condition, but for now I keep it as-is to avoid the possibility of breaking the existing behavior. If the second condition is true, we fetch the latest task definition from AWS account, override the updated fields, and register a new task definition to update a service. By this way, we don't have to evaluate tokens in unchanged fields locally, allowing to use hotswap in much more situations. ### How we compare the old and new task definition Here is an example task definition: ```json { "ContainerDefinitions": [ { "Environment": [ { "Name": "VPC_ID", "Value": { "Fn::GetAtt": [ "Vpc8378EB38", "CidrBlock" ] } } ], "Essential": true, "Image": "nginx:stable", "Name": "EcsApp" } ], "Cpu": "256", "Family": "myteststackTask289798EC", "Memory": "512", "NetworkMode": "awsvpc", "RequiresCompatibilities": [ "FARGATE" ], "TaskRoleArn": { "Fn::GetAtt": [ "TaskTaskRoleE98524A1", "Arn" ] } } ``` We compare the old and new task definition in the following steps: 1. Check if there are only changes in `ContainerDefinitions` field. If not, we cannot hotswap. 2. Try `evaluateCfnExpression` on the containerDefinitons. If it can be evaluated, proceed to hotswap. If not, proceed to step 3. 3. Check if the length of `ContainerDefinitions` is the same. If not, we cannot hotswap. 4. For each container definition, deep-compare each key (e.g. `Environment`, `Image`, `Name`, etc) 5. For each key, if there is any diff in the corresponding value, try `evaluateCfnExpression` on the value. If the evaluation fails, we cannot hotswap. 6. After checking all the keys and there is no field that cannot be hotswapped, proceed to hotswap. Imagine if there is a change only in `Image` field (container image tag) but `Environment` field contains unsupported intrinsics (e.g. `"Fn::GetAtt": ["Vpc8378EB38", "CidrBlock"]`). In the previous CDK CLI we cannot hotswap it due to an evaluation error. We can now hotswap it because we don't have to evaluate the `Environment` field when it has no diffs. Closes aws#25563 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information