From 85fdeb5f86a75054e6066a0a4e7a7059621c9b1d Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Wed, 19 Aug 2020 14:36:24 +0100 Subject: [PATCH] feat(rds): Validate log types for clusters (#9797) This builds on #9772 to add validation for the types of logs that are available on each cluster type (by engine). Notes: * Currently a draft: I want feedback on this approach before either adding some constants for ease-of-use and porting a similar approach to instances. * This is built off of #9772, so includes the changes from #9772 while it is pending a push/merge. * Added pr-linter/exempt-readme label (for now) as the current change is validation only. Will add README changes if/when this includes some usable constants. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-rds/lib/cluster-engine.ts | 8 +++++++ packages/@aws-cdk/aws-rds/lib/cluster.ts | 21 +++++++++++------ .../aws-rds/test/test.cluster-engine.ts | 10 +++++++- .../@aws-cdk/aws-rds/test/test.cluster.ts | 23 +++++++++++++++++++ 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts index b16a9799d59c0..3b334d4127971 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster-engine.ts @@ -61,6 +61,9 @@ export interface IClusterEngine extends IEngine { /** The application used by this engine to perform rotation for a multi-user scenario. */ readonly multiUserRotationApplication: secretsmanager.SecretRotationApplication; + /** The log types that are available with this engine type */ + readonly supportedLogTypes: string[]; + /** * Method called when the engine is used to create a new cluster. */ @@ -81,6 +84,7 @@ abstract class ClusterEngineBase implements IClusterEngine { public readonly parameterGroupFamily?: string; public readonly singleUserRotationApplication: secretsmanager.SecretRotationApplication; public readonly multiUserRotationApplication: secretsmanager.SecretRotationApplication; + public abstract readonly supportedLogTypes: string[]; private readonly defaultPort?: number; @@ -116,6 +120,8 @@ interface MysqlClusterEngineBaseProps { } abstract class MySqlClusterEngineBase extends ClusterEngineBase { + public readonly supportedLogTypes: string[] = ['error', 'general', 'slowquery', 'audit']; + constructor(props: MysqlClusterEngineBaseProps) { super({ ...props, @@ -408,6 +414,8 @@ export interface AuroraPostgresClusterEngineProps { } class AuroraPostgresClusterEngine extends ClusterEngineBase { + public readonly supportedLogTypes: string[] = ['postgresql']; + constructor(version?: AuroraPostgresEngineVersion) { super({ engineType: 'aurora-postgresql', diff --git a/packages/@aws-cdk/aws-rds/lib/cluster.ts b/packages/@aws-cdk/aws-rds/lib/cluster.ts index 033d04635b26d..d52fff47c476e 100644 --- a/packages/@aws-cdk/aws-rds/lib/cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/cluster.ts @@ -613,13 +613,20 @@ export class DatabaseCluster extends DatabaseClusterBase { } private setLogRetention(props: DatabaseClusterProps) { - if (props.cloudwatchLogsExports && props.cloudwatchLogsRetention) { - for (const log of props.cloudwatchLogsExports) { - new lambda.LogRetention(this, `LogRetention${log}`, { - logGroupName: `/aws/rds/cluster/${this.clusterIdentifier}/${log}`, - retention: props.cloudwatchLogsRetention, - role: props.cloudwatchLogsRetentionRole, - }); + if (props.cloudwatchLogsExports) { + const unsupportedLogTypes = props.cloudwatchLogsExports.filter(logType => !props.engine.supportedLogTypes.includes(logType)); + if (unsupportedLogTypes.length > 0) { + throw new Error(`Unsupported logs for the current engine type: ${unsupportedLogTypes.join(',')}`); + } + + if (props.cloudwatchLogsRetention) { + for (const log of props.cloudwatchLogsExports) { + new lambda.LogRetention(this, `LogRetention${log}`, { + logGroupName: `/aws/rds/cluster/${this.clusterIdentifier}/${log}`, + retention: props.cloudwatchLogsRetention, + role: props.cloudwatchLogsRetentionRole, + }); + } } } } diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts b/packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts index e32db06a40f9b..d5af56f1d10da 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts +++ b/packages/@aws-cdk/aws-rds/test/test.cluster-engine.ts @@ -107,4 +107,12 @@ export = { test.done(); }, -}; + + 'supported log types'(test: Test) { + const mysqlLogTypes = ['error', 'general', 'slowquery', 'audit']; + test.deepEqual(DatabaseClusterEngine.aurora({ version: AuroraEngineVersion.VER_1_22_2 }).supportedLogTypes, mysqlLogTypes); + test.deepEqual(DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_2_08_1 }).supportedLogTypes, mysqlLogTypes); + test.deepEqual(DatabaseClusterEngine.auroraPostgres({ version: AuroraPostgresEngineVersion.VER_9_6_9 }).supportedLogTypes, ['postgresql']); + test.done(); + }, +} diff --git a/packages/@aws-cdk/aws-rds/test/test.cluster.ts b/packages/@aws-cdk/aws-rds/test/test.cluster.ts index 80aa285266bff..105964b73e0c4 100644 --- a/packages/@aws-cdk/aws-rds/test/test.cluster.ts +++ b/packages/@aws-cdk/aws-rds/test/test.cluster.ts @@ -1155,6 +1155,29 @@ export = { test.done(); }, + 'throws if given unsupported CloudWatch log exports'(test: Test) { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + test.throws(() => { + new DatabaseCluster(stack, 'Database', { + engine: DatabaseClusterEngine.AURORA, + masterUser: { + username: 'admin', + password: cdk.SecretValue.plainText('tooshort'), + }, + instanceProps: { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + }, + cloudwatchLogsExports: ['error', 'general', 'slowquery', 'audit', 'thislogdoesnotexist', 'neitherdoesthisone'], + }); + }, /Unsupported logs for the current engine type: thislogdoesnotexist,neitherdoesthisone/); + + test.done(); + }, + 'does not throw (but adds a node error) if a (dummy) VPC does not have sufficient subnets'(test: Test) { // GIVEN const stack = testStack();