-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathdatabase-query.ts
103 lines (91 loc) · 3.55 KB
/
database-query.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import * as path from 'path';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as cdk from 'aws-cdk-lib/core';
import * as customresources from 'aws-cdk-lib/custom-resources';
import { Construct } from 'constructs';
import { DatabaseQueryHandlerProps } from './handler-props';
import { Cluster } from '../cluster';
import { DatabaseOptions } from '../database-options';
export interface DatabaseQueryProps<HandlerProps> extends DatabaseOptions {
readonly handler: string;
readonly properties: HandlerProps;
/**
* The policy to apply when this resource is removed from the application.
*
* @default cdk.RemovalPolicy.Destroy
*/
readonly removalPolicy?: cdk.RemovalPolicy;
}
export class DatabaseQuery<HandlerProps> extends Construct implements iam.IGrantable {
readonly grantPrincipal: iam.IPrincipal;
readonly ref: string;
private readonly resource: cdk.CustomResource;
constructor(scope: Construct, id: string, props: DatabaseQueryProps<HandlerProps>) {
super(scope, id);
const adminUser = this.getAdminUser(props);
const handler = new lambda.SingletonFunction(this, 'Handler', {
code: lambda.Code.fromAsset(path.join(__dirname, 'database-query-provider'), {
exclude: ['*.ts'],
}),
runtime: lambda.determineLatestNodeRuntime(this),
handler: 'index.handler',
timeout: cdk.Duration.minutes(1),
uuid: '3de5bea7-27da-4796-8662-5efb56431b5f',
lambdaPurpose: 'Query Redshift Database',
});
handler.addToRolePolicy(new iam.PolicyStatement({
actions: ['redshift-data:DescribeStatement', 'redshift-data:ExecuteStatement'],
resources: ['*'],
}));
adminUser.grantRead(handler);
const provider = new customresources.Provider(this, 'Provider', {
onEventHandler: handler,
});
const queryHandlerProps: DatabaseQueryHandlerProps & HandlerProps = {
handler: props.handler,
clusterName: props.cluster.clusterName,
adminUserArn: adminUser.secretArn,
databaseName: props.databaseName,
...props.properties,
};
this.resource = new cdk.CustomResource(this, 'Resource', {
resourceType: 'Custom::RedshiftDatabaseQuery',
serviceToken: provider.serviceToken,
removalPolicy: props.removalPolicy,
properties: queryHandlerProps,
});
this.grantPrincipal = handler.grantPrincipal;
this.ref = this.resource.ref;
}
public applyRemovalPolicy(policy: cdk.RemovalPolicy): void {
this.resource.applyRemovalPolicy(policy);
}
public getAtt(attributeName: string): cdk.Reference {
return this.resource.getAtt(attributeName);
}
public getAttString(attributeName: string): string {
return this.resource.getAttString(attributeName);
}
private getAdminUser(props: DatabaseOptions): secretsmanager.ISecret {
const cluster = props.cluster;
let adminUser = props.adminUser;
if (!adminUser) {
if (cluster instanceof Cluster) {
if (cluster.secret) {
adminUser = cluster.secret;
} else {
throw new Error(
'Administrative access to the Redshift cluster is required but an admin user secret was not provided and the cluster did not generate admin user credentials (they were provided explicitly)',
);
}
} else {
throw new Error(
'Administrative access to the Redshift cluster is required but an admin user secret was not provided and the cluster was imported',
);
}
}
return adminUser;
}
}