Skip to content

Commit

Permalink
fix(dynamodb): replicas not created on table replacement
Browse files Browse the repository at this point in the history
Closes aws#12332
  • Loading branch information
jogold committed Feb 26, 2021
1 parent d331afe commit e1662ce
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 33 deletions.
33 changes: 26 additions & 7 deletions packages/@aws-cdk/aws-dynamodb/lib/replica-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ import { DynamoDB } from 'aws-sdk'; // eslint-disable-line import/no-extraneous-
export async function onEventHandler(event: OnEventRequest): Promise<OnEventResponse> {
console.log('Event: %j', event);

/**
* Process only Create and Delete requests. We shouldn't receive any
* update request and in case we do there is nothing to update.
*/
if (event.RequestType === 'Create' || event.RequestType === 'Delete') {
const updateTableAction = getUpdateTableAction(event);

if (updateTableAction) {
const dynamodb = new DynamoDB();

const data = await dynamodb.updateTable({
TableName: event.ResourceProperties.TableName,
ReplicaUpdates: [
{
[event.RequestType]: {
[updateTableAction]: {
RegionName: event.ResourceProperties.Region,
},
},
Expand All @@ -25,7 +23,7 @@ export async function onEventHandler(event: OnEventRequest): Promise<OnEventResp
console.log('Update table: %j', data);
}

return { PhysicalResourceId: event.ResourceProperties.Region };
return { PhysicalResourceId: `${event.ResourceProperties.TableName}-${event.ResourceProperties.Region}` };
}

export async function isCompleteHandler(event: IsCompleteRequest): Promise<IsCompleteResponse> {
Expand Down Expand Up @@ -53,3 +51,24 @@ export async function isCompleteHandler(event: IsCompleteRequest): Promise<IsCom
return { IsComplete: tableActive && regionReplica === undefined };
}
}

function getUpdateTableAction(event: OnEventRequest): 'Create' | 'Update' | 'Delete' | undefined {
switch (event.RequestType) {
case 'Create':
return 'Create';
case 'Update':
// If it's a table replacement, create a replica in the "new" table
if (event.OldResourceProperties?.TableName !== event.ResourceProperties.TableName) {
return 'Create';
}
return;
case 'Delete':
// Process only deletes that have the new physical resource id format. This
// is to prevent replica deletion when switching from the old format (region)
// to the new format (table-region).
if (event.PhysicalResourceId === `${event.ResourceProperties.TableName}-${event.ResourceProperties.Region}`) {
return 'Delete';
}
return;
}
}
13 changes: 10 additions & 3 deletions packages/@aws-cdk/aws-dynamodb/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1670,12 +1670,19 @@ interface ScalableAttributePair {
*/
class SourceTableAttachedPolicy extends CoreConstruct implements iam.IGrantable {
public readonly grantPrincipal: iam.IPrincipal;
public readonly policy: iam.IPolicy;
public readonly policy: iam.IManagedPolicy;

public constructor(sourceTable: Table, role: iam.IRole) {
super(sourceTable, `SourceTableAttachedPolicy-${Names.nodeUniqueId(role.node)}`);

const policy = new iam.Policy(this, 'Resource', { roles: [role] });
const policy = new iam.ManagedPolicy(this, 'Resource', {
// A CF update of the description property of a managed policy requires
// a replacement. Use the table name in the description to force a managed
// policy replacement when the table name changes. This way we preserve permissions
// to delete old replicas in case of a table replacement.
description: `DynamoDB replication managed policy for table ${sourceTable.tableName}`,
roles: [role],
});
this.policy = policy;
this.grantPrincipal = new SourceTableAttachedPrincipal(role, policy);
}
Expand All @@ -1686,7 +1693,7 @@ class SourceTableAttachedPolicy extends CoreConstruct implements iam.IGrantable
* `SourceTableAttachedPolicy` class so it can act as an `IGrantable`.
*/
class SourceTableAttachedPrincipal extends iam.PrincipalBase {
public constructor(private readonly role: iam.IRole, private readonly policy: iam.Policy) {
public constructor(private readonly role: iam.IRole, private readonly policy: iam.ManagedPolicy) {
super();
}

Expand Down
68 changes: 45 additions & 23 deletions packages/@aws-cdk/aws-dynamodb/test/integ.global.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"DeletionPolicy": "Delete"
},
"TableSourceTableAttachedPolicycdkdynamodbglobal20191121awscdkawsdynamodbReplicaProviderOnEventHandlerServiceRole6F43DF4AA4E210EA": {
"Type": "AWS::IAM::Policy",
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Statement": [
Expand Down Expand Up @@ -119,7 +119,18 @@
],
"Version": "2012-10-17"
},
"PolicyName": "TableSourceTableAttachedPolicycdkdynamodbglobal20191121awscdkawsdynamodbReplicaProviderOnEventHandlerServiceRole6F43DF4AA4E210EA",
"Description": {
"Fn::Join": [
"",
[
"DynamoDB replication managed policy for table ",
{
"Ref": "TableCD117FA1"
}
]
]
},
"Path": "/",
"Roles": [
{
"Fn::GetAtt": [
Expand All @@ -131,7 +142,7 @@
}
},
"TableSourceTableAttachedPolicycdkdynamodbglobal20191121awscdkawsdynamodbReplicaProviderIsCompleteHandlerServiceRole397161288F61AAFA": {
"Type": "AWS::IAM::Policy",
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Statement": [
Expand Down Expand Up @@ -164,7 +175,18 @@
],
"Version": "2012-10-17"
},
"PolicyName": "leSourceTableAttachedPolicycdkdynamodbglobal20191121awscdkawsdynamodbReplicaProviderIsCompleteHandlerServiceRole397161288F61AAFA",
"Description": {
"Fn::Join": [
"",
[
"DynamoDB replication managed policy for table ",
{
"Ref": "TableCD117FA1"
}
]
]
},
"Path": "/",
"Roles": [
{
"Fn::GetAtt": [
Expand Down Expand Up @@ -231,7 +253,7 @@
},
"/",
{
"Ref": "AssetParametersf8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429ceaS3Bucket434BDB62"
"Ref": "AssetParameters816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4S3BucketD60A604E"
},
"/",
{
Expand All @@ -241,7 +263,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParametersf8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429ceaS3VersionKey01638790"
"Ref": "AssetParameters816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4S3VersionKey3B6CF32E"
}
]
}
Expand All @@ -254,7 +276,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParametersf8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429ceaS3VersionKey01638790"
"Ref": "AssetParameters816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4S3VersionKey3B6CF32E"
}
]
}
Expand All @@ -264,11 +286,11 @@
]
},
"Parameters": {
"referencetocdkdynamodbglobal20191121AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714S3Bucket71E24D5BRef": {
"Ref": "AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714S3Bucket1C6779E0"
"referencetocdkdynamodbglobal20191121AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dS3BucketEC84E854Ref": {
"Ref": "AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dS3Bucket6074C90D"
},
"referencetocdkdynamodbglobal20191121AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714S3VersionKeyD88E8BACRef": {
"Ref": "AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714S3VersionKey5C1D9275"
"referencetocdkdynamodbglobal20191121AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dS3VersionKey3A697E08Ref": {
"Ref": "AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dS3VersionKeyD46C7C64"
},
"referencetocdkdynamodbglobal20191121AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketC7F3A147Ref": {
"Ref": "AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketDC4B98B1"
Expand All @@ -283,17 +305,17 @@
}
},
"Parameters": {
"AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714S3Bucket1C6779E0": {
"AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dS3Bucket6074C90D": {
"Type": "String",
"Description": "S3 bucket for asset \"f13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714\""
"Description": "S3 bucket for asset \"64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23d\""
},
"AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714S3VersionKey5C1D9275": {
"AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dS3VersionKeyD46C7C64": {
"Type": "String",
"Description": "S3 key for asset version \"f13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714\""
"Description": "S3 key for asset version \"64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23d\""
},
"AssetParametersf13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714ArtifactHash477AAEA7": {
"AssetParameters64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23dArtifactHash47790F8C": {
"Type": "String",
"Description": "Artifact hash for asset \"f13d472270faaa08099009152a8848a0e7434b14773f3c3f94acca6f6c3ae714\""
"Description": "Artifact hash for asset \"64d932156f2fe2344df8ba133f8f77d38885adfe7c269abff78ee2b7f313a23d\""
},
"AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketDC4B98B1": {
"Type": "String",
Expand All @@ -307,17 +329,17 @@
"Type": "String",
"Description": "Artifact hash for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\""
},
"AssetParametersf8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429ceaS3Bucket434BDB62": {
"AssetParameters816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4S3BucketD60A604E": {
"Type": "String",
"Description": "S3 bucket for asset \"f8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429cea\""
"Description": "S3 bucket for asset \"816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4\""
},
"AssetParametersf8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429ceaS3VersionKey01638790": {
"AssetParameters816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4S3VersionKey3B6CF32E": {
"Type": "String",
"Description": "S3 key for asset version \"f8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429cea\""
"Description": "S3 key for asset version \"816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4\""
},
"AssetParametersf8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429ceaArtifactHashD0E61C22": {
"AssetParameters816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4ArtifactHashB77AE3D0": {
"Type": "String",
"Description": "Artifact hash for asset \"f8cfc24954f0c95960d9a93888c01bf5e95802f26bfa5dc6fde5c913a1429cea\""
"Description": "Artifact hash for asset \"816e041d692b4f0f5961b7f877ade2b5c7fca4174f48dc2d73054d56ab26ceb4\""
}
}
}

0 comments on commit e1662ce

Please sign in to comment.