Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(redshift): Tables can include comments #23847

Merged
merged 5 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions packages/@aws-cdk/aws-redshift/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ import * as ec2 from '@aws-cdk/aws-ec2';
import * as s3 from '@aws-cdk/aws-s3';

const vpc = new ec2.Vpc(this, 'Vpc');
const bucket = s3.Bucket.fromBucketName(stack, 'bucket', 'logging-bucket');
const bucket = s3.Bucket.fromBucketName(this, 'bucket', 'logging-bucket');

const cluster = new Cluster(this, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
loggingProperties: {
loggingBucket = bucket,
loggingBucket: bucket,
loggingKeyPrefix: 'prefix',
}
});
Expand Down Expand Up @@ -200,6 +200,20 @@ new Table(this, 'Table', {
});
```

Tables can also be configured with a comment:

```ts fixture=cluster
new Table(this, 'Table', {
tableColumns: [
{ name: 'col1', dataType: 'varchar(4)' },
{ name: 'col2', dataType: 'float' }
],
cluster: cluster,
databaseName: 'databaseName',
comment: 'This is a comment',
});
```

### Granting Privileges

You can give a user privileges to perform certain actions on a table by using the
Expand Down Expand Up @@ -305,7 +319,9 @@ cluster.addRotationMultiUser('MultiUserRotation', {
You can add a parameter to a parameter group with`ClusterParameterGroup.addParameter()`.

```ts
const params = new ClusterParameterGroup(stack, 'Params', {
import { ClusterParameterGroup } from '@aws-cdk/aws-redshift';

const params = new ClusterParameterGroup(this, 'Params', {
description: 'desc',
parameters: {
require_ssl: 'true',
Expand All @@ -318,6 +334,8 @@ params.addParameter('enable_user_activity_logging', 'true');
Additionally, you can add a parameter to the cluster's associated parameter group with `Cluster.addToParameterGroup()`. If the cluster does not have an associated parameter group, a new parameter group is created.

```ts
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from '@aws-cdk/core';
declare const vpc: ec2.Vpc;

const cluster = new Cluster(this, 'Cluster', {
Expand All @@ -336,9 +354,11 @@ cluster.addToParameterGroup('enable_user_activity_logging', 'true');
If you configure your cluster to be publicly accessible, you can optionally select an *elastic IP address* to use for the external IP address. An elastic IP address is a static IP address that is associated with your AWS account. You can use an elastic IP address to connect to your cluster from outside the VPC. An elastic IP address gives you the ability to change your underlying configuration without affecting the IP address that clients use to connect to your cluster. This approach can be helpful for situations such as recovery after a failure.

```ts
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from '@aws-cdk/core';
declare const vpc: ec2.Vpc;

new Cluster(stack, 'Redshift', {
new Cluster(this, 'Redshift', {
masterUser: {
masterUsername: 'admin',
masterPassword: cdk.SecretValue.unsafePlainText('tooshort'),
Expand All @@ -352,6 +372,7 @@ new Cluster(stack, 'Redshift', {
If the Cluster is in a VPC and you want to connect to it using the private IP address from within the cluster, it is important to enable *DNS resolution* and *DNS hostnames* in the VPC config. If these parameters would not be set, connections from within the VPC would connect to the elastic IP address and not the private IP address.

```ts
import * as ec2 from '@aws-cdk/aws-ec2';
const vpc = new ec2.Vpc(this, 'VPC', {
enableDnsSupport: true,
enableDnsHostnames: true,
Expand All @@ -373,9 +394,11 @@ In some cases, you might want to associate the cluster with an elastic IP addres
When you use Amazon Redshift enhanced VPC routing, Amazon Redshift forces all COPY and UNLOAD traffic between your cluster and your data repositories through your virtual private cloud (VPC) based on the Amazon VPC service. By using enhanced VPC routing, you can use standard VPC features, such as VPC security groups, network access control lists (ACLs), VPC endpoints, VPC endpoint policies, internet gateways, and Domain Name System (DNS) servers, as described in the Amazon VPC User Guide. You use these features to tightly manage the flow of data between your Amazon Redshift cluster and other resources. When you use enhanced VPC routing to route traffic through your VPC, you can also use VPC flow logs to monitor COPY and UNLOAD traffic.

```ts
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from '@aws-cdk/core';
declare const vpc: ec2.Vpc;

new Cluster(stack, 'Redshift', {
new Cluster(this, 'Redshift', {
masterUser: {
masterUsername: 'admin',
masterPassword: cdk.SecretValue.unsafePlainText('tooshort'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ async function createTable(
}

await executeStatement(statement, tableAndClusterProps);

if (tableAndClusterProps.tableComment) {
await executeStatement(`COMMENT ON TABLE ${tableName} IS '${tableAndClusterProps.tableComment}'`, tableAndClusterProps);
}

return tableName;
}

Expand Down Expand Up @@ -143,6 +148,12 @@ async function updateTable(
}
}

const oldComment = oldResourceProperties.tableComment;
const newComment = tableAndClusterProps.tableComment;
if (oldComment !== newComment) {
alterationStatements.push(`COMMENT ON TABLE ${tableName} IS ${newComment ? `'${newComment}'` : 'NULL'}`);
}

await Promise.all(alterationStatements.map(statement => executeStatement(statement, tableAndClusterProps)));

return tableName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface TableHandlerProps {
readonly tableColumns: Column[];
readonly distStyle?: TableDistStyle;
readonly sortStyle: TableSortStyle;
readonly tableComment?: string;
}

export interface TablePrivilege {
Expand Down
8 changes: 8 additions & 0 deletions packages/@aws-cdk/aws-redshift/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ export interface TableProps extends DatabaseOptions {
* @default cdk.RemovalPolicy.Retain
*/
readonly removalPolicy?: cdk.RemovalPolicy;

/**
* A comment to attach to the table.
*
* @default - no comment
*/
readonly tableComment?: string;
}

/**
Expand Down Expand Up @@ -234,6 +241,7 @@ export class Table extends TableBase {
tableColumns: this.tableColumns,
distStyle: props.distStyle,
sortStyle: props.sortStyle ?? this.getDefaultSortStyle(props.tableColumns),
tableComment: props.tableComment,
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,20 @@ describe('create', () => {
Sql: `CREATE TABLE ${tableNamePrefix}${requestIdTruncated} (col1 varchar(4),col2 float,col3 float) DISTSTYLE KEY DISTKEY(col1) COMPOUND SORTKEY(col2,col3)`,
}));
});

test('serializes table comment in statement', async () => {
const event = baseEvent;
const newResourceProperties: ResourcePropertiesType = {
...resourceProperties,
tableComment: 'table comment',
};

await manageTable(newResourceProperties, event);

expect(mockExecuteStatement).toHaveBeenCalledWith(expect.objectContaining({
Sql: `COMMENT ON TABLE ${tableNamePrefix}${requestIdTruncated} IS 'table comment'`,
}));
});
});

describe('delete', () => {
Expand Down Expand Up @@ -502,4 +516,40 @@ describe('update', () => {
});
});

describe('table comment', () => {
test('does not replace if comment added on table', async () => {
const newComment = 'newComment';
const newResourceProperties = {
...resourceProperties,
tableComment: newComment,
};

await expect(manageTable(newResourceProperties, event)).resolves.toMatchObject({
PhysicalResourceId: physicalResourceId,
});
expect(mockExecuteStatement).toHaveBeenCalledWith(expect.objectContaining({
Sql: `COMMENT ON TABLE ${physicalResourceId} IS '${newComment}'`,
}));
});

test('does not replace if comment removed on table', async () => {
const newEvent = {
...event,
OldResourceProperties: {
...event.OldResourceProperties,
tableComment: 'oldComment',
},
};
const newResourceProperties = {
...resourceProperties,
};

await expect(manageTable(newResourceProperties, newEvent)).resolves.toMatchObject({
PhysicalResourceId: physicalResourceId,
});
expect(mockExecuteStatement).toHaveBeenCalledWith(expect.objectContaining({
Sql: `COMMENT ON TABLE ${physicalResourceId} IS NULL`,
}));
});
});
});

This file was deleted.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "22.0.0",
"version": "29.0.0",
"files": {
"8359857aa9b7c3fbc8bba9a505282ba848915383c4549f21b9f93f9f35b56415": {
"ab58b1384030fef0fc8663c06f6fd62196fb3ae8807ab82e4559967d3b885b08": {
"source": {
"path": "asset.8359857aa9b7c3fbc8bba9a505282ba848915383c4549f21b9f93f9f35b56415",
"path": "asset.ab58b1384030fef0fc8663c06f6fd62196fb3ae8807ab82e4559967d3b885b08",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "8359857aa9b7c3fbc8bba9a505282ba848915383c4549f21b9f93f9f35b56415.zip",
"objectKey": "ab58b1384030fef0fc8663c06f6fd62196fb3ae8807ab82e4559967d3b885b08.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand All @@ -27,15 +27,15 @@
}
}
},
"fadca82b7c081a8b6de68f952878e92ba5610dce63ccbead865e1b854073bff0": {
"fd9bc22f4d8ca7fabbbe054d374117616ffa6a2393152ecb529d2b385432d259": {
"source": {
"path": "aws-cdk-redshift-cluster-database.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "fadca82b7c081a8b6de68f952878e92ba5610dce63ccbead865e1b854073bff0.json",
"objectKey": "fd9bc22f4d8ca7fabbbe054d374117616ffa6a2393152ecb529d2b385432d259.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
"S3Key": "8359857aa9b7c3fbc8bba9a505282ba848915383c4549f21b9f93f9f35b56415.zip"
"S3Key": "ab58b1384030fef0fc8663c06f6fd62196fb3ae8807ab82e4559967d3b885b08.zip"
},
"Role": {
"Fn::GetAtt": [
Expand Down Expand Up @@ -1176,7 +1176,8 @@
}
],
"distStyle": "KEY",
"sortStyle": "INTERLEAVED"
"sortStyle": "INTERLEAVED",
"tableComment": "A test table"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"22.0.0"}
{"version":"29.0.0"}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "22.0.0",
"version": "29.0.0",
"testCases": {
"redshift-cluster-database-integ/DefaultTest": {
"stacks": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "22.0.0",
"version": "29.0.0",
"artifacts": {
"aws-cdk-redshift-cluster-database.assets": {
"type": "cdk:asset-manifest",
Expand All @@ -17,7 +17,7 @@
"validateOnSynth": false,
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/fadca82b7c081a8b6de68f952878e92ba5610dce63ccbead865e1b854073bff0.json",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/fd9bc22f4d8ca7fabbbe054d374117616ffa6a2393152ecb529d2b385432d259.json",
"requiresBootstrapStackVersion": 6,
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
"additionalDependencies": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "22.0.0",
"version": "29.0.0",
"files": {
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
"source": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,7 @@
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.182"
"version": "10.1.209"
}
},
"TablePrivileges": {
Expand Down Expand Up @@ -1470,13 +1470,13 @@
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.182"
"version": "10.1.209"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.182"
"version": "10.1.209"
}
}
},
Expand Down Expand Up @@ -1639,7 +1639,7 @@
"s3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
"s3Key": "8359857aa9b7c3fbc8bba9a505282ba848915383c4549f21b9f93f9f35b56415.zip"
"s3Key": "ab58b1384030fef0fc8663c06f6fd62196fb3ae8807ab82e4559967d3b885b08.zip"
},
"role": {
"Fn::GetAtt": [
Expand Down Expand Up @@ -1902,7 +1902,7 @@
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.182"
"version": "10.1.209"
}
}
},
Expand Down Expand Up @@ -1946,7 +1946,7 @@
"path": "redshift-cluster-database-integ/DefaultTest/Default",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.182"
"version": "10.1.209"
}
},
"DeployAssert": {
Expand Down Expand Up @@ -1992,7 +1992,7 @@
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.182"
"version": "10.1.209"
}
}
},
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-redshift/test/integ.database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const table = new redshift.Table(stack, 'Table', {
],
distStyle: redshift.TableDistStyle.KEY,
sortStyle: redshift.TableSortStyle.INTERLEAVED,
tableComment: 'A test table',
});
table.grant(user, redshift.TableAction.INSERT, redshift.TableAction.DELETE);

Expand Down