From 8208774fb9a5f9d58a5fea24e60aa6862e861aba Mon Sep 17 00:00:00 2001 From: Perry Son <13183804+perrylson@users.noreply.github.com> Date: Fri, 18 Oct 2024 12:38:59 -0700 Subject: [PATCH] feat(efs): allow AccessPoint to set client token (#31184) ### Reason for this change The [CfnAccessPoint](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_efs.CfnAccessPoint.html) construct supports client token specification. However, the current L2 implementation of [AccessPoint](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_efs.AccessPoint.html) does not support this property. The `disable-update-workflow` option was needed when running the updated integration test. ### Description of changes Added the `clientToken` prop to the existing props of AccessPoint. ### Description of how you validated changes Validated with unit and integration testing. ### 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* --- .../test/integ.efs.js.snapshot/cdk.out | 2 +- .../test/integ.efs.js.snapshot/integ.json | 2 +- .../test/integ.efs.js.snapshot/manifest.json | 11 +++++-- .../test-efs-integ.assets.json | 6 ++-- .../test-efs-integ.template.json | 1 + ...efaultTestDeployAssert7E1529D5.assets.json | 2 +- .../test/integ.efs.js.snapshot/tree.json | 5 ++-- .../test/aws-efs/test/integ.efs.ts | 1 + packages/aws-cdk-lib/aws-efs/README.md | 5 +++- .../aws-cdk-lib/aws-efs/lib/access-point.ts | 17 ++++++++++- .../aws-efs/test/access-point.test.ts | 29 +++++++++++++++++++ 11 files changed, 68 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/cdk.out index 560dae10d018f..bd5311dc372de 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"33.0.0"} \ No newline at end of file +{"version":"36.0.5"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/integ.json index 4e919fb88d5ba..8ed7e1cb009cd 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "33.0.0", + "version": "36.0.5", "testCases": { "test-efs-integ-test/DefaultTest": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/manifest.json index a11e326711d33..c6dbaf807913c 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "33.0.0", + "version": "36.0.5", "artifacts": { "test-efs-integ.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "test-efs-integ.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/a5b1ebc6006a2ce28112fae5df0ca689544d5dbbbbdd004fc794310ce2d08695.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/f38d63f13d470d5b3ddbf40c819538dea6aefd66655c0fdbfc1d5bc3edf24d17.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -186,7 +187,10 @@ "/test-efs-integ/FileSystem/AccessPoint/Resource": [ { "type": "aws:cdk:logicalId", - "data": "FileSystemAccessPointF8178182" + "data": "FileSystemAccessPointF8178182", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" + ] } ], "/test-efs-integ/BootstrapVersion": [ @@ -217,6 +221,7 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "testefsintegtestDefaultTestDeployAssert7E1529D5.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.assets.json index 77423251f9e2e..fc5074dea778b 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.assets.json @@ -1,7 +1,7 @@ { - "version": "33.0.0", + "version": "36.0.5", "files": { - "a5b1ebc6006a2ce28112fae5df0ca689544d5dbbbbdd004fc794310ce2d08695": { + "f38d63f13d470d5b3ddbf40c819538dea6aefd66655c0fdbfc1d5bc3edf24d17": { "source": { "path": "test-efs-integ.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "a5b1ebc6006a2ce28112fae5df0ca689544d5dbbbbdd004fc794310ce2d08695.json", + "objectKey": "f38d63f13d470d5b3ddbf40c819538dea6aefd66655c0fdbfc1d5bc3edf24d17.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.template.json index f13f94be56bcd..4e6af142e86ce 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/test-efs-integ.template.json @@ -458,6 +458,7 @@ "Value": "test-efs-integ/FileSystem/AccessPoint" } ], + "ClientToken": "client-token", "FileSystemId": { "Ref": "FileSystem8A8E25C0" }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json index dd7c253a12bbe..32c92a250f37d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/testefsintegtestDefaultTestDeployAssert7E1529D5.assets.json @@ -1,5 +1,5 @@ { - "version": "33.0.0", + "version": "36.0.5", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "source": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/tree.json index 08d186b424150..a690d5ea9de34 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.js.snapshot/tree.json @@ -751,6 +751,7 @@ "attributes": { "aws:cdk:cloudformation:type": "AWS::EFS::AccessPoint", "aws:cdk:cloudformation:props": { + "clientToken": "client-token", "fileSystemId": { "Ref": "FileSystem8A8E25C0" }, @@ -826,7 +827,7 @@ "path": "test-efs-integ-test/DefaultTest/Default", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.69" + "version": "10.3.0" } }, "DeployAssert": { @@ -872,7 +873,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.69" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.ts index b1f3e8f92604c..9f0ba846ac17a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-efs/test/integ.efs.ts @@ -24,6 +24,7 @@ fileSystem.addAccessPoint('AccessPoint', { gid: '1000', uid: '1000', }, + clientToken: 'client-token', }); new integ.IntegTest(app, 'test-efs-integ-test', { diff --git a/packages/aws-cdk-lib/aws-efs/README.md b/packages/aws-cdk-lib/aws-efs/README.md index 1a4419dcb0f24..07731d4b26785 100644 --- a/packages/aws-cdk-lib/aws-efs/README.md +++ b/packages/aws-cdk-lib/aws-efs/README.md @@ -217,7 +217,10 @@ the access point can only access data in its own directory and below. To learn m Use the `addAccessPoint` API to create an access point from a fileSystem. ```ts fixture=with-filesystem-instance -fileSystem.addAccessPoint('AccessPoint'); +fileSystem.addAccessPoint('MyAccessPoint', { + // create a unique access point via an optional client token + clientToken: 'client-token', +}); ``` By default, when you create an access point, the root(`/`) directory is exposed to the client diff --git a/packages/aws-cdk-lib/aws-efs/lib/access-point.ts b/packages/aws-cdk-lib/aws-efs/lib/access-point.ts index dcdea87857de9..d6bb8a59d5f5a 100644 --- a/packages/aws-cdk-lib/aws-efs/lib/access-point.ts +++ b/packages/aws-cdk-lib/aws-efs/lib/access-point.ts @@ -1,7 +1,7 @@ import { Construct } from 'constructs'; import { IFileSystem } from './efs-file-system'; import { CfnAccessPoint } from './efs.generated'; -import { ArnFormat, IResource, Resource, Stack, Tags } from '../../core'; +import { ArnFormat, IResource, Resource, Stack, Tags, Token } from '../../core'; /** * Represents an EFS AccessPoint @@ -102,6 +102,15 @@ export interface AccessPointOptions { * @default - user identity not enforced */ readonly posixUser?: PosixUser; + + /** + * The opaque string specified in the request to ensure idempotent creation. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-efs-accesspoint.html#cfn-efs-accesspoint-clienttoken + * + * @default - No client token + */ + readonly clientToken?: string; } /** @@ -201,6 +210,11 @@ export class AccessPoint extends AccessPointBase { constructor(scope: Construct, id: string, props: AccessPointProps) { super(scope, id); + const clientToken = props.clientToken; + if ((clientToken?.length === 0 || (clientToken && clientToken.length > 64)) && !Token.isUnresolved(clientToken)) { + throw new Error(`The length of \'clientToken\' must range from 1 to 64 characters, got: ${clientToken.length} characters`); + } + const resource = new CfnAccessPoint(this, 'Resource', { fileSystemId: props.fileSystem.fileSystemId, rootDirectory: { @@ -216,6 +230,7 @@ export class AccessPoint extends AccessPointBase { gid: props.posixUser.gid, secondaryGids: props.posixUser.secondaryGids, } : undefined, + clientToken, }); Tags.of(this).add('Name', this.node.path); diff --git a/packages/aws-cdk-lib/aws-efs/test/access-point.test.ts b/packages/aws-cdk-lib/aws-efs/test/access-point.test.ts index 2a9b0387594dd..8f867d939fb04 100644 --- a/packages/aws-cdk-lib/aws-efs/test/access-point.test.ts +++ b/packages/aws-cdk-lib/aws-efs/test/access-point.test.ts @@ -50,6 +50,35 @@ test('support tags for AccessPoint', () => { }); }); +test('allow client token to be set for AccessPoint', () => { + // WHEN + new AccessPoint(stack, 'MyAccessPoint', { + fileSystem, + clientToken: 'client-token', + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::EFS::AccessPoint', { + ClientToken: 'client-token', + }); +}); + +test('throw when client token has a length that is less than 1', () => { + expect(() => new AccessPoint(stack, 'MyAccessPoint', { + fileSystem, + clientToken: '', + }, + )).toThrow(/The length of \'clientToken\' must range from 1 to 64 characters, got: 0 characters/); +}); + +test('throw when client token has a length that is greater than 64', () => { + expect(() => new AccessPoint(stack, 'MyAccessPoint', { + fileSystem, + clientToken: 'a'.repeat(65), + }, + )).toThrow(/The length of \'clientToken\' must range from 1 to 64 characters, got: 65 characters/); +}); + test('import an AccessPoint using fromAccessPointId', () => { // WHEN const ap = new AccessPoint(stack, 'MyAccessPoint', {