-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathutil.ts
171 lines (151 loc) · 6.84 KB
/
util.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import { Construct } from 'constructs';
import * as ec2 from '../../../aws-ec2';
import * as iam from '../../../aws-iam';
import * as s3 from '../../../aws-s3';
import { RemovalPolicy } from '../../../core';
import { ValidationError } from '../../../core/lib/errors';
import { DatabaseSecret } from '../database-secret';
import { IEngine } from '../engine';
import { CommonRotationUserOptions, Credentials, SnapshotCredentials } from '../props';
/**
* The default set of characters we exclude from generated passwords for database users.
* It's a combination of characters that have a tendency to cause problems in shell scripts,
* some engine-specific characters (for example, Oracle doesn't like '@' in its passwords),
* and some that trip up other services, like DMS.
*
* This constant is private to the RDS module.
*/
export const DEFAULT_PASSWORD_EXCLUDE_CHARS = " %+~`#$&*()|[]{}:;<>?!'/@\"\\";
/** Common base of `DatabaseInstanceProps` and `DatabaseClusterBaseProps` that has only the S3 props */
export interface DatabaseS3ImportExportProps {
readonly s3ImportRole?: iam.IRole;
readonly s3ImportBuckets?: s3.IBucket[];
readonly s3ExportRole?: iam.IRole;
readonly s3ExportBuckets?: s3.IBucket[];
}
/**
* Validates the S3 import/export props and returns the import/export roles, if any.
* If `combineRoles` is true, will reuse the import role for export (or vice versa) if possible.
*
* Notably, `combineRoles` is set to true for instances, but false for clusters.
* This is because the `combineRoles` functionality is most applicable to instances and didn't exist
* for the initial clusters implementation. To maintain backwards compatibility, it is set to false for clusters.
*/
export function setupS3ImportExport(
scope: Construct,
props: DatabaseS3ImportExportProps,
combineRoles: boolean): { s3ImportRole?: iam.IRole; s3ExportRole?: iam.IRole } {
let s3ImportRole = props.s3ImportRole;
let s3ExportRole = props.s3ExportRole;
if (props.s3ImportBuckets && props.s3ImportBuckets.length > 0) {
if (props.s3ImportRole) {
throw new ValidationError('Only one of s3ImportRole or s3ImportBuckets must be specified, not both.', scope);
}
s3ImportRole = (combineRoles && s3ExportRole) ? s3ExportRole : new iam.Role(scope, 'S3ImportRole', {
assumedBy: new iam.ServicePrincipal('rds.amazonaws.com'),
});
for (const bucket of props.s3ImportBuckets) {
bucket.grantRead(s3ImportRole);
}
}
if (props.s3ExportBuckets && props.s3ExportBuckets.length > 0) {
if (props.s3ExportRole) {
throw new ValidationError('Only one of s3ExportRole or s3ExportBuckets must be specified, not both.', scope);
}
s3ExportRole = (combineRoles && s3ImportRole) ? s3ImportRole : new iam.Role(scope, 'S3ExportRole', {
assumedBy: new iam.ServicePrincipal('rds.amazonaws.com'),
});
for (const bucket of props.s3ExportBuckets) {
bucket.grantReadWrite(s3ExportRole);
}
}
return { s3ImportRole, s3ExportRole };
}
export function engineDescription(engine: IEngine) {
return engine.engineType + (engine.engineVersion?.fullVersion ? `-${engine.engineVersion.fullVersion}` : '');
}
/**
* By default, deletion protection is disabled.
* Enable if explicitly provided or if the RemovalPolicy has been set to RETAIN
*/
export function defaultDeletionProtection(deletionProtection?: boolean, removalPolicy?: RemovalPolicy): boolean | undefined {
return deletionProtection ?? (removalPolicy === RemovalPolicy.RETAIN ? true : undefined);
}
/**
* Renders the credentials for an instance or cluster
*/
export function renderCredentials(scope: Construct, engine: IEngine, credentials?: Credentials): Credentials {
let renderedCredentials = credentials ?? Credentials.fromUsername(engine.defaultUsername ?? 'admin'); // For backwards compatibilty
if (!renderedCredentials.secret && !renderedCredentials.password) {
renderedCredentials = Credentials.fromSecret(
new DatabaseSecret(scope, 'Secret', {
username: renderedCredentials.username,
secretName: renderedCredentials.secretName,
encryptionKey: renderedCredentials.encryptionKey,
excludeCharacters: renderedCredentials.excludeCharacters,
// if username must be referenced as a string we can safely replace the
// secret when customization options are changed without risking a replacement
replaceOnPasswordCriteriaChanges: credentials?.usernameAsString,
replicaRegions: renderedCredentials.replicaRegions,
}),
// pass username if it must be referenced as a string
credentials?.usernameAsString ? renderedCredentials.username : undefined,
);
}
return renderedCredentials;
}
/**
* Renders the credentials for an instance or cluster using provided snapshot credentials
*/
export function renderSnapshotCredentials(scope: Construct, credentials?: SnapshotCredentials) {
let renderedCredentials = credentials;
let secret = renderedCredentials?.secret;
if (!secret && renderedCredentials?.generatePassword) {
if (!renderedCredentials.username) {
throw new ValidationError('`snapshotCredentials` `username` must be specified when `generatePassword` is set to true', scope);
}
renderedCredentials = SnapshotCredentials.fromSecret(
new DatabaseSecret(scope, 'SnapshotSecret', {
username: renderedCredentials.username,
encryptionKey: renderedCredentials.encryptionKey,
excludeCharacters: renderedCredentials.excludeCharacters,
replaceOnPasswordCriteriaChanges: renderedCredentials.replaceOnPasswordCriteriaChanges,
replicaRegions: renderedCredentials.replicaRegions,
}),
);
}
return renderedCredentials;
}
/**
* The RemovalPolicy that should be applied to a "helper" resource, if the base resource has the given removal policy
*
* - For Clusters, this determines the RemovalPolicy for Instances/SubnetGroups.
* - For Instances, this determines the RemovalPolicy for SubnetGroups.
*
* If the basePolicy is:
*
* DESTROY or SNAPSHOT -> DESTROY (snapshot is good enough to recreate)
* RETAIN -> RETAIN (anything else will lose data or fail to deploy)
* (undefined) -> DESTROY (base policy is assumed to be SNAPSHOT)
*/
export function helperRemovalPolicy(basePolicy?: RemovalPolicy): RemovalPolicy {
return basePolicy === RemovalPolicy.RETAIN
? RemovalPolicy.RETAIN
: RemovalPolicy.DESTROY;
}
/**
* Return a given value unless it's the same as another value
*/
export function renderUnless<A>(value: A, suppressValue: A): A | undefined {
return value === suppressValue ? undefined : value;
}
/**
* Applies defaults for rotation options
*/
export function applyDefaultRotationOptions(options: CommonRotationUserOptions, defaultvpcSubnets?: ec2.SubnetSelection): CommonRotationUserOptions {
return {
excludeCharacters: DEFAULT_PASSWORD_EXCLUDE_CHARS,
vpcSubnets: defaultvpcSubnets,
...options,
};
}