diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 3547453719a1d..6e3415acbc233 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -703,6 +703,12 @@ export interface IUserPool extends IResource { * Register an identity provider with this user pool. */ registerIdentityProvider(provider: IUserPoolIdentityProvider): void; + + /** + * Adds an IAM policy statement associated with this user pool to an + * IAM principal's policy. + */ + grant(grantee: IGrantable, ...actions: string[]): Grant; } abstract class UserPoolBase extends Resource implements IUserPool { @@ -735,10 +741,6 @@ abstract class UserPoolBase extends Resource implements IUserPool { this.identityProviders.push(provider); } - /** - * Adds an IAM policy statement associated with this user pool to an - * IAM principal's policy. - */ public grant(grantee: IGrantable, ...actions: string[]): Grant { return Grant.addToPrincipal({ grantee, diff --git a/packages/@aws-cdk/aws-docdb/README.md b/packages/@aws-cdk/aws-docdb/README.md index 33aa65fdc916d..a86bfe5b77460 100644 --- a/packages/@aws-cdk/aws-docdb/README.md +++ b/packages/@aws-cdk/aws-docdb/README.md @@ -25,7 +25,7 @@ const cluster = new docdb.DatabaseCluster(this, 'Database', { excludeCharacters: '\"@/:', // optional, defaults to the set "\"@/" and is also used for eventually created rotations secretName: '/myapp/mydocdb/masteruser', // optional, if you prefer to specify the secret name }, - instanceType: ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE), vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, @@ -78,7 +78,7 @@ const cluster = new docdb.DatabaseCluster(this, 'Database', { masterUser: { username: 'myuser', }, - instanceType: ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE), vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, @@ -150,7 +150,7 @@ const cluster = new docdb.DatabaseCluster(this, 'Database', { masterUser: { username: 'myuser', }, - instanceType: ec2.InstanceType.of(ec2.InstanceClass.R5, ec2.InstanceSize.LARGE), + instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE), vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, diff --git a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/aws-cdk-docdb-cluster-rotation.template.json b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/aws-cdk-docdb-cluster-rotation.template.json index 1b9a1420fdf28..aba37350f74aa 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/aws-cdk-docdb-cluster-rotation.template.json +++ b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/aws-cdk-docdb-cluster-rotation.template.json @@ -672,7 +672,9 @@ }, "excludeCharacters": "\"@/" } - } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Mappings": { diff --git a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/integ.json b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/integ.json index 2e47ecb3d7615..5466489d4db95 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/integ.json @@ -1,7 +1,7 @@ { - "version": "18.0.0", + "version": "20.0.0", "testCases": { - "aws-docdb/test/integ.cluster-rotation.lit": { + "integ.cluster-rotation.lit": { "stacks": [ "aws-cdk-docdb-cluster-rotation" ], diff --git a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/manifest.json index b2ce0da74a287..f48bfe21a93fb 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/tree.json b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/tree.json index 4e837468aa909..462203c5dc77f 100644 --- a/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-docdb/test/cluster-rotation.lit.integ.snapshot/tree.json @@ -8,8 +8,8 @@ "id": "Tree", "path": "Tree", "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.33" } }, "aws-cdk-docdb-cluster-rotation": { diff --git a/packages/@aws-cdk/aws-iam/lib/policy-document.ts b/packages/@aws-cdk/aws-iam/lib/policy-document.ts index f41ef46812a92..c11e41312b1ce 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-document.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-document.ts @@ -76,6 +76,7 @@ export class PolicyDocument implements cdk.IResolvable { } public resolve(context: cdk.IResolveContext): any { + this.freezeStatements(); this._maybeMergeStatements(context.scope); // In the previous implementation of 'merge', sorting of actions/resources on @@ -212,6 +213,7 @@ export class PolicyDocument implements cdk.IResolvable { const newDocs: PolicyDocument[] = []; // Maps final statements to original statements + this.freezeStatements(); let statementsToOriginals = new Map(this.statements.map(s => [s, [s]])); // We always run 'mergeStatements' to minimize the policy before splitting. @@ -298,4 +300,13 @@ export class PolicyDocument implements cdk.IResolvable { private shouldMerge(scope: IConstruct) { return this.minimize ?? cdk.FeatureFlags.of(scope).isEnabled(cxapi.IAM_MINIMIZE_POLICIES) ?? false; } + + /** + * Freeze all statements + */ + private freezeStatements() { + for (const statement of this.statements) { + statement.freeze(); + } + } } diff --git a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts index 19cf7cff8b85b..f912a4c180182 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-statement.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-statement.ts @@ -69,16 +69,6 @@ export class PolicyStatement { return ret; } - /** - * Statement ID for this statement - */ - public sid?: string; - - /** - * Whether to allow or deny the actions in this statement - */ - public effect: Effect; - private readonly _action = new Array(); private readonly _notAction = new Array(); private readonly _principal: { [key: string]: any[] } = {}; @@ -86,11 +76,14 @@ export class PolicyStatement { private readonly _resource = new Array(); private readonly _notResource = new Array(); private readonly _condition: { [key: string]: any } = { }; + private _sid?: string; + private _effect: Effect; private principalConditionsJson?: string; // Hold on to those principals private readonly _principals = new Array(); private readonly _notPrincipals = new Array(); + private _frozen = false; constructor(props: PolicyStatementProps = {}) { // Validate actions @@ -101,8 +94,8 @@ export class PolicyStatement { } } - this.sid = props.sid; - this.effect = props.effect || Effect.ALLOW; + this._sid = props.sid; + this._effect = props.effect || Effect.ALLOW; this.addActions(...props.actions || []); this.addNotActions(...props.notActions || []); @@ -115,6 +108,36 @@ export class PolicyStatement { } } + /** + * Statement ID for this statement + */ + public get sid(): string | undefined { + return this._sid; + } + + /** + * Set Statement ID for this statement + */ + public set sid(sid: string | undefined) { + this.assertNotFrozen('sid'); + this._sid = sid; + } + + /** + * Whether to allow or deny the actions in this statement + */ + public get effect(): Effect { + return this._effect; + } + + /** + * Set effect for this statement + */ + public set effect(effect: Effect) { + this.assertNotFrozen('effect'); + this._effect = effect; + } + // // Actions // @@ -127,6 +150,7 @@ export class PolicyStatement { * @param actions actions that will be allowed. */ public addActions(...actions: string[]) { + this.assertNotFrozen('addActions'); if (actions.length > 0 && this._notAction.length > 0) { throw new Error('Cannot add \'Actions\' to policy statement if \'NotActions\' have been added'); } @@ -142,6 +166,7 @@ export class PolicyStatement { * @param notActions actions that will be denied. All other actions will be permitted. */ public addNotActions(...notActions: string[]) { + this.assertNotFrozen('addNotActions'); if (notActions.length > 0 && this._action.length > 0) { throw new Error('Cannot add \'NotActions\' to policy statement if \'Actions\' have been added'); } @@ -167,6 +192,7 @@ export class PolicyStatement { * @param principals IAM principals that will be added */ public addPrincipals(...principals: IPrincipal[]) { + this.assertNotFrozen('addPrincipals'); this._principals.push(...principals); if (Object.keys(principals).length > 0 && Object.keys(this._notPrincipal).length > 0) { throw new Error('Cannot add \'Principals\' to policy statement if \'NotPrincipals\' have been added'); @@ -188,6 +214,7 @@ export class PolicyStatement { * @param notPrincipals IAM principals that will be denied access */ public addNotPrincipals(...notPrincipals: IPrincipal[]) { + this.assertNotFrozen('addNotPrincipals'); this._notPrincipals.push(...notPrincipals); if (Object.keys(notPrincipals).length > 0 && Object.keys(this._principal).length > 0) { throw new Error('Cannot add \'NotPrincipals\' to policy statement if \'Principals\' have been added'); @@ -280,6 +307,7 @@ export class PolicyStatement { * @param arns Amazon Resource Names (ARNs) of the resources that this policy statement applies to */ public addResources(...arns: string[]) { + this.assertNotFrozen('addResources'); if (arns.length > 0 && this._notResource.length > 0) { throw new Error('Cannot add \'Resources\' to policy statement if \'NotResources\' have been added'); } @@ -295,6 +323,7 @@ export class PolicyStatement { * @param arns Amazon Resource Names (ARNs) of the resources that this policy statement does not apply to */ public addNotResources(...arns: string[]) { + this.assertNotFrozen('addNotResources'); if (arns.length > 0 && this._resource.length > 0) { throw new Error('Cannot add \'NotResources\' to policy statement if \'Resources\' have been added'); } @@ -344,6 +373,7 @@ export class PolicyStatement { * ``` */ public addCondition(key: string, value: Condition) { + this.assertNotFrozen('addCondition'); const existingValue = this._condition[key]; this._condition[key] = existingValue ? { ...existingValue, ...value } : value; } @@ -544,6 +574,29 @@ export class PolicyStatement { return { ...this._condition }; } + /** + * Make the PolicyStatement immutable + * + * After calling this, any of the `addXxx()` methods will throw an exception. + * + * Libraries that lazily generate statement bodies can override this method to + * fill the actual PolicyStatement fields. Be aware that this method may be called + * multiple times. + */ + public freeze(): PolicyStatement { + this._frozen = true; + return this; + } + + /** + * Whether the PolicyStatement has been frozen + * + * The statement object is frozen when `freeze()` is called. + */ + public get frozen(): boolean { + return this._frozen; + } + /** * Estimate the size of this policy statement * @@ -577,6 +630,15 @@ export class PolicyStatement { } } } + + /** + * Throw an exception when the object is frozen + */ + private assertNotFrozen(method: string) { + if (this._frozen) { + throw new Error(`${method}: freeze() has been called on this PolicyStatement previously, so it can no longer be modified`); + } + } } /** diff --git a/packages/@aws-cdk/aws-iam/test/merge-statements.test.ts b/packages/@aws-cdk/aws-iam/test/merge-statements.test.ts index 061db0e134d02..c4d84204352f6 100644 --- a/packages/@aws-cdk/aws-iam/test/merge-statements.test.ts +++ b/packages/@aws-cdk/aws-iam/test/merge-statements.test.ts @@ -471,6 +471,25 @@ test('keep merging even if it requires multiple passes', () => { ]); }); +test('lazily generated statements are merged correctly', () => { + assertMerged([ + new LazyStatement((s) => { + s.addActions('service:A'); + s.addResources('R1'); + }), + new LazyStatement((s) => { + s.addActions('service:B'); + s.addResources('R1'); + }), + ], [ + { + Effect: 'Allow', + Action: ['service:A', 'service:B'], + Resource: 'R1', + }, + ]); +}); + function assertNoMerge(statements: iam.PolicyStatement[]) { const app = new App(); const stack = new Stack(app, 'Stack'); @@ -499,3 +518,17 @@ function assertMerged(statements: iam.PolicyStatement[], expected: any[]) { function assertMergedC(doMerge: boolean, statements: iam.PolicyStatement[], expected: any[]) { return doMerge ? assertMerged(statements, expected) : assertNoMerge(statements); } + +/** + * A statement that fills itself only when freeze() is called. + */ +class LazyStatement extends iam.PolicyStatement { + constructor(private readonly modifyMe: (x: iam.PolicyStatement) => void) { + super(); + } + + public freeze() { + this.modifyMe(this); + return super.freeze(); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts b/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts index 7498ee2814d80..ec1f9f691f727 100644 --- a/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts +++ b/packages/@aws-cdk/aws-iam/test/policy-statement.test.ts @@ -1,5 +1,5 @@ import { Stack } from '@aws-cdk/core'; -import { AnyPrincipal, Group, PolicyDocument, PolicyStatement } from '../lib'; +import { AnyPrincipal, Group, PolicyDocument, PolicyStatement, Effect } from '../lib'; describe('IAM policy statement', () => { @@ -214,4 +214,31 @@ describe('IAM policy statement', () => { expect(() => policyStatement.addNotPrincipals(group)) .toThrow(/Cannot use an IAM Group as the 'Principal' or 'NotPrincipal' in an IAM Policy/); }); + + + test('a frozen policy statement cannot be modified any more', () => { + // GIVEN + const statement = new PolicyStatement({ + actions: ['action:a'], + resources: ['*'], + }); + statement.freeze(); + + // WHEN + const modifications = [ + () => statement.sid = 'asdf', + () => statement.effect = Effect.DENY, + () => statement.addActions('abc:def'), + () => statement.addNotActions('abc:def'), + () => statement.addResources('*'), + () => statement.addNotResources('*'), + () => statement.addPrincipals(new AnyPrincipal()), + () => statement.addNotPrincipals(new AnyPrincipal()), + () => statement.addCondition('key', 'value'), + ]; + + for (const mod of modifications) { + expect(mod).toThrow(/can no longer be modified/); + } + }); }); diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 9f186d471d173..1db33fbd80631 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -155,12 +155,13 @@ if (fn.timeout) { AWS Lambda supports resource-based policies for controlling access to Lambda functions and layers on a per-resource basis. In particular, this allows you to -give permission to AWS services and other AWS accounts to modify and invoke your -functions. You can also restrict permissions given to AWS services by providing -a source account or ARN (representing the account and identifier of the resource -that accesses the function or layer). +give permission to AWS services, AWS Organizations, or other AWS accounts to +modify and invoke your functions. + +### Grant function access to AWS services ```ts +// Grant permissions to a service declare const fn: lambda.Function; const principal = new iam.ServicePrincipal('my-service'); @@ -172,10 +173,58 @@ fn.addPermission('my-service Invocation', { }); ``` -For more information, see [Resource-based -policies](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html) +You can also restrict permissions given to AWS services by providing +a source account or ARN (representing the account and identifier of the resource +that accesses the function or layer). + +For more information, see +[Granting function access to AWS services](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-serviceinvoke) +in the AWS Lambda Developer Guide. + +### Grant function access to an AWS Organization + +```ts +// Grant permissions to an entire AWS organization +declare const fn: lambda.Function; +const org = new iam.OrganizationPrincipal('o-xxxxxxxxxx'); + +fn.grantInvoke(org); +``` + +In the above example, the `principal` will be `*` and all users in the +organization `o-xxxxxxxxxx` will get function invocation permissions. + +You can restrict permissions given to the organization by specifying an +AWS account or role as the `principal`: + +```ts +// Grant permission to an account ONLY IF they are part of the organization +declare const fn: lambda.Function; +const account = new iam.AccountPrincipal('123456789012'); + +fn.grantInvoke(account.inOrganization('o-xxxxxxxxxx')); +``` + +For more information, see +[Granting function access to an organization](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-xorginvoke) in the AWS Lambda Developer Guide. +### Grant function access to other AWS accounts + +```ts +// Grant permission to other AWS account +declare const fn: lambda.Function; +const account = new iam.AccountPrincipal('123456789012'); + +fn.grantInvoke(account); +``` + +For more information, see +[Granting function access to other accounts](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-xaccountinvoke) +in the AWS Lambda Developer Guide. + +### Grant function access to unowned principals + Providing an unowned principal (such as account principals, generic ARN principals, service principals, and principals in other accounts) to a call to `fn.grantInvoke` will result in a resource-based policy being created. If the @@ -198,13 +247,6 @@ const servicePrincipalWithConditions = servicePrincipal.withConditions({ }); fn.grantInvoke(servicePrincipalWithConditions); - -// Equivalent to: -fn.addPermission('my-service Invocation', { - principal: servicePrincipal, - sourceArn: sourceArn, - sourceAccount: sourceAccount, -}); ``` ## Versions diff --git a/packages/@aws-cdk/aws-lambda/lib/function-base.ts b/packages/@aws-cdk/aws-lambda/lib/function-base.ts index dde2d6a212f2a..14376c3b32909 100644 --- a/packages/@aws-cdk/aws-lambda/lib/function-base.ts +++ b/packages/@aws-cdk/aws-lambda/lib/function-base.ts @@ -343,8 +343,10 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC return; } - const principal = this.parsePermissionPrincipal(permission.principal); - const { sourceAccount, sourceArn } = this.parseConditions(permission.principal) ?? {}; + let principal = this.parsePermissionPrincipal(permission.principal); + + let { sourceArn, sourceAccount, principalOrgID } = this.validateConditionCombinations(permission.principal) ?? {}; + const action = permission.action ?? 'lambda:InvokeFunction'; const scope = permission.scope ?? this; @@ -357,6 +359,7 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC eventSourceToken: permission.eventSourceToken, sourceAccount: permission.sourceAccount ?? sourceAccount, sourceArn: permission.sourceArn ?? sourceArn, + principalOrgId: permission.organizationId ?? principalOrgID, functionUrlAuthType: permission.functionUrlAuthType, }); } @@ -552,7 +555,6 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC private parsePermissionPrincipal(principal: iam.IPrincipal) { // Try some specific common classes first. // use duck-typing, not instance of - // @deprecated: after v2, we can change these to 'instanceof' if ('wrapped' in principal) { // eslint-disable-next-line dot-notation principal = principal['wrapped']; @@ -570,6 +572,15 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC return (principal as iam.ArnPrincipal).arn; } + const stringEquals = matchSingleKey('StringEquals', principal.policyFragment.conditions); + if (stringEquals) { + const orgId = matchSingleKey('aws:PrincipalOrgID', stringEquals); + if (orgId) { + // we will move the organization id to the `principalOrgId` property of `Permissions`. + return '*'; + } + } + // Try a best-effort approach to support simple principals that are not any of the predefined // classes, but are simple enough that they will fit into the Permission model. Main target // here: imported Roles, Users, Groups. @@ -584,17 +595,67 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC } throw new Error(`Invalid principal type for Lambda permission statement: ${principal.constructor.name}. ` + - 'Supported: AccountPrincipal, ArnPrincipal, ServicePrincipal'); + 'Supported: AccountPrincipal, ArnPrincipal, ServicePrincipal, OrganizationPrincipal'); + + /** + * Returns the value at the key if the object contains the key and nothing else. Otherwise, + * returns undefined. + */ + function matchSingleKey(key: string, obj: Record): any | undefined { + if (Object.keys(obj).length !== 1) { return undefined; } + + return obj[key]; + } + } - private parseConditions(principal: iam.IPrincipal): { sourceAccount: string, sourceArn: string } | null { + private validateConditionCombinations(principal: iam.IPrincipal): { + sourceArn: string | undefined, + sourceAccount: string | undefined, + principalOrgID: string | undefined, + } | undefined { + const conditions = this.validateConditions(principal); + + if (!conditions) { return undefined; } + + const sourceArn = conditions.ArnLike ? conditions.ArnLike['aws:SourceArn'] : undefined; + const sourceAccount = conditions.StringEquals ? conditions.StringEquals['aws:SourceAccount'] : undefined; + const principalOrgID = conditions.StringEquals ? conditions.StringEquals['aws:PrincipalOrgID'] : undefined; + + // PrincipalOrgID cannot be combined with any other conditions + if (principalOrgID && (sourceArn || sourceAccount)) { + throw new Error('PrincipalWithConditions had unsupported condition combinations for Lambda permission statement: principalOrgID cannot be set with other conditions.'); + } + + return { + sourceArn, + sourceAccount, + principalOrgID, + }; + } + + private validateConditions(principal: iam.IPrincipal): iam.Conditions | undefined { if (this.isPrincipalWithConditions(principal)) { const conditions: iam.Conditions = principal.policyFragment.conditions; const conditionPairs = flatMap( Object.entries(conditions), ([operator, conditionObjs]) => Object.keys(conditionObjs as object).map(key => { return { operator, key }; }), ); - const supportedPrincipalConditions = [{ operator: 'ArnLike', key: 'aws:SourceArn' }, { operator: 'StringEquals', key: 'aws:SourceAccount' }]; + + // These are all the supported conditions. Some combinations are not supported, + // like only 'aws:SourceArn' or 'aws:PrincipalOrgID' and 'aws:SourceAccount'. + // These will be validated through `this.validateConditionCombinations`. + const supportedPrincipalConditions = [{ + operator: 'ArnLike', + key: 'aws:SourceArn', + }, + { + operator: 'StringEquals', + key: 'aws:SourceAccount', + }, { + operator: 'StringEquals', + key: 'aws:PrincipalOrgID', + }]; const unsupportedConditions = conditionPairs.filter( (condition) => !supportedPrincipalConditions.some( @@ -603,21 +664,18 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC ); if (unsupportedConditions.length == 0) { - return { - sourceAccount: conditions.StringEquals['aws:SourceAccount'], - sourceArn: conditions.ArnLike['aws:SourceArn'], - }; + return conditions; } else { throw new Error(`PrincipalWithConditions had unsupported conditions for Lambda permission statement: ${JSON.stringify(unsupportedConditions)}. ` + `Supported operator/condition pairs: ${JSON.stringify(supportedPrincipalConditions)}`); } - } else { - return null; } + + return undefined; } - private isPrincipalWithConditions(principal: iam.IPrincipal): principal is iam.PrincipalWithConditions { - return 'conditions' in principal; + private isPrincipalWithConditions(principal: iam.IPrincipal): boolean { + return Object.keys(principal.policyFragment.conditions).length > 0; } } diff --git a/packages/@aws-cdk/aws-lambda/lib/permission.ts b/packages/@aws-cdk/aws-lambda/lib/permission.ts index c493722e2f543..8f367dd63d312 100644 --- a/packages/@aws-cdk/aws-lambda/lib/permission.ts +++ b/packages/@aws-cdk/aws-lambda/lib/permission.ts @@ -23,19 +23,22 @@ export interface Permission { * A unique token that must be supplied by the principal invoking the * function. * - * @default The caller would not need to present a token. + * @default - The caller would not need to present a token. */ readonly eventSourceToken?: string; /** * The entity for which you are granting permission to invoke the Lambda - * function. This entity can be any valid AWS service principal, such as - * s3.amazonaws.com or sns.amazonaws.com, or, if you are granting - * cross-account permission, an AWS account ID. For example, you might want - * to allow a custom application in another AWS account to push events to - * Lambda by invoking your function. + * function. This entity can be any of the following: * - * The principal can be either an AccountPrincipal or a ServicePrincipal. + * - a valid AWS service principal, such as `s3.amazonaws.com` or `sns.amazonaws.com` + * - an AWS account ID for cross-account permissions. For example, you might want + * to allow a custom application in another AWS account to push events to + * Lambda by invoking your function. + * - an AWS organization principal to grant permissions to an entire organization. + * + * The principal can be an AccountPrincipal, an ArnPrincipal, a ServicePrincipal, + * or an OrganizationPrincipal. */ readonly principal: iam.IPrincipal; @@ -67,6 +70,19 @@ export interface Permission { */ readonly sourceArn?: string; + /** + * The organization you want to grant permissions to. Use this ONLY if you + * need to grant permissions to a subset of the organization. If you want to + * grant permissions to the entire organization, sending the organization principal + * through the `principal` property will suffice. + * + * You can use this property to ensure that all source principals are owned by + * a specific organization. + * + * @default - No organizationId + */ + readonly organizationId?: string; + /** * The authType for the function URL that you are granting permissions for. * diff --git a/packages/@aws-cdk/aws-lambda/test/function.test.ts b/packages/@aws-cdk/aws-lambda/test/function.test.ts index 4706d8b6e5a50..fe93c2bcecb69 100644 --- a/packages/@aws-cdk/aws-lambda/test/function.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/function.test.ts @@ -118,7 +118,7 @@ describe('function', () => { })).toThrow(); }); - describe('addToResourcePolicy', () => { + describe('addPermissions', () => { test('can be used to add permissions to the Lambda function', () => { const stack = new cdk.Stack(); const fn = newTestLambda(stack); @@ -183,16 +183,42 @@ describe('function', () => { }); }); - test('fails if the principal is not a service, account or arn principal', () => { + test('can supply principalOrgID via permission property', () => { + const stack = new cdk.Stack(); + const fn = newTestLambda(stack); + const org = new iam.OrganizationPrincipal('o-xxxxxxxxxx'); + const account = new iam.AccountPrincipal('123456789012'); + + fn.addPermission('S3Permission', { + action: 'lambda:*', + principal: account, + organizationId: org.organizationId, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { + Action: 'lambda:*', + FunctionName: { + 'Fn::GetAtt': [ + 'MyLambdaCCE802FB', + 'Arn', + ], + }, + Principal: account.accountId, + PrincipalOrgID: org.organizationId, + }); + }); + + test('fails if the principal is not a service, account, arn, or organization principal', () => { const stack = new cdk.Stack(); const fn = newTestLambda(stack); - expect(() => fn.addPermission('F1', { principal: new iam.OrganizationPrincipal('org') })) + expect(() => fn.addPermission('F1', { principal: new iam.CanonicalUserPrincipal('org') })) .toThrow(/Invalid principal type for Lambda permission statement/); fn.addPermission('S1', { principal: new iam.ServicePrincipal('my-service') }); fn.addPermission('S2', { principal: new iam.AccountPrincipal('account') }); fn.addPermission('S3', { principal: new iam.ArnPrincipal('my:arn') }); + fn.addPermission('S4', { principal: new iam.OrganizationPrincipal('my:org') }); }); test('applies source account/ARN conditions if the principal has conditions', () => { @@ -226,6 +252,58 @@ describe('function', () => { }); }); + test('applies source arn condition if principal has conditions', () => { + const stack = new cdk.Stack(); + const fn = newTestLambda(stack); + const sourceArn = 'some-arn'; + const service = 'my-service'; + const principal = new iam.PrincipalWithConditions(new iam.ServicePrincipal(service), { + ArnLike: { + 'aws:SourceArn': sourceArn, + }, + }); + + fn.addPermission('S1', { principal: principal }); + + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'MyLambdaCCE802FB', + 'Arn', + ], + }, + Principal: service, + SourceArn: sourceArn, + }); + }); + + test('applies principal org id conditions if the principal has conditions', () => { + const stack = new cdk.Stack(); + const fn = newTestLambda(stack); + const principalOrgId = 'org-xxxxxxxxxx'; + const service = 'my-service'; + const principal = new iam.PrincipalWithConditions(new iam.ServicePrincipal(service), { + StringEquals: { + 'aws:PrincipalOrgID': principalOrgId, + }, + }); + + fn.addPermission('S1', { principal: principal }); + + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'MyLambdaCCE802FB', + 'Arn', + ], + }, + Principal: service, + PrincipalOrgID: principalOrgId, + }); + }); + test('fails if the principal has conditions that are not supported', () => { const stack = new cdk.Stack(); const fn = newTestLambda(stack); @@ -253,6 +331,23 @@ describe('function', () => { })).toThrow(/PrincipalWithConditions had unsupported conditions for Lambda permission statement/); }); + test('fails if the principal has condition combinations that are not supported', () => { + const stack = new cdk.Stack(); + const fn = newTestLambda(stack); + + expect(() => fn.addPermission('F2', { + principal: new iam.PrincipalWithConditions(new iam.ServicePrincipal('my-service'), { + StringEquals: { + 'aws:SourceAccount': 'source-account', + 'aws:PrincipalOrgID': 'principal-org-id', + }, + ArnLike: { + 'aws:SourceArn': 'source-arn', + }, + }), + })).toThrow(/PrincipalWithConditions had unsupported condition combinations for Lambda permission statement/); + }); + test('BYORole', () => { // GIVEN const stack = new cdk.Stack(); @@ -1239,6 +1334,33 @@ describe('function', () => { }); }); + test('with an organization principal', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new lambda.Function(stack, 'Function', { + code: lambda.Code.fromInline('xxx'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_14_X, + }); + const org = new iam.OrganizationPrincipal('my-org-id'); + + // WHEN + fn.grantInvoke(org); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: { + 'Fn::GetAtt': [ + 'Function76856677', + 'Arn', + ], + }, + Principal: '*', + PrincipalOrgID: 'my-org-id', + }); + }); + test('can be called twice for the same service principal', () => { // GIVEN const stack = new cdk.Stack(); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.permissions.ts b/packages/@aws-cdk/aws-lambda/test/integ.permissions.ts new file mode 100644 index 0000000000000..92a9964dd76d2 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.permissions.ts @@ -0,0 +1,17 @@ +import * as iam from '@aws-cdk/aws-iam'; +import * as cdk from '@aws-cdk/core'; +import * as lambda from '../lib'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'lambda-permissions'); + +const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_14_X, +}); + +fn.grantInvoke(new iam.AnyPrincipal().inOrganization('o-yyyyyyyyyy')); + +fn.grantInvoke(new iam.OrganizationPrincipal('o-xxxxxxxxxx')); diff --git a/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..588d7b269d34f --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/integ.json new file mode 100644 index 0000000000000..2e8ba8922ac72 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/integ.json @@ -0,0 +1,14 @@ +{ + "version": "20.0.0", + "testCases": { + "integ.permissions": { + "stacks": [ + "lambda-permissions" + ], + "diffAssets": false, + "stackUpdateWorkflow": true + } + }, + "synthContext": {}, + "enableLookups": false +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/lambda-permissions.assets.json b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/lambda-permissions.assets.json new file mode 100644 index 0000000000000..1a37de91e4acd --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/lambda-permissions.assets.json @@ -0,0 +1,19 @@ +{ + "version": "20.0.0", + "files": { + "f189cb5ef6ca8751f98c7dbbfdfb7dac59c18d9bb287a62e9b01d4fb43156f7e": { + "source": { + "path": "lambda-permissions.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "f189cb5ef6ca8751f98c7dbbfdfb7dac59c18d9bb287a62e9b01d4fb43156f7e.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/lambda-permissions.template.json b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/lambda-permissions.template.json new file mode 100644 index 0000000000000..74c24026d0f44 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/lambda-permissions.template.json @@ -0,0 +1,82 @@ +{ + "Resources": { + "MyLambdaServiceRole4539ECB6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyLambdaCCE802FB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Handler": "index.handler", + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "MyLambdaServiceRole4539ECB6" + ] + }, + "MyLambdaInvokehlab6Vr41INt1IUXIhhCesB4gzNedP5IURKNgciwD9D5EABD": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "Principal": "*", + "PrincipalOrgID": "o-yyyyyyyyyy" + } + }, + "MyLambdaInvoke138AF9IJcZORjZNKCKShZMMuVQwCnUkbFqMoQf5of0C1F7DFD8": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "Principal": "*", + "PrincipalOrgID": "o-xxxxxxxxxx" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..b1a86d7f35697 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/manifest.json @@ -0,0 +1,64 @@ +{ + "version": "20.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "lambda-permissions": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "lambda-permissions.template.json", + "validateOnSynth": false + }, + "metadata": { + "/lambda-permissions/MyLambda/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaServiceRole4539ECB6" + } + ], + "/lambda-permissions/MyLambda/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaCCE802FB" + } + ], + "/lambda-permissions/MyLambda/Invokehl--ab6+Vr41INt1IUX--IhhCesB4gzNedP5IURKNgciw=": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaInvokehlab6Vr41INt1IUXIhhCesB4gzNedP5IURKNgciwD9D5EABD" + } + ], + "/lambda-permissions/MyLambda/Invoke138AF9IJcZORjZ--NKCKShZMMuVQwCnUkbFqMoQf5of0=": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaInvoke138AF9IJcZORjZNKCKShZMMuVQwCnUkbFqMoQf5of0C1F7DFD8" + } + ], + "MyLambdaInvokeAnyPrincipal256ECDE1": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaInvokeAnyPrincipal256ECDE1", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "MyLambdaInvokeOrganizationPrincipaloxxxxxxxxxxEB282AD1": [ + { + "type": "aws:cdk:logicalId", + "data": "MyLambdaInvokeOrganizationPrincipaloxxxxxxxxxxEB282AD1", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ] + }, + "displayName": "lambda-permissions" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/tree.json new file mode 100644 index 0000000000000..51fbde7100a28 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/permissions.integ.snapshot/tree.json @@ -0,0 +1,158 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.33" + } + }, + "lambda-permissions": { + "id": "lambda-permissions", + "path": "lambda-permissions", + "children": { + "MyLambda": { + "id": "MyLambda", + "path": "lambda-permissions/MyLambda", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "lambda-permissions/MyLambda/ServiceRole", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-permissions/MyLambda/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-permissions/MyLambda/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "foo" + }, + "role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "handler": "index.handler", + "runtime": "nodejs14.x" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.CfnFunction", + "version": "0.0.0" + } + }, + "Invokehl--ab6+Vr41INt1IUX--IhhCesB4gzNedP5IURKNgciw=": { + "id": "Invokehl--ab6+Vr41INt1IUX--IhhCesB4gzNedP5IURKNgciw=", + "path": "lambda-permissions/MyLambda/Invokehl--ab6+Vr41INt1IUX--IhhCesB4gzNedP5IURKNgciw=", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "principal": "*", + "principalOrgId": "o-yyyyyyyyyy" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.CfnPermission", + "version": "0.0.0" + } + }, + "Invoke138AF9IJcZORjZ--NKCKShZMMuVQwCnUkbFqMoQf5of0=": { + "id": "Invoke138AF9IJcZORjZ--NKCKShZMMuVQwCnUkbFqMoQf5of0=", + "path": "lambda-permissions/MyLambda/Invoke138AF9IJcZORjZ--NKCKShZMMuVQwCnUkbFqMoQf5of0=", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Permission", + "aws:cdk:cloudformation:props": { + "action": "lambda:InvokeFunction", + "functionName": { + "Fn::GetAtt": [ + "MyLambdaCCE802FB", + "Arn" + ] + }, + "principal": "*", + "principalOrgId": "o-xxxxxxxxxx" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.CfnPermission", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-lambda.Function", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/aws-cdk-rds-cluster-rotation.template.json b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/aws-cdk-rds-cluster-rotation.template.json index 5ff6ccf5caaf8..d8c03d16f4d19 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/aws-cdk-rds-cluster-rotation.template.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/aws-cdk-rds-cluster-rotation.template.json @@ -694,7 +694,9 @@ }, "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" } - } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Mappings": { diff --git a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/cdk.out index 90bef2e09ad39..588d7b269d34f 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/integ.json b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/integ.json index 6e3d8b141c440..fd51e5c086ca5 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/integ.json @@ -1,7 +1,7 @@ { - "version": "18.0.0", + "version": "20.0.0", "testCases": { - "aws-rds/test/integ.cluster-rotation.lit": { + "integ.cluster-rotation.lit": { "stacks": [ "aws-cdk-rds-cluster-rotation" ], diff --git a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/manifest.json index d46eee42ff80a..e61d37f939d72 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "20.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/tree.json index d5fc24686e317..263b95885df85 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-rotation.lit.integ.snapshot/tree.json @@ -8,8 +8,8 @@ "id": "Tree", "path": "Tree", "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.1.33" } }, "aws-cdk-rds-cluster-rotation": { diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.d.ts b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.d.ts deleted file mode 100644 index a64fd5d9eb2dc..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { IsCompleteRequest, IsCompleteResponse, OnEventRequest, OnEventResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types'; -export declare function onEventHandler(event: OnEventRequest): Promise; -export declare function isCompleteHandler(event: IsCompleteRequest): Promise; diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.js b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.js deleted file mode 100644 index de753f1849b44..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.js +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isCompleteHandler = exports.onEventHandler = void 0; -const aws_sdk_1 = require("aws-sdk"); // eslint-disable-line import/no-extraneous-dependencies -async function onEventHandler(event) { - var _a; - console.log('Event: %j', event); - const rds = new aws_sdk_1.RDS(); - const physicalResourceId = `${event.ResourceProperties.DBClusterIdentifier}-${event.ResourceProperties.DBClusterIdentifier}`; - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - const data = await rds.createDBClusterSnapshot({ - DBClusterIdentifier: event.ResourceProperties.DBClusterIdentifier, - DBClusterSnapshotIdentifier: event.ResourceProperties.DBClusterSnapshotIdentifier, - }).promise(); - return { - PhysicalResourceId: physicalResourceId, - Data: { - DBClusterSnapshotArn: (_a = data.DBClusterSnapshot) === null || _a === void 0 ? void 0 : _a.DBClusterSnapshotArn, - }, - }; - } - if (event.RequestType === 'Delete') { - await rds.deleteDBClusterSnapshot({ - DBClusterSnapshotIdentifier: event.ResourceProperties.DBClusterSnapshotIdentifier, - }).promise(); - } - return { - PhysicalResourceId: `${event.ResourceProperties.DBClusterIdentifier}-${event.ResourceProperties.DBClusterIdentifier}`, - }; -} -exports.onEventHandler = onEventHandler; -async function isCompleteHandler(event) { - console.log('Event: %j', event); - const snapshotStatus = await tryGetClusterSnapshotStatus(event.ResourceProperties.DBClusterSnapshotIdentifier); - switch (event.RequestType) { - case 'Create': - case 'Update': - return { IsComplete: snapshotStatus === 'available' }; - case 'Delete': - return { IsComplete: snapshotStatus === undefined }; - } -} -exports.isCompleteHandler = isCompleteHandler; -async function tryGetClusterSnapshotStatus(identifier) { - var _a; - try { - const rds = new aws_sdk_1.RDS(); - const data = await rds.describeDBClusterSnapshots({ - DBClusterSnapshotIdentifier: identifier, - }).promise(); - return (_a = data.DBClusterSnapshots) === null || _a === void 0 ? void 0 : _a[0].Status; - } - catch (err) { - if (err.code === 'DBClusterSnapshotNotFoundFault') { - return undefined; - } - throw err; - } -} -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxxQ0FBOEIsQ0FBQyx3REFBd0Q7QUFFaEYsS0FBSyxVQUFVLGNBQWMsQ0FBQyxLQUFxQjs7SUFDeEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFaEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxhQUFHLEVBQUUsQ0FBQztJQUV0QixNQUFNLGtCQUFrQixHQUFHLEdBQUcsS0FBSyxDQUFDLGtCQUFrQixDQUFDLG1CQUFtQixJQUFJLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO0lBRTdILElBQUksS0FBSyxDQUFDLFdBQVcsS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLFdBQVcsS0FBSyxRQUFRLEVBQUU7UUFDcEUsTUFBTSxJQUFJLEdBQUcsTUFBTSxHQUFHLENBQUMsdUJBQXVCLENBQUM7WUFDN0MsbUJBQW1CLEVBQUUsS0FBSyxDQUFDLGtCQUFrQixDQUFDLG1CQUFtQjtZQUNqRSwyQkFBMkIsRUFBRSxLQUFLLENBQUMsa0JBQWtCLENBQUMsMkJBQTJCO1NBQ2xGLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNiLE9BQU87WUFDTCxrQkFBa0IsRUFBRSxrQkFBa0I7WUFDdEMsSUFBSSxFQUFFO2dCQUNKLG9CQUFvQixRQUFFLElBQUksQ0FBQyxpQkFBaUIsMENBQUUsb0JBQW9CO2FBQ25FO1NBQ0YsQ0FBQztLQUNIO0lBRUQsSUFBSSxLQUFLLENBQUMsV0FBVyxLQUFLLFFBQVEsRUFBRTtRQUNsQyxNQUFNLEdBQUcsQ0FBQyx1QkFBdUIsQ0FBQztZQUNoQywyQkFBMkIsRUFBRSxLQUFLLENBQUMsa0JBQWtCLENBQUMsMkJBQTJCO1NBQ2xGLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztLQUNkO0lBRUQsT0FBTztRQUNMLGtCQUFrQixFQUFFLEdBQUcsS0FBSyxDQUFDLGtCQUFrQixDQUFDLG1CQUFtQixJQUFJLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxtQkFBbUIsRUFBRTtLQUN0SCxDQUFDO0FBQ0osQ0FBQztBQTdCRCx3Q0E2QkM7QUFFTSxLQUFLLFVBQVUsaUJBQWlCLENBQUMsS0FBd0I7SUFDOUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFaEMsTUFBTSxjQUFjLEdBQUcsTUFBTSwyQkFBMkIsQ0FBQyxLQUFLLENBQUMsa0JBQWtCLENBQUMsMkJBQTJCLENBQUMsQ0FBQztJQUUvRyxRQUFRLEtBQUssQ0FBQyxXQUFXLEVBQUU7UUFDekIsS0FBSyxRQUFRLENBQUM7UUFDZCxLQUFLLFFBQVE7WUFDWCxPQUFPLEVBQUUsVUFBVSxFQUFFLGNBQWMsS0FBSyxXQUFXLEVBQUUsQ0FBQztRQUN4RCxLQUFLLFFBQVE7WUFDWCxPQUFPLEVBQUUsVUFBVSxFQUFFLGNBQWMsS0FBSyxTQUFTLEVBQUUsQ0FBQztLQUN2RDtBQUNILENBQUM7QUFaRCw4Q0FZQztBQUVELEtBQUssVUFBVSwyQkFBMkIsQ0FBQyxVQUFrQjs7SUFDM0QsSUFBSTtRQUNGLE1BQU0sR0FBRyxHQUFHLElBQUksYUFBRyxFQUFFLENBQUM7UUFDdEIsTUFBTSxJQUFJLEdBQUcsTUFBTSxHQUFHLENBQUMsMEJBQTBCLENBQUM7WUFDaEQsMkJBQTJCLEVBQUUsVUFBVTtTQUN4QyxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDYixhQUFPLElBQUksQ0FBQyxrQkFBa0IsMENBQUcsQ0FBQyxFQUFFLE1BQU0sQ0FBQztLQUM1QztJQUFDLE9BQU8sR0FBRyxFQUFFO1FBQ1osSUFBSSxHQUFHLENBQUMsSUFBSSxLQUFLLGdDQUFnQyxFQUFFO1lBQ2pELE9BQU8sU0FBUyxDQUFDO1NBQ2xCO1FBQ0QsTUFBTSxHQUFHLENBQUM7S0FDWDtBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlICovXG5pbXBvcnQgdHlwZSB7IElzQ29tcGxldGVSZXF1ZXN0LCBJc0NvbXBsZXRlUmVzcG9uc2UsIE9uRXZlbnRSZXF1ZXN0LCBPbkV2ZW50UmVzcG9uc2UgfSBmcm9tICdAYXdzLWNkay9jdXN0b20tcmVzb3VyY2VzL2xpYi9wcm92aWRlci1mcmFtZXdvcmsvdHlwZXMnO1xuaW1wb3J0IHsgUkRTIH0gZnJvbSAnYXdzLXNkayc7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBvbkV2ZW50SGFuZGxlcihldmVudDogT25FdmVudFJlcXVlc3QpOiBQcm9taXNlPE9uRXZlbnRSZXNwb25zZT4ge1xuICBjb25zb2xlLmxvZygnRXZlbnQ6ICVqJywgZXZlbnQpO1xuXG4gIGNvbnN0IHJkcyA9IG5ldyBSRFMoKTtcblxuICBjb25zdCBwaHlzaWNhbFJlc291cmNlSWQgPSBgJHtldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuREJDbHVzdGVySWRlbnRpZmllcn0tJHtldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuREJDbHVzdGVySWRlbnRpZmllcn1gO1xuXG4gIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScgfHwgZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdVcGRhdGUnKSB7XG4gICAgY29uc3QgZGF0YSA9IGF3YWl0IHJkcy5jcmVhdGVEQkNsdXN0ZXJTbmFwc2hvdCh7XG4gICAgICBEQkNsdXN0ZXJJZGVudGlmaWVyOiBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuREJDbHVzdGVySWRlbnRpZmllcixcbiAgICAgIERCQ2x1c3RlclNuYXBzaG90SWRlbnRpZmllcjogZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkRCQ2x1c3RlclNuYXBzaG90SWRlbnRpZmllcixcbiAgICB9KS5wcm9taXNlKCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICAgICAgRGF0YToge1xuICAgICAgICBEQkNsdXN0ZXJTbmFwc2hvdEFybjogZGF0YS5EQkNsdXN0ZXJTbmFwc2hvdD8uREJDbHVzdGVyU25hcHNob3RBcm4sXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdEZWxldGUnKSB7XG4gICAgYXdhaXQgcmRzLmRlbGV0ZURCQ2x1c3RlclNuYXBzaG90KHtcbiAgICAgIERCQ2x1c3RlclNuYXBzaG90SWRlbnRpZmllcjogZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkRCQ2x1c3RlclNuYXBzaG90SWRlbnRpZmllcixcbiAgICB9KS5wcm9taXNlKCk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogYCR7ZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkRCQ2x1c3RlcklkZW50aWZpZXJ9LSR7ZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkRCQ2x1c3RlcklkZW50aWZpZXJ9YCxcbiAgfTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGlzQ29tcGxldGVIYW5kbGVyKGV2ZW50OiBJc0NvbXBsZXRlUmVxdWVzdCk6IFByb21pc2U8SXNDb21wbGV0ZVJlc3BvbnNlPiB7XG4gIGNvbnNvbGUubG9nKCdFdmVudDogJWonLCBldmVudCk7XG5cbiAgY29uc3Qgc25hcHNob3RTdGF0dXMgPSBhd2FpdCB0cnlHZXRDbHVzdGVyU25hcHNob3RTdGF0dXMoZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkRCQ2x1c3RlclNuYXBzaG90SWRlbnRpZmllcik7XG5cbiAgc3dpdGNoIChldmVudC5SZXF1ZXN0VHlwZSkge1xuICAgIGNhc2UgJ0NyZWF0ZSc6XG4gICAgY2FzZSAnVXBkYXRlJzpcbiAgICAgIHJldHVybiB7IElzQ29tcGxldGU6IHNuYXBzaG90U3RhdHVzID09PSAnYXZhaWxhYmxlJyB9O1xuICAgIGNhc2UgJ0RlbGV0ZSc6XG4gICAgICByZXR1cm4geyBJc0NvbXBsZXRlOiBzbmFwc2hvdFN0YXR1cyA9PT0gdW5kZWZpbmVkIH07XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gdHJ5R2V0Q2x1c3RlclNuYXBzaG90U3RhdHVzKGlkZW50aWZpZXI6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIHRyeSB7XG4gICAgY29uc3QgcmRzID0gbmV3IFJEUygpO1xuICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZHMuZGVzY3JpYmVEQkNsdXN0ZXJTbmFwc2hvdHMoe1xuICAgICAgREJDbHVzdGVyU25hcHNob3RJZGVudGlmaWVyOiBpZGVudGlmaWVyLFxuICAgIH0pLnByb21pc2UoKTtcbiAgICByZXR1cm4gZGF0YS5EQkNsdXN0ZXJTbmFwc2hvdHM/LlswXS5TdGF0dXM7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGlmIChlcnIuY29kZSA9PT0gJ0RCQ2x1c3RlclNuYXBzaG90Tm90Rm91bmRGYXVsdCcpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHRocm93IGVycjtcbiAgfVxufVxuIl19 \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.ts b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.ts deleted file mode 100644 index 6d5a3c23336cd..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable no-console */ -import type { IsCompleteRequest, IsCompleteResponse, OnEventRequest, OnEventResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types'; -import { RDS } from 'aws-sdk'; // eslint-disable-line import/no-extraneous-dependencies - -export async function onEventHandler(event: OnEventRequest): Promise { - console.log('Event: %j', event); - - const rds = new RDS(); - - const physicalResourceId = `${event.ResourceProperties.DBClusterIdentifier}-${event.ResourceProperties.DBClusterIdentifier}`; - - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - const data = await rds.createDBClusterSnapshot({ - DBClusterIdentifier: event.ResourceProperties.DBClusterIdentifier, - DBClusterSnapshotIdentifier: event.ResourceProperties.DBClusterSnapshotIdentifier, - }).promise(); - return { - PhysicalResourceId: physicalResourceId, - Data: { - DBClusterSnapshotArn: data.DBClusterSnapshot?.DBClusterSnapshotArn, - }, - }; - } - - if (event.RequestType === 'Delete') { - await rds.deleteDBClusterSnapshot({ - DBClusterSnapshotIdentifier: event.ResourceProperties.DBClusterSnapshotIdentifier, - }).promise(); - } - - return { - PhysicalResourceId: `${event.ResourceProperties.DBClusterIdentifier}-${event.ResourceProperties.DBClusterIdentifier}`, - }; -} - -export async function isCompleteHandler(event: IsCompleteRequest): Promise { - console.log('Event: %j', event); - - const snapshotStatus = await tryGetClusterSnapshotStatus(event.ResourceProperties.DBClusterSnapshotIdentifier); - - switch (event.RequestType) { - case 'Create': - case 'Update': - return { IsComplete: snapshotStatus === 'available' }; - case 'Delete': - return { IsComplete: snapshotStatus === undefined }; - } -} - -async function tryGetClusterSnapshotStatus(identifier: string): Promise { - try { - const rds = new RDS(); - const data = await rds.describeDBClusterSnapshots({ - DBClusterSnapshotIdentifier: identifier, - }).promise(); - return data.DBClusterSnapshots?.[0].Status; - } catch (err) { - if (err.code === 'DBClusterSnapshotNotFoundFault') { - return undefined; - } - throw err; - } -} diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json index af10bb991e428..2e8669a256acc 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.assets.json @@ -1,15 +1,15 @@ { "version": "20.0.0", "files": { - "1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e": { + "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd": { "source": { - "path": "asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e", + "path": "asset.2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e.zip", + "objectKey": "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } @@ -27,7 +27,7 @@ } } }, - "7ae22b0346a792d7afbb4c291d9c2c253dd988bb7b8060120b9dea312ca4126a": { + "d420f64d8c29ddedb314409aadfd9c62f7e400e88517663af54720ace8c3fc84": { "source": { "path": "cdk-integ-cluster-snapshot.template.json", "packaging": "file" @@ -35,7 +35,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "7ae22b0346a792d7afbb4c291d9c2c253dd988bb7b8060120b9dea312ca4126a.json", + "objectKey": "d420f64d8c29ddedb314409aadfd9c62f7e400e88517663af54720ace8c3fc84.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json index 227a1655f18c7..c8a85283c3fcb 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/cdk-integ-cluster-snapshot.template.json @@ -608,7 +608,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D" }, "S3Key": { "Fn::Join": [ @@ -621,7 +621,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -634,7 +634,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -759,7 +759,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D" }, "S3Key": { "Fn::Join": [ @@ -772,7 +772,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -785,7 +785,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -1802,21 +1802,23 @@ }, "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" } - } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" } }, "Parameters": { - "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC": { + "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D": { "Type": "String", - "Description": "S3 bucket for asset \"1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e\"" + "Description": "S3 bucket for asset \"2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd\"" }, - "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D": { + "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE": { "Type": "String", - "Description": "S3 key for asset version \"1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e\"" + "Description": "S3 key for asset version \"2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd\"" }, - "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eArtifactHash725480C4": { + "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddArtifactHashB3DFB88A": { "Type": "String", - "Description": "Artifact hash for asset \"1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e\"" + "Description": "Artifact hash for asset \"2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd\"" }, "AssetParameters8dd02cc4ac473ca5b08800e92edaa31a1a7db4005928021d029c5363584f11b9S3Bucket40DFAF90": { "Type": "String", diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json index 33c85530761b7..6e47bf2f1ccac 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/manifest.json @@ -19,13 +19,13 @@ { "type": "aws:cdk:asset", "data": { - "path": "asset.1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e", - "id": "1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e", + "path": "asset.2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd", + "id": "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd", "packaging": "zip", - "sourceHash": "1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e", - "s3BucketParameter": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC", - "s3KeyParameter": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D", - "artifactHashParameter": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eArtifactHash725480C4" + "sourceHash": "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd", + "s3BucketParameter": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D", + "s3KeyParameter": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE", + "artifactHashParameter": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddArtifactHashB3DFB88A" } }, { @@ -323,22 +323,22 @@ "data": "SnapshoterSnapshotAA1755BE" } ], - "/cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/S3Bucket": [ + "/cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC" + "data": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D" } ], - "/cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/S3VersionKey": [ + "/cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "data": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ], - "/cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/ArtifactHash": [ + "/cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eArtifactHash725480C4" + "data": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddArtifactHashB3DFB88A" } ], "/cdk-integ-cluster-snapshot/AssetParameters/8dd02cc4ac473ca5b08800e92edaa31a1a7db4005928021d029c5363584f11b9/S3Bucket": [ diff --git a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json index ae21afb241e51..76e82da222044 100644 --- a/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-rds/test/cluster-snapshot.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "cdk-integ-cluster-snapshot": { @@ -1038,7 +1038,7 @@ "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D" }, "s3Key": { "Fn::Join": [ @@ -1051,7 +1051,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -1064,7 +1064,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -1239,7 +1239,7 @@ "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3BucketB5E782AC" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3BucketDCD8B62D" }, "s3Key": { "Fn::Join": [ @@ -1252,7 +1252,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -1265,7 +1265,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2eS3VersionKey5DD1F95D" + "Ref": "AssetParameters2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aaddS3VersionKey96F91EAE" } ] } @@ -2174,7 +2174,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } } }, @@ -2204,20 +2204,20 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "AssetParameters": { "id": "AssetParameters", "path": "cdk-integ-cluster-snapshot/AssetParameters", "children": { - "1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e": { - "id": "1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e", - "path": "cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e", + "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd": { + "id": "2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd", + "path": "cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/S3Bucket", + "path": "cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd/S3Bucket", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -2225,7 +2225,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/S3VersionKey", + "path": "cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd/S3VersionKey", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -2233,7 +2233,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "cdk-integ-cluster-snapshot/AssetParameters/1e025324752b3133dc230c4b8b8752f666b63c09cd4aa605ec2b322cc40def2e/ArtifactHash", + "path": "cdk-integ-cluster-snapshot/AssetParameters/2e7ee01d9005281c0784e709cad69500591734343d1cb95da2fb4a3f5076aadd/ArtifactHash", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -2242,7 +2242,7 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "8dd02cc4ac473ca5b08800e92edaa31a1a7db4005928021d029c5363584f11b9": { @@ -2276,13 +2276,13 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "FromSnapshot": { diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.d.ts b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.d.ts deleted file mode 100644 index 9bbf5854684b6..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context): Promise; diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.js b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.js deleted file mode 100644 index 5292af72a643d..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.js +++ /dev/null @@ -1,176 +0,0 @@ -"use strict"; -/* eslint-disable no-console */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.handler = void 0; -// eslint-disable-next-line import/no-extraneous-dependencies -const AWS = require("aws-sdk"); -/** - * Creates a log group and doesn't throw if it exists. - * - * @param logGroupName the name of the log group to create. - * @param region to create the log group in - * @param options CloudWatch API SDK options. - */ -async function createLogGroupSafe(logGroupName, region, options) { - var _a; - // If we set the log retention for a lambda, then due to the async nature of - // Lambda logging there could be a race condition when the same log group is - // already being created by the lambda execution. This can sometime result in - // an error "OperationAbortedException: A conflicting operation is currently - // in progress...Please try again." - // To avoid an error, we do as requested and try again. - let retryCount = (options === null || options === void 0 ? void 0 : options.maxRetries) == undefined ? 10 : options.maxRetries; - const delay = ((_a = options === null || options === void 0 ? void 0 : options.retryOptions) === null || _a === void 0 ? void 0 : _a.base) == undefined ? 10 : options.retryOptions.base; - do { - try { - const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options }); - await cloudwatchlogs.createLogGroup({ logGroupName }).promise(); - return; - } - catch (error) { - if (error.code === 'ResourceAlreadyExistsException') { - // The log group is already created by the lambda execution - return; - } - if (error.code === 'OperationAbortedException') { - if (retryCount > 0) { - retryCount--; - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } - else { - // The log group is still being created by another execution but we are out of retries - throw new Error('Out of attempts to create a logGroup'); - } - } - throw error; - } - } while (true); // exit happens on retry count check -} -/** - * Puts or deletes a retention policy on a log group. - * - * @param logGroupName the name of the log group to create - * @param region the region of the log group - * @param options CloudWatch API SDK options. - * @param retentionInDays the number of days to retain the log events in the specified log group. - */ -async function setRetentionPolicy(logGroupName, region, options, retentionInDays) { - var _a; - // The same as in createLogGroupSafe(), here we could end up with the race - // condition where a log group is either already being created or its retention - // policy is being updated. This would result in an OperationAbortedException, - // which we will try to catch and retry the command a number of times before failing - let retryCount = (options === null || options === void 0 ? void 0 : options.maxRetries) == undefined ? 10 : options.maxRetries; - const delay = ((_a = options === null || options === void 0 ? void 0 : options.retryOptions) === null || _a === void 0 ? void 0 : _a.base) == undefined ? 10 : options.retryOptions.base; - do { - try { - const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options }); - if (!retentionInDays) { - await cloudwatchlogs.deleteRetentionPolicy({ logGroupName }).promise(); - } - else { - await cloudwatchlogs.putRetentionPolicy({ logGroupName, retentionInDays }).promise(); - } - return; - } - catch (error) { - if (error.code === 'OperationAbortedException') { - if (retryCount > 0) { - retryCount--; - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } - else { - // The log group is still being created by another execution but we are out of retries - throw new Error('Out of attempts to create a logGroup'); - } - } - throw error; - } - } while (true); // exit happens on retry count check -} -async function handler(event, context) { - try { - console.log(JSON.stringify(event)); - // The target log group - const logGroupName = event.ResourceProperties.LogGroupName; - // The region of the target log group - const logGroupRegion = event.ResourceProperties.LogGroupRegion; - // Parse to AWS SDK retry options - const retryOptions = parseRetryOptions(event.ResourceProperties.SdkRetry); - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - // Act on the target log group - await createLogGroupSafe(logGroupName, logGroupRegion, retryOptions); - await setRetentionPolicy(logGroupName, logGroupRegion, retryOptions, parseInt(event.ResourceProperties.RetentionInDays, 10)); - if (event.RequestType === 'Create') { - // Set a retention policy of 1 day on the logs of this very function. - // Due to the async nature of the log group creation, the log group for this function might - // still be not created yet at this point. Therefore we attempt to create it. - // In case it is being created, createLogGroupSafe will handle the conflict. - const region = process.env.AWS_REGION; - await createLogGroupSafe(`/aws/lambda/${context.functionName}`, region, retryOptions); - // If createLogGroupSafe fails, the log group is not created even after multiple attempts. - // In this case we have nothing to set the retention policy on but an exception will skip - // the next line. - await setRetentionPolicy(`/aws/lambda/${context.functionName}`, region, retryOptions, 1); - } - } - await respond('SUCCESS', 'OK', logGroupName); - } - catch (e) { - console.log(e); - await respond('FAILED', e.message, event.ResourceProperties.LogGroupName); - } - function respond(responseStatus, reason, physicalResourceId) { - const responseBody = JSON.stringify({ - Status: responseStatus, - Reason: reason, - PhysicalResourceId: physicalResourceId, - StackId: event.StackId, - RequestId: event.RequestId, - LogicalResourceId: event.LogicalResourceId, - Data: { - // Add log group name as part of the response so that it's available via Fn::GetAtt - LogGroupName: event.ResourceProperties.LogGroupName, - }, - }); - console.log('Responding', responseBody); - // eslint-disable-next-line @typescript-eslint/no-require-imports - const parsedUrl = require('url').parse(event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - return new Promise((resolve, reject) => { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const request = require('https').request(requestOptions, resolve); - request.on('error', reject); - request.write(responseBody); - request.end(); - } - catch (e) { - reject(e); - } - }); - } - function parseRetryOptions(rawOptions) { - const retryOptions = {}; - if (rawOptions) { - if (rawOptions.maxRetries) { - retryOptions.maxRetries = parseInt(rawOptions.maxRetries, 10); - } - if (rawOptions.base) { - retryOptions.retryOptions = { - base: parseInt(rawOptions.base, 10), - }; - } - } - return retryOptions; - } -} -exports.handler = handler; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0JBQStCOzs7QUFFL0IsNkRBQTZEO0FBQzdELCtCQUErQjtBQVMvQjs7Ozs7O0dBTUc7QUFDSCxLQUFLLFVBQVUsa0JBQWtCLENBQUMsWUFBb0IsRUFBRSxNQUFlLEVBQUUsT0FBeUI7O0lBQ2hHLDRFQUE0RTtJQUM1RSw0RUFBNEU7SUFDNUUsNkVBQTZFO0lBQzdFLDRFQUE0RTtJQUM1RSxtQ0FBbUM7SUFDbkMsdURBQXVEO0lBQ3ZELElBQUksVUFBVSxHQUFHLENBQUEsT0FBTyxhQUFQLE9BQU8sdUJBQVAsT0FBTyxDQUFFLFVBQVUsS0FBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztJQUM1RSxNQUFNLEtBQUssR0FBRyxPQUFBLE9BQU8sYUFBUCxPQUFPLHVCQUFQLE9BQU8sQ0FBRSxZQUFZLDBDQUFFLElBQUksS0FBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUM7SUFDeEYsR0FBRztRQUNELElBQUk7WUFDRixNQUFNLGNBQWMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxjQUFjLENBQUMsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDaEcsTUFBTSxjQUFjLENBQUMsY0FBYyxDQUFDLEVBQUUsWUFBWSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNoRSxPQUFPO1NBQ1I7UUFBQyxPQUFPLEtBQUssRUFBRTtZQUNkLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxnQ0FBZ0MsRUFBRTtnQkFDbkQsMkRBQTJEO2dCQUMzRCxPQUFPO2FBQ1I7WUFDRCxJQUFJLEtBQUssQ0FBQyxJQUFJLEtBQUssMkJBQTJCLEVBQUU7Z0JBQzlDLElBQUksVUFBVSxHQUFHLENBQUMsRUFBRTtvQkFDbEIsVUFBVSxFQUFFLENBQUM7b0JBQ2IsTUFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztvQkFDekQsU0FBUztpQkFDVjtxQkFBTTtvQkFDTCxzRkFBc0Y7b0JBQ3RGLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQztpQkFDekQ7YUFDRjtZQUNELE1BQU0sS0FBSyxDQUFDO1NBQ2I7S0FDRixRQUFRLElBQUksRUFBRSxDQUFDLG9DQUFvQztBQUN0RCxDQUFDO0FBRUQ7Ozs7Ozs7R0FPRztBQUNILEtBQUssVUFBVSxrQkFBa0IsQ0FBQyxZQUFvQixFQUFFLE1BQWUsRUFBRSxPQUF5QixFQUFFLGVBQXdCOztJQUMxSCwwRUFBMEU7SUFDMUUsK0VBQStFO0lBQy9FLDhFQUE4RTtJQUM5RSxvRkFBb0Y7SUFDcEYsSUFBSSxVQUFVLEdBQUcsQ0FBQSxPQUFPLGFBQVAsT0FBTyx1QkFBUCxPQUFPLENBQUUsVUFBVSxLQUFJLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDO0lBQzVFLE1BQU0sS0FBSyxHQUFHLE9BQUEsT0FBTyxhQUFQLE9BQU8sdUJBQVAsT0FBTyxDQUFFLFlBQVksMENBQUUsSUFBSSxLQUFJLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQztJQUN4RixHQUFHO1FBQ0QsSUFBSTtZQUNGLE1BQU0sY0FBYyxHQUFHLElBQUksR0FBRyxDQUFDLGNBQWMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEdBQUcsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUNoRyxJQUFJLENBQUMsZUFBZSxFQUFFO2dCQUNwQixNQUFNLGNBQWMsQ0FBQyxxQkFBcUIsQ0FBQyxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7YUFDeEU7aUJBQU07Z0JBQ0wsTUFBTSxjQUFjLENBQUMsa0JBQWtCLENBQUMsRUFBRSxZQUFZLEVBQUUsZUFBZSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQzthQUN0RjtZQUNELE9BQU87U0FFUjtRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ2QsSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLDJCQUEyQixFQUFFO2dCQUM5QyxJQUFJLFVBQVUsR0FBRyxDQUFDLEVBQUU7b0JBQ2xCLFVBQVUsRUFBRSxDQUFDO29CQUNiLE1BQU0sSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7b0JBQ3pELFNBQVM7aUJBQ1Y7cUJBQU07b0JBQ0wsc0ZBQXNGO29CQUN0RixNQUFNLElBQUksS0FBSyxDQUFDLHNDQUFzQyxDQUFDLENBQUM7aUJBQ3pEO2FBQ0Y7WUFDRCxNQUFNLEtBQUssQ0FBQztTQUNiO0tBQ0YsUUFBUSxJQUFJLEVBQUUsQ0FBQyxvQ0FBb0M7QUFDdEQsQ0FBQztBQUVNLEtBQUssVUFBVSxPQUFPLENBQUMsS0FBa0QsRUFBRSxPQUEwQjtJQUMxRyxJQUFJO1FBQ0YsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFFbkMsdUJBQXVCO1FBQ3ZCLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLENBQUM7UUFFM0QscUNBQXFDO1FBQ3JDLE1BQU0sY0FBYyxHQUFHLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxjQUFjLENBQUM7UUFFL0QsaUNBQWlDO1FBQ2pDLE1BQU0sWUFBWSxHQUFHLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUUxRSxJQUFJLEtBQUssQ0FBQyxXQUFXLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxXQUFXLEtBQUssUUFBUSxFQUFFO1lBQ3BFLDhCQUE4QjtZQUM5QixNQUFNLGtCQUFrQixDQUFDLFlBQVksRUFBRSxjQUFjLEVBQUUsWUFBWSxDQUFDLENBQUM7WUFDckUsTUFBTSxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsY0FBYyxFQUFFLFlBQVksRUFBRSxRQUFRLENBQUMsS0FBSyxDQUFDLGtCQUFrQixDQUFDLGVBQWUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBRTdILElBQUksS0FBSyxDQUFDLFdBQVcsS0FBSyxRQUFRLEVBQUU7Z0JBQ2xDLHFFQUFxRTtnQkFDckUsMkZBQTJGO2dCQUMzRiw2RUFBNkU7Z0JBQzdFLDRFQUE0RTtnQkFDNUUsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUM7Z0JBQ3RDLE1BQU0sa0JBQWtCLENBQUMsZUFBZSxPQUFPLENBQUMsWUFBWSxFQUFFLEVBQUUsTUFBTSxFQUFFLFlBQVksQ0FBQyxDQUFDO2dCQUN0RiwwRkFBMEY7Z0JBQzFGLHlGQUF5RjtnQkFDekYsaUJBQWlCO2dCQUNqQixNQUFNLGtCQUFrQixDQUFDLGVBQWUsT0FBTyxDQUFDLFlBQVksRUFBRSxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDLENBQUM7YUFDMUY7U0FDRjtRQUVELE1BQU0sT0FBTyxDQUFDLFNBQVMsRUFBRSxJQUFJLEVBQUUsWUFBWSxDQUFDLENBQUM7S0FDOUM7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFZixNQUFNLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsa0JBQWtCLENBQUMsWUFBWSxDQUFDLENBQUM7S0FDM0U7SUFFRCxTQUFTLE9BQU8sQ0FBQyxjQUFzQixFQUFFLE1BQWMsRUFBRSxrQkFBMEI7UUFDakYsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztZQUNsQyxNQUFNLEVBQUUsY0FBYztZQUN0QixNQUFNLEVBQUUsTUFBTTtZQUNkLGtCQUFrQixFQUFFLGtCQUFrQjtZQUN0QyxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87WUFDdEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO1lBQzFCLGlCQUFpQixFQUFFLEtBQUssQ0FBQyxpQkFBaUI7WUFDMUMsSUFBSSxFQUFFO2dCQUNKLG1GQUFtRjtnQkFDbkYsWUFBWSxFQUFFLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZO2FBQ3BEO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFFeEMsaUVBQWlFO1FBQ2pFLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzFELE1BQU0sY0FBYyxHQUFHO1lBQ3JCLFFBQVEsRUFBRSxTQUFTLENBQUMsUUFBUTtZQUM1QixJQUFJLEVBQUUsU0FBUyxDQUFDLElBQUk7WUFDcEIsTUFBTSxFQUFFLEtBQUs7WUFDYixPQUFPLEVBQUUsRUFBRSxjQUFjLEVBQUUsRUFBRSxFQUFFLGdCQUFnQixFQUFFLFlBQVksQ0FBQyxNQUFNLEVBQUU7U0FDdkUsQ0FBQztRQUVGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7WUFDckMsSUFBSTtnQkFDRixpRUFBaUU7Z0JBQ2pFLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUMsY0FBYyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2dCQUNsRSxPQUFPLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztnQkFDNUIsT0FBTyxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsQ0FBQztnQkFDNUIsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO2FBQ2Y7WUFBQyxPQUFPLENBQUMsRUFBRTtnQkFDVixNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDWDtRQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELFNBQVMsaUJBQWlCLENBQUMsVUFBZTtRQUN4QyxNQUFNLFlBQVksR0FBb0IsRUFBRSxDQUFDO1FBQ3pDLElBQUksVUFBVSxFQUFFO1lBQ2QsSUFBSSxVQUFVLENBQUMsVUFBVSxFQUFFO2dCQUN6QixZQUFZLENBQUMsVUFBVSxHQUFHLFFBQVEsQ0FBQyxVQUFVLENBQUMsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDO2FBQy9EO1lBQ0QsSUFBSSxVQUFVLENBQUMsSUFBSSxFQUFFO2dCQUNuQixZQUFZLENBQUMsWUFBWSxHQUFHO29CQUMxQixJQUFJLEVBQUUsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO2lCQUNwQyxDQUFDO2FBQ0g7U0FDRjtRQUNELE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7QUFDSCxDQUFDO0FBM0ZELDBCQTJGQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIG5vLWNvbnNvbGUgKi9cblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuaW1wb3J0ICogYXMgQVdTIGZyb20gJ2F3cy1zZGsnO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuaW1wb3J0IHR5cGUgeyBSZXRyeURlbGF5T3B0aW9ucyB9IGZyb20gJ2F3cy1zZGsvbGliL2NvbmZpZy1iYXNlJztcblxuaW50ZXJmYWNlIFNka1JldHJ5T3B0aW9ucyB7XG4gIG1heFJldHJpZXM/OiBudW1iZXI7XG4gIHJldHJ5T3B0aW9ucz86IFJldHJ5RGVsYXlPcHRpb25zO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBsb2cgZ3JvdXAgYW5kIGRvZXNuJ3QgdGhyb3cgaWYgaXQgZXhpc3RzLlxuICpcbiAqIEBwYXJhbSBsb2dHcm91cE5hbWUgdGhlIG5hbWUgb2YgdGhlIGxvZyBncm91cCB0byBjcmVhdGUuXG4gKiBAcGFyYW0gcmVnaW9uIHRvIGNyZWF0ZSB0aGUgbG9nIGdyb3VwIGluXG4gKiBAcGFyYW0gb3B0aW9ucyBDbG91ZFdhdGNoIEFQSSBTREsgb3B0aW9ucy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY3JlYXRlTG9nR3JvdXBTYWZlKGxvZ0dyb3VwTmFtZTogc3RyaW5nLCByZWdpb24/OiBzdHJpbmcsIG9wdGlvbnM/OiBTZGtSZXRyeU9wdGlvbnMpIHtcbiAgLy8gSWYgd2Ugc2V0IHRoZSBsb2cgcmV0ZW50aW9uIGZvciBhIGxhbWJkYSwgdGhlbiBkdWUgdG8gdGhlIGFzeW5jIG5hdHVyZSBvZlxuICAvLyBMYW1iZGEgbG9nZ2luZyB0aGVyZSBjb3VsZCBiZSBhIHJhY2UgY29uZGl0aW9uIHdoZW4gdGhlIHNhbWUgbG9nIGdyb3VwIGlzXG4gIC8vIGFscmVhZHkgYmVpbmcgY3JlYXRlZCBieSB0aGUgbGFtYmRhIGV4ZWN1dGlvbi4gVGhpcyBjYW4gc29tZXRpbWUgcmVzdWx0IGluXG4gIC8vIGFuIGVycm9yIFwiT3BlcmF0aW9uQWJvcnRlZEV4Y2VwdGlvbjogQSBjb25mbGljdGluZyBvcGVyYXRpb24gaXMgY3VycmVudGx5XG4gIC8vIGluIHByb2dyZXNzLi4uUGxlYXNlIHRyeSBhZ2Fpbi5cIlxuICAvLyBUbyBhdm9pZCBhbiBlcnJvciwgd2UgZG8gYXMgcmVxdWVzdGVkIGFuZCB0cnkgYWdhaW4uXG4gIGxldCByZXRyeUNvdW50ID0gb3B0aW9ucz8ubWF4UmV0cmllcyA9PSB1bmRlZmluZWQgPyAxMCA6IG9wdGlvbnMubWF4UmV0cmllcztcbiAgY29uc3QgZGVsYXkgPSBvcHRpb25zPy5yZXRyeU9wdGlvbnM/LmJhc2UgPT0gdW5kZWZpbmVkID8gMTAgOiBvcHRpb25zLnJldHJ5T3B0aW9ucy5iYXNlO1xuICBkbyB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNsb3Vkd2F0Y2hsb2dzID0gbmV3IEFXUy5DbG91ZFdhdGNoTG9ncyh7IGFwaVZlcnNpb246ICcyMDE0LTAzLTI4JywgcmVnaW9uLCAuLi5vcHRpb25zIH0pO1xuICAgICAgYXdhaXQgY2xvdWR3YXRjaGxvZ3MuY3JlYXRlTG9nR3JvdXAoeyBsb2dHcm91cE5hbWUgfSkucHJvbWlzZSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoZXJyb3IuY29kZSA9PT0gJ1Jlc291cmNlQWxyZWFkeUV4aXN0c0V4Y2VwdGlvbicpIHtcbiAgICAgICAgLy8gVGhlIGxvZyBncm91cCBpcyBhbHJlYWR5IGNyZWF0ZWQgYnkgdGhlIGxhbWJkYSBleGVjdXRpb25cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKGVycm9yLmNvZGUgPT09ICdPcGVyYXRpb25BYm9ydGVkRXhjZXB0aW9uJykge1xuICAgICAgICBpZiAocmV0cnlDb3VudCA+IDApIHtcbiAgICAgICAgICByZXRyeUNvdW50LS07XG4gICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIGRlbGF5KSk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gVGhlIGxvZyBncm91cCBpcyBzdGlsbCBiZWluZyBjcmVhdGVkIGJ5IGFub3RoZXIgZXhlY3V0aW9uIGJ1dCB3ZSBhcmUgb3V0IG9mIHJldHJpZXNcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ091dCBvZiBhdHRlbXB0cyB0byBjcmVhdGUgYSBsb2dHcm91cCcpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH0gd2hpbGUgKHRydWUpOyAvLyBleGl0IGhhcHBlbnMgb24gcmV0cnkgY291bnQgY2hlY2tcbn1cblxuLyoqXG4gKiBQdXRzIG9yIGRlbGV0ZXMgYSByZXRlbnRpb24gcG9saWN5IG9uIGEgbG9nIGdyb3VwLlxuICpcbiAqIEBwYXJhbSBsb2dHcm91cE5hbWUgdGhlIG5hbWUgb2YgdGhlIGxvZyBncm91cCB0byBjcmVhdGVcbiAqIEBwYXJhbSByZWdpb24gdGhlIHJlZ2lvbiBvZiB0aGUgbG9nIGdyb3VwXG4gKiBAcGFyYW0gb3B0aW9ucyBDbG91ZFdhdGNoIEFQSSBTREsgb3B0aW9ucy5cbiAqIEBwYXJhbSByZXRlbnRpb25JbkRheXMgdGhlIG51bWJlciBvZiBkYXlzIHRvIHJldGFpbiB0aGUgbG9nIGV2ZW50cyBpbiB0aGUgc3BlY2lmaWVkIGxvZyBncm91cC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc2V0UmV0ZW50aW9uUG9saWN5KGxvZ0dyb3VwTmFtZTogc3RyaW5nLCByZWdpb24/OiBzdHJpbmcsIG9wdGlvbnM/OiBTZGtSZXRyeU9wdGlvbnMsIHJldGVudGlvbkluRGF5cz86IG51bWJlcikge1xuICAvLyBUaGUgc2FtZSBhcyBpbiBjcmVhdGVMb2dHcm91cFNhZmUoKSwgaGVyZSB3ZSBjb3VsZCBlbmQgdXAgd2l0aCB0aGUgcmFjZVxuICAvLyBjb25kaXRpb24gd2hlcmUgYSBsb2cgZ3JvdXAgaXMgZWl0aGVyIGFscmVhZHkgYmVpbmcgY3JlYXRlZCBvciBpdHMgcmV0ZW50aW9uXG4gIC8vIHBvbGljeSBpcyBiZWluZyB1cGRhdGVkLiBUaGlzIHdvdWxkIHJlc3VsdCBpbiBhbiBPcGVyYXRpb25BYm9ydGVkRXhjZXB0aW9uLFxuICAvLyB3aGljaCB3ZSB3aWxsIHRyeSB0byBjYXRjaCBhbmQgcmV0cnkgdGhlIGNvbW1hbmQgYSBudW1iZXIgb2YgdGltZXMgYmVmb3JlIGZhaWxpbmdcbiAgbGV0IHJldHJ5Q291bnQgPSBvcHRpb25zPy5tYXhSZXRyaWVzID09IHVuZGVmaW5lZCA/IDEwIDogb3B0aW9ucy5tYXhSZXRyaWVzO1xuICBjb25zdCBkZWxheSA9IG9wdGlvbnM/LnJldHJ5T3B0aW9ucz8uYmFzZSA9PSB1bmRlZmluZWQgPyAxMCA6IG9wdGlvbnMucmV0cnlPcHRpb25zLmJhc2U7XG4gIGRvIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY2xvdWR3YXRjaGxvZ3MgPSBuZXcgQVdTLkNsb3VkV2F0Y2hMb2dzKHsgYXBpVmVyc2lvbjogJzIwMTQtMDMtMjgnLCByZWdpb24sIC4uLm9wdGlvbnMgfSk7XG4gICAgICBpZiAoIXJldGVudGlvbkluRGF5cykge1xuICAgICAgICBhd2FpdCBjbG91ZHdhdGNobG9ncy5kZWxldGVSZXRlbnRpb25Qb2xpY3koeyBsb2dHcm91cE5hbWUgfSkucHJvbWlzZSgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYXdhaXQgY2xvdWR3YXRjaGxvZ3MucHV0UmV0ZW50aW9uUG9saWN5KHsgbG9nR3JvdXBOYW1lLCByZXRlbnRpb25JbkRheXMgfSkucHJvbWlzZSgpO1xuICAgICAgfVxuICAgICAgcmV0dXJuO1xuXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvci5jb2RlID09PSAnT3BlcmF0aW9uQWJvcnRlZEV4Y2VwdGlvbicpIHtcbiAgICAgICAgaWYgKHJldHJ5Q291bnQgPiAwKSB7XG4gICAgICAgICAgcmV0cnlDb3VudC0tO1xuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCBkZWxheSkpO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIFRoZSBsb2cgZ3JvdXAgaXMgc3RpbGwgYmVpbmcgY3JlYXRlZCBieSBhbm90aGVyIGV4ZWN1dGlvbiBidXQgd2UgYXJlIG91dCBvZiByZXRyaWVzXG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdPdXQgb2YgYXR0ZW1wdHMgdG8gY3JlYXRlIGEgbG9nR3JvdXAnKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9IHdoaWxlICh0cnVlKTsgLy8gZXhpdCBoYXBwZW5zIG9uIHJldHJ5IGNvdW50IGNoZWNrXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVyKGV2ZW50OiBBV1NMYW1iZGEuQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUV2ZW50LCBjb250ZXh0OiBBV1NMYW1iZGEuQ29udGV4dCkge1xuICB0cnkge1xuICAgIGNvbnNvbGUubG9nKEpTT04uc3RyaW5naWZ5KGV2ZW50KSk7XG5cbiAgICAvLyBUaGUgdGFyZ2V0IGxvZyBncm91cFxuICAgIGNvbnN0IGxvZ0dyb3VwTmFtZSA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5Mb2dHcm91cE5hbWU7XG5cbiAgICAvLyBUaGUgcmVnaW9uIG9mIHRoZSB0YXJnZXQgbG9nIGdyb3VwXG4gICAgY29uc3QgbG9nR3JvdXBSZWdpb24gPSBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuTG9nR3JvdXBSZWdpb247XG5cbiAgICAvLyBQYXJzZSB0byBBV1MgU0RLIHJldHJ5IG9wdGlvbnNcbiAgICBjb25zdCByZXRyeU9wdGlvbnMgPSBwYXJzZVJldHJ5T3B0aW9ucyhldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuU2RrUmV0cnkpO1xuXG4gICAgaWYgKGV2ZW50LlJlcXVlc3RUeXBlID09PSAnQ3JlYXRlJyB8fCBldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ1VwZGF0ZScpIHtcbiAgICAgIC8vIEFjdCBvbiB0aGUgdGFyZ2V0IGxvZyBncm91cFxuICAgICAgYXdhaXQgY3JlYXRlTG9nR3JvdXBTYWZlKGxvZ0dyb3VwTmFtZSwgbG9nR3JvdXBSZWdpb24sIHJldHJ5T3B0aW9ucyk7XG4gICAgICBhd2FpdCBzZXRSZXRlbnRpb25Qb2xpY3kobG9nR3JvdXBOYW1lLCBsb2dHcm91cFJlZ2lvbiwgcmV0cnlPcHRpb25zLCBwYXJzZUludChldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuUmV0ZW50aW9uSW5EYXlzLCAxMCkpO1xuXG4gICAgICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdDcmVhdGUnKSB7XG4gICAgICAgIC8vIFNldCBhIHJldGVudGlvbiBwb2xpY3kgb2YgMSBkYXkgb24gdGhlIGxvZ3Mgb2YgdGhpcyB2ZXJ5IGZ1bmN0aW9uLlxuICAgICAgICAvLyBEdWUgdG8gdGhlIGFzeW5jIG5hdHVyZSBvZiB0aGUgbG9nIGdyb3VwIGNyZWF0aW9uLCB0aGUgbG9nIGdyb3VwIGZvciB0aGlzIGZ1bmN0aW9uIG1pZ2h0XG4gICAgICAgIC8vIHN0aWxsIGJlIG5vdCBjcmVhdGVkIHlldCBhdCB0aGlzIHBvaW50LiBUaGVyZWZvcmUgd2UgYXR0ZW1wdCB0byBjcmVhdGUgaXQuXG4gICAgICAgIC8vIEluIGNhc2UgaXQgaXMgYmVpbmcgY3JlYXRlZCwgY3JlYXRlTG9nR3JvdXBTYWZlIHdpbGwgaGFuZGxlIHRoZSBjb25mbGljdC5cbiAgICAgICAgY29uc3QgcmVnaW9uID0gcHJvY2Vzcy5lbnYuQVdTX1JFR0lPTjtcbiAgICAgICAgYXdhaXQgY3JlYXRlTG9nR3JvdXBTYWZlKGAvYXdzL2xhbWJkYS8ke2NvbnRleHQuZnVuY3Rpb25OYW1lfWAsIHJlZ2lvbiwgcmV0cnlPcHRpb25zKTtcbiAgICAgICAgLy8gSWYgY3JlYXRlTG9nR3JvdXBTYWZlIGZhaWxzLCB0aGUgbG9nIGdyb3VwIGlzIG5vdCBjcmVhdGVkIGV2ZW4gYWZ0ZXIgbXVsdGlwbGUgYXR0ZW1wdHMuXG4gICAgICAgIC8vIEluIHRoaXMgY2FzZSB3ZSBoYXZlIG5vdGhpbmcgdG8gc2V0IHRoZSByZXRlbnRpb24gcG9saWN5IG9uIGJ1dCBhbiBleGNlcHRpb24gd2lsbCBza2lwXG4gICAgICAgIC8vIHRoZSBuZXh0IGxpbmUuXG4gICAgICAgIGF3YWl0IHNldFJldGVudGlvblBvbGljeShgL2F3cy9sYW1iZGEvJHtjb250ZXh0LmZ1bmN0aW9uTmFtZX1gLCByZWdpb24sIHJldHJ5T3B0aW9ucywgMSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgYXdhaXQgcmVzcG9uZCgnU1VDQ0VTUycsICdPSycsIGxvZ0dyb3VwTmFtZSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBjb25zb2xlLmxvZyhlKTtcblxuICAgIGF3YWl0IHJlc3BvbmQoJ0ZBSUxFRCcsIGUubWVzc2FnZSwgZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkxvZ0dyb3VwTmFtZSk7XG4gIH1cblxuICBmdW5jdGlvbiByZXNwb25kKHJlc3BvbnNlU3RhdHVzOiBzdHJpbmcsIHJlYXNvbjogc3RyaW5nLCBwaHlzaWNhbFJlc291cmNlSWQ6IHN0cmluZykge1xuICAgIGNvbnN0IHJlc3BvbnNlQm9keSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIFN0YXR1czogcmVzcG9uc2VTdGF0dXMsXG4gICAgICBSZWFzb246IHJlYXNvbixcbiAgICAgIFBoeXNpY2FsUmVzb3VyY2VJZDogcGh5c2ljYWxSZXNvdXJjZUlkLFxuICAgICAgU3RhY2tJZDogZXZlbnQuU3RhY2tJZCxcbiAgICAgIFJlcXVlc3RJZDogZXZlbnQuUmVxdWVzdElkLFxuICAgICAgTG9naWNhbFJlc291cmNlSWQ6IGV2ZW50LkxvZ2ljYWxSZXNvdXJjZUlkLFxuICAgICAgRGF0YToge1xuICAgICAgICAvLyBBZGQgbG9nIGdyb3VwIG5hbWUgYXMgcGFydCBvZiB0aGUgcmVzcG9uc2Ugc28gdGhhdCBpdCdzIGF2YWlsYWJsZSB2aWEgRm46OkdldEF0dFxuICAgICAgICBMb2dHcm91cE5hbWU6IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5Mb2dHcm91cE5hbWUsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgY29uc29sZS5sb2coJ1Jlc3BvbmRpbmcnLCByZXNwb25zZUJvZHkpO1xuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCBwYXJzZWRVcmwgPSByZXF1aXJlKCd1cmwnKS5wYXJzZShldmVudC5SZXNwb25zZVVSTCk7XG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSB7XG4gICAgICBob3N0bmFtZTogcGFyc2VkVXJsLmhvc3RuYW1lLFxuICAgICAgcGF0aDogcGFyc2VkVXJsLnBhdGgsXG4gICAgICBtZXRob2Q6ICdQVVQnLFxuICAgICAgaGVhZGVyczogeyAnY29udGVudC10eXBlJzogJycsICdjb250ZW50LWxlbmd0aCc6IHJlc3BvbnNlQm9keS5sZW5ndGggfSxcbiAgICB9O1xuXG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgICAgIGNvbnN0IHJlcXVlc3QgPSByZXF1aXJlKCdodHRwcycpLnJlcXVlc3QocmVxdWVzdE9wdGlvbnMsIHJlc29sdmUpO1xuICAgICAgICByZXF1ZXN0Lm9uKCdlcnJvcicsIHJlamVjdCk7XG4gICAgICAgIHJlcXVlc3Qud3JpdGUocmVzcG9uc2VCb2R5KTtcbiAgICAgICAgcmVxdWVzdC5lbmQoKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmVqZWN0KGUpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgZnVuY3Rpb24gcGFyc2VSZXRyeU9wdGlvbnMocmF3T3B0aW9uczogYW55KTogU2RrUmV0cnlPcHRpb25zIHtcbiAgICBjb25zdCByZXRyeU9wdGlvbnM6IFNka1JldHJ5T3B0aW9ucyA9IHt9O1xuICAgIGlmIChyYXdPcHRpb25zKSB7XG4gICAgICBpZiAocmF3T3B0aW9ucy5tYXhSZXRyaWVzKSB7XG4gICAgICAgIHJldHJ5T3B0aW9ucy5tYXhSZXRyaWVzID0gcGFyc2VJbnQocmF3T3B0aW9ucy5tYXhSZXRyaWVzLCAxMCk7XG4gICAgICB9XG4gICAgICBpZiAocmF3T3B0aW9ucy5iYXNlKSB7XG4gICAgICAgIHJldHJ5T3B0aW9ucy5yZXRyeU9wdGlvbnMgPSB7XG4gICAgICAgICAgYmFzZTogcGFyc2VJbnQocmF3T3B0aW9ucy5iYXNlLCAxMCksXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXRyeU9wdGlvbnM7XG4gIH1cbn1cbiJdfQ== \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.ts b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.ts deleted file mode 100644 index d2c14e5a72cc7..0000000000000 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/index.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* eslint-disable no-console */ - -// eslint-disable-next-line import/no-extraneous-dependencies -import * as AWS from 'aws-sdk'; -// eslint-disable-next-line import/no-extraneous-dependencies -import type { RetryDelayOptions } from 'aws-sdk/lib/config-base'; - -interface SdkRetryOptions { - maxRetries?: number; - retryOptions?: RetryDelayOptions; -} - -/** - * Creates a log group and doesn't throw if it exists. - * - * @param logGroupName the name of the log group to create. - * @param region to create the log group in - * @param options CloudWatch API SDK options. - */ -async function createLogGroupSafe(logGroupName: string, region?: string, options?: SdkRetryOptions) { - // If we set the log retention for a lambda, then due to the async nature of - // Lambda logging there could be a race condition when the same log group is - // already being created by the lambda execution. This can sometime result in - // an error "OperationAbortedException: A conflicting operation is currently - // in progress...Please try again." - // To avoid an error, we do as requested and try again. - let retryCount = options?.maxRetries == undefined ? 10 : options.maxRetries; - const delay = options?.retryOptions?.base == undefined ? 10 : options.retryOptions.base; - do { - try { - const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options }); - await cloudwatchlogs.createLogGroup({ logGroupName }).promise(); - return; - } catch (error) { - if (error.code === 'ResourceAlreadyExistsException') { - // The log group is already created by the lambda execution - return; - } - if (error.code === 'OperationAbortedException') { - if (retryCount > 0) { - retryCount--; - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } else { - // The log group is still being created by another execution but we are out of retries - throw new Error('Out of attempts to create a logGroup'); - } - } - throw error; - } - } while (true); // exit happens on retry count check -} - -/** - * Puts or deletes a retention policy on a log group. - * - * @param logGroupName the name of the log group to create - * @param region the region of the log group - * @param options CloudWatch API SDK options. - * @param retentionInDays the number of days to retain the log events in the specified log group. - */ -async function setRetentionPolicy(logGroupName: string, region?: string, options?: SdkRetryOptions, retentionInDays?: number) { - // The same as in createLogGroupSafe(), here we could end up with the race - // condition where a log group is either already being created or its retention - // policy is being updated. This would result in an OperationAbortedException, - // which we will try to catch and retry the command a number of times before failing - let retryCount = options?.maxRetries == undefined ? 10 : options.maxRetries; - const delay = options?.retryOptions?.base == undefined ? 10 : options.retryOptions.base; - do { - try { - const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options }); - if (!retentionInDays) { - await cloudwatchlogs.deleteRetentionPolicy({ logGroupName }).promise(); - } else { - await cloudwatchlogs.putRetentionPolicy({ logGroupName, retentionInDays }).promise(); - } - return; - - } catch (error) { - if (error.code === 'OperationAbortedException') { - if (retryCount > 0) { - retryCount--; - await new Promise(resolve => setTimeout(resolve, delay)); - continue; - } else { - // The log group is still being created by another execution but we are out of retries - throw new Error('Out of attempts to create a logGroup'); - } - } - throw error; - } - } while (true); // exit happens on retry count check -} - -export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context) { - try { - console.log(JSON.stringify(event)); - - // The target log group - const logGroupName = event.ResourceProperties.LogGroupName; - - // The region of the target log group - const logGroupRegion = event.ResourceProperties.LogGroupRegion; - - // Parse to AWS SDK retry options - const retryOptions = parseRetryOptions(event.ResourceProperties.SdkRetry); - - if (event.RequestType === 'Create' || event.RequestType === 'Update') { - // Act on the target log group - await createLogGroupSafe(logGroupName, logGroupRegion, retryOptions); - await setRetentionPolicy(logGroupName, logGroupRegion, retryOptions, parseInt(event.ResourceProperties.RetentionInDays, 10)); - - if (event.RequestType === 'Create') { - // Set a retention policy of 1 day on the logs of this very function. - // Due to the async nature of the log group creation, the log group for this function might - // still be not created yet at this point. Therefore we attempt to create it. - // In case it is being created, createLogGroupSafe will handle the conflict. - const region = process.env.AWS_REGION; - await createLogGroupSafe(`/aws/lambda/${context.functionName}`, region, retryOptions); - // If createLogGroupSafe fails, the log group is not created even after multiple attempts. - // In this case we have nothing to set the retention policy on but an exception will skip - // the next line. - await setRetentionPolicy(`/aws/lambda/${context.functionName}`, region, retryOptions, 1); - } - } - - await respond('SUCCESS', 'OK', logGroupName); - } catch (e) { - console.log(e); - - await respond('FAILED', e.message, event.ResourceProperties.LogGroupName); - } - - function respond(responseStatus: string, reason: string, physicalResourceId: string) { - const responseBody = JSON.stringify({ - Status: responseStatus, - Reason: reason, - PhysicalResourceId: physicalResourceId, - StackId: event.StackId, - RequestId: event.RequestId, - LogicalResourceId: event.LogicalResourceId, - Data: { - // Add log group name as part of the response so that it's available via Fn::GetAtt - LogGroupName: event.ResourceProperties.LogGroupName, - }, - }); - - console.log('Responding', responseBody); - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const parsedUrl = require('url').parse(event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: 'PUT', - headers: { 'content-type': '', 'content-length': responseBody.length }, - }; - - return new Promise((resolve, reject) => { - try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const request = require('https').request(requestOptions, resolve); - request.on('error', reject); - request.write(responseBody); - request.end(); - } catch (e) { - reject(e); - } - }); - } - - function parseRetryOptions(rawOptions: any): SdkRetryOptions { - const retryOptions: SdkRetryOptions = {}; - if (rawOptions) { - if (rawOptions.maxRetries) { - retryOptions.maxRetries = parseInt(rawOptions.maxRetries, 10); - } - if (rawOptions.base) { - retryOptions.retryOptions = { - base: parseInt(rawOptions.base, 10), - }; - } - } - return retryOptions; - } -} diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.assets.json b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.assets.json index b5f8e22a7ebee..9f33a88befcd5 100644 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.assets.json +++ b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.assets.json @@ -1,20 +1,20 @@ { "version": "20.0.0", "files": { - "22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665": { + "af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b": { "source": { - "path": "asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665", + "path": "asset.af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b", "packaging": "zip" }, "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665.zip", + "objectKey": "af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b.zip", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } }, - "1090427864de85daf0a7222efa793fde9fe83cadad48a9d8095ee2c67b0f94b5": { + "098893bf824e1d22a1a3bab7a8ac2ff5eab0638bef3f5938212e218d09852aab": { "source": { "path": "aws-cdk-rds-instance.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "1090427864de85daf0a7222efa793fde9fe83cadad48a9d8095ee2c67b0f94b5.json", + "objectKey": "098893bf824e1d22a1a3bab7a8ac2ff5eab0638bef3f5938212e218d09852aab.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.template.json b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.template.json index 3644bc19bdf4f..2103e0fa90ead 100644 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.template.json +++ b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/aws-cdk-rds-instance.template.json @@ -901,7 +901,9 @@ }, "excludeCharacters": " %+~`#$&*()|[]{}:;<>?!'/@\"\\" } - } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" }, "InstanceAvailabilityAD5D452C": { "Type": "AWS::Events::Rule", @@ -1036,7 +1038,7 @@ "Runtime": "nodejs14.x", "Code": { "S3Bucket": { - "Ref": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3Bucket0D8A173B" + "Ref": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3Bucket2E84C3BE" }, "S3Key": { "Fn::Join": [ @@ -1049,7 +1051,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3VersionKeyE95BF332" + "Ref": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3VersionKey91073FC4" } ] } @@ -1062,7 +1064,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3VersionKeyE95BF332" + "Ref": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3VersionKey91073FC4" } ] } @@ -1172,17 +1174,17 @@ } }, "Parameters": { - "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3Bucket0D8A173B": { + "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3Bucket2E84C3BE": { "Type": "String", - "Description": "S3 bucket for asset \"22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665\"" + "Description": "S3 bucket for asset \"af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b\"" }, - "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3VersionKeyE95BF332": { + "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3VersionKey91073FC4": { "Type": "String", - "Description": "S3 key for asset version \"22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665\"" + "Description": "S3 key for asset version \"af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b\"" }, - "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665ArtifactHashF4A1E70E": { + "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bArtifactHash61CB979E": { "Type": "String", - "Description": "Artifact hash for asset \"22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665\"" + "Description": "Artifact hash for asset \"af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/manifest.json index 541d9b21c7b01..b66945eefef43 100644 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/manifest.json @@ -19,13 +19,13 @@ { "type": "aws:cdk:asset", "data": { - "path": "asset.22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665", - "id": "22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665", + "path": "asset.af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b", + "id": "af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b", "packaging": "zip", - "sourceHash": "22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665", - "s3BucketParameter": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3Bucket0D8A173B", - "s3KeyParameter": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3VersionKeyE95BF332", - "artifactHashParameter": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665ArtifactHashF4A1E70E" + "sourceHash": "af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b", + "s3BucketParameter": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3Bucket2E84C3BE", + "s3KeyParameter": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3VersionKey91073FC4", + "artifactHashParameter": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bArtifactHash61CB979E" } } ], @@ -317,22 +317,22 @@ "data": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A" } ], - "/aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/S3Bucket": [ + "/aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3Bucket0D8A173B" + "data": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3Bucket2E84C3BE" } ], - "/aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/S3VersionKey": [ + "/aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665S3VersionKeyE95BF332" + "data": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bS3VersionKey91073FC4" } ], - "/aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/ArtifactHash": [ + "/aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665ArtifactHashF4A1E70E" + "data": "AssetParametersaf4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8bArtifactHash61CB979E" } ], "/aws-cdk-rds-instance/HighCPU/Resource": [ diff --git a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/tree.json b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/tree.json index 91c58543ec637..62b2354454c0c 100644 --- a/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-rds/test/instance.lit.integ.snapshot/tree.json @@ -9,7 +9,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "aws-cdk-rds-instance": { @@ -1645,20 +1645,20 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "AssetParameters": { "id": "AssetParameters", "path": "aws-cdk-rds-instance/AssetParameters", "children": { - "22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665": { - "id": "22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665", - "path": "aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665", + "af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b": { + "id": "af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b", + "path": "aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/S3Bucket", + "path": "aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b/S3Bucket", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -1666,7 +1666,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/S3VersionKey", + "path": "aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b/S3VersionKey", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -1674,7 +1674,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "aws-cdk-rds-instance/AssetParameters/22bb41d703c8e7a9a1712308f455fcf58cc012b0a386c9df563a6244a61e6665/ArtifactHash", + "path": "aws-cdk-rds-instance/AssetParameters/af4ed033ae57b4313cf2e73fe9eb52500f1319be4e9212747877583220481c8b/ArtifactHash", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -1683,13 +1683,13 @@ }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } } }, "constructInfo": { "fqn": "constructs.Construct", - "version": "10.0.9" + "version": "10.1.33" } }, "HighCPU": { diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts index 5c421cf04c5c8..41253801bc88e 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret-rotation.ts @@ -1,7 +1,7 @@ import * as ec2 from '@aws-cdk/aws-ec2'; import * as lambda from '@aws-cdk/aws-lambda'; import * as serverless from '@aws-cdk/aws-sam'; -import { Duration, Names, Stack, Token, CfnMapping, Aws } from '@aws-cdk/core'; +import { Duration, Names, Stack, Token, CfnMapping, Aws, RemovalPolicy } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { ISecret } from './secret'; @@ -329,6 +329,7 @@ export class SecretRotation extends Construct { }, parameters, }); + application.applyRemovalPolicy(RemovalPolicy.DESTROY); // This creates a CF a dependency between the rotation schedule and the // serverless application. This is needed because it's the application diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts index c6c153ce83ae6..81656443c5efe 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret-rotation.test.ts @@ -72,54 +72,58 @@ test('secret rotation single user', () => { GroupDescription: 'Default/SecretRotation/SecurityGroup', }); - Template.fromStack(stack).hasResourceProperties('AWS::Serverless::Application', { - Location: { - ApplicationId: { - 'Fn::FindInMap': ['SecretRotationSARMappingC10A2F5D', { Ref: 'AWS::Partition' }, 'applicationId'], - }, - SemanticVersion: { - 'Fn::FindInMap': ['SecretRotationSARMappingC10A2F5D', { Ref: 'AWS::Partition' }, 'semanticVersion'], + Template.fromStack(stack).hasResource('AWS::Serverless::Application', { + Properties: { + Location: { + ApplicationId: { + 'Fn::FindInMap': ['SecretRotationSARMappingC10A2F5D', { Ref: 'AWS::Partition' }, 'applicationId'], + }, + SemanticVersion: { + 'Fn::FindInMap': ['SecretRotationSARMappingC10A2F5D', { Ref: 'AWS::Partition' }, 'semanticVersion'], + }, }, - }, - Parameters: { - endpoint: { - 'Fn::Join': [ - '', - [ - 'https://secretsmanager.', - { - Ref: 'AWS::Region', - }, - '.', - { - Ref: 'AWS::URLSuffix', - }, + Parameters: { + endpoint: { + 'Fn::Join': [ + '', + [ + 'https://secretsmanager.', + { + Ref: 'AWS::Region', + }, + '.', + { + Ref: 'AWS::URLSuffix', + }, + ], ], - ], - }, - functionName: 'SecretRotation', - excludeCharacters: excludeCharacters, - vpcSecurityGroupIds: { - 'Fn::GetAtt': [ - 'SecretRotationSecurityGroup9985012B', - 'GroupId', - ], - }, - vpcSubnetIds: { - 'Fn::Join': [ - '', - [ - { - Ref: 'VPCPrivateSubnet1Subnet8BCA10E0', - }, - ',', - { - Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A', - }, + }, + functionName: 'SecretRotation', + excludeCharacters: excludeCharacters, + vpcSecurityGroupIds: { + 'Fn::GetAtt': [ + 'SecretRotationSecurityGroup9985012B', + 'GroupId', ], - ], + }, + vpcSubnetIds: { + 'Fn::Join': [ + '', + [ + { + Ref: 'VPCPrivateSubnet1Subnet8BCA10E0', + }, + ',', + { + Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A', + }, + ], + ], + }, }, }, + DeletionPolicy: 'Delete', + UpdateReplacePolicy: 'Delete', }); Template.fromStack(stack).hasResourceProperties('AWS::SecretsManager::ResourcePolicy', { diff --git a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts index 51504f260eb45..a10644fafc247 100644 --- a/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts +++ b/packages/@aws-cdk/cfnspec/build-tools/create-missing-libraries.ts @@ -183,7 +183,7 @@ async function main() { '@aws-cdk/core': version, }, engines: { - node: '>= 10.13.0 <13 || >=13.7.0', + node: '>= 14.15.0', }, stability: 'experimental', maturity: 'cfn-only',