|
| 1 | +import * as path from 'path'; |
1 | 2 | import * as ec2 from '@aws-cdk/aws-ec2';
|
2 | 3 | import * as iam from '@aws-cdk/aws-iam';
|
3 | 4 | import * as kms from '@aws-cdk/aws-kms';
|
| 5 | +import * as lambda from '@aws-cdk/aws-lambda'; |
4 | 6 | import * as s3 from '@aws-cdk/aws-s3';
|
5 | 7 | import * as secretsmanager from '@aws-cdk/aws-secretsmanager';
|
6 |
| -import { Duration, IResource, Lazy, RemovalPolicy, Resource, SecretValue, Token } from '@aws-cdk/core'; |
7 |
| -import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources'; |
| 8 | +import { ArnFormat, CustomResource, Duration, IResource, Lazy, RemovalPolicy, Resource, SecretValue, Stack, Token } from '@aws-cdk/core'; |
| 9 | +import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId, Provider } from '@aws-cdk/custom-resources'; |
8 | 10 | import { Construct } from 'constructs';
|
9 | 11 | import { DatabaseSecret } from './database-secret';
|
10 | 12 | import { Endpoint } from './endpoint';
|
11 | 13 | import { ClusterParameterGroup, IClusterParameterGroup } from './parameter-group';
|
12 | 14 | import { CfnCluster } from './redshift.generated';
|
13 | 15 | import { ClusterSubnetGroup, IClusterSubnetGroup } from './subnet-group';
|
14 |
| - |
15 | 16 | /**
|
16 | 17 | * Possible Node Types to use in the cluster
|
17 | 18 | * used for defining `ClusterProps.nodeType`.
|
@@ -364,6 +365,12 @@ export interface ClusterProps {
|
364 | 365 | */
|
365 | 366 | readonly elasticIp?: string
|
366 | 367 |
|
| 368 | + /** |
| 369 | + * If this flag is set, the cluster will be rebooted when changes to the cluster's parameter group that require a restart to apply. |
| 370 | + * @default false |
| 371 | + */ |
| 372 | + readonly rebootForParameterChanges?: boolean |
| 373 | + |
367 | 374 | /**
|
368 | 375 | * If this flag is set, Amazon Redshift forces all COPY and UNLOAD traffic between your cluster and your data repositories through your virtual private cloud (VPC).
|
369 | 376 | *
|
@@ -592,7 +599,9 @@ export class Cluster extends ClusterBase {
|
592 | 599 |
|
593 | 600 | const defaultPort = ec2.Port.tcp(this.clusterEndpoint.port);
|
594 | 601 | this.connections = new ec2.Connections({ securityGroups, defaultPort });
|
595 |
| - |
| 602 | + if (props.rebootForParameterChanges) { |
| 603 | + this.enableRebootForParameterChanges(); |
| 604 | + } |
596 | 605 | // Add default role if specified and also available in the roles list
|
597 | 606 | if (props.defaultRole) {
|
598 | 607 | if (props.roles?.some(x => x === props.defaultRole)) {
|
@@ -689,6 +698,71 @@ export class Cluster extends ClusterBase {
|
689 | 698 | }
|
690 | 699 | }
|
691 | 700 |
|
| 701 | + /** |
| 702 | + * Enables automatic cluster rebooting when changes to the cluster's parameter group require a restart to apply. |
| 703 | + */ |
| 704 | + public enableRebootForParameterChanges(): void { |
| 705 | + if (this.node.tryFindChild('RedshiftClusterRebooterCustomResource')) { |
| 706 | + return; |
| 707 | + } |
| 708 | + const rebootFunction = new lambda.SingletonFunction(this, 'RedshiftClusterRebooterFunction', { |
| 709 | + uuid: '511e207f-13df-4b8b-b632-c32b30b65ac2', |
| 710 | + runtime: lambda.Runtime.NODEJS_16_X, |
| 711 | + code: lambda.Code.fromAsset(path.join(__dirname, 'cluster-parameter-change-reboot-handler')), |
| 712 | + handler: 'index.handler', |
| 713 | + timeout: Duration.seconds(900), |
| 714 | + }); |
| 715 | + rebootFunction.addToRolePolicy(new iam.PolicyStatement({ |
| 716 | + actions: ['redshift:DescribeClusters'], |
| 717 | + resources: ['*'], |
| 718 | + })); |
| 719 | + rebootFunction.addToRolePolicy(new iam.PolicyStatement({ |
| 720 | + actions: ['redshift:RebootCluster'], |
| 721 | + resources: [ |
| 722 | + Stack.of(this).formatArn({ |
| 723 | + service: 'redshift', |
| 724 | + resource: 'cluster', |
| 725 | + resourceName: this.clusterName, |
| 726 | + arnFormat: ArnFormat.COLON_RESOURCE_NAME, |
| 727 | + }), |
| 728 | + ], |
| 729 | + })); |
| 730 | + const provider = new Provider(this, 'ResourceProvider', { |
| 731 | + onEventHandler: rebootFunction, |
| 732 | + }); |
| 733 | + const customResource = new CustomResource(this, 'RedshiftClusterRebooterCustomResource', { |
| 734 | + resourceType: 'Custom::RedshiftClusterRebooter', |
| 735 | + serviceToken: provider.serviceToken, |
| 736 | + properties: { |
| 737 | + ClusterId: this.clusterName, |
| 738 | + ParameterGroupName: Lazy.string({ |
| 739 | + produce: () => { |
| 740 | + if (!this.parameterGroup) { |
| 741 | + throw new Error('Cannot enable reboot for parameter changes when there is no associated ClusterParameterGroup.'); |
| 742 | + } |
| 743 | + return this.parameterGroup.clusterParameterGroupName; |
| 744 | + }, |
| 745 | + }), |
| 746 | + ParametersString: Lazy.string({ |
| 747 | + produce: () => { |
| 748 | + if (!(this.parameterGroup instanceof ClusterParameterGroup)) { |
| 749 | + throw new Error('Cannot enable reboot for parameter changes when using an imported parameter group.'); |
| 750 | + } |
| 751 | + return JSON.stringify(this.parameterGroup.parameters); |
| 752 | + }, |
| 753 | + }), |
| 754 | + }, |
| 755 | + }); |
| 756 | + Lazy.any({ |
| 757 | + produce: () => { |
| 758 | + if (!this.parameterGroup) { |
| 759 | + throw new Error('Cannot enable reboot for parameter changes when there is no associated ClusterParameterGroup.'); |
| 760 | + } |
| 761 | + customResource.node.addDependency(this, this.parameterGroup); |
| 762 | + }, |
| 763 | + }); |
| 764 | + } |
| 765 | + |
692 | 766 | /**
|
693 | 767 | * Adds default IAM role to cluster. The default IAM role must be already associated to the cluster to be added as the default role.
|
694 | 768 | *
|
|
0 commit comments