From d1ae1573641dd7888d897b10909608dd349d02e4 Mon Sep 17 00:00:00 2001 From: Daniel Neilson Date: Wed, 18 Nov 2020 16:10:00 +0000 Subject: [PATCH] Add DB version pre-flight checks --- .../lib/deadline/lib/database-connection.ts | 25 ++++ .../aws-rfdk/lib/deadline/lib/repository.ts | 1 + .../deadline/test/database-connection.test.ts | 137 ++++++++++++++++++ 3 files changed, 163 insertions(+) diff --git a/packages/aws-rfdk/lib/deadline/lib/database-connection.ts b/packages/aws-rfdk/lib/deadline/lib/database-connection.ts index c90004d4d..8eb59eec5 100644 --- a/packages/aws-rfdk/lib/deadline/lib/database-connection.ts +++ b/packages/aws-rfdk/lib/deadline/lib/database-connection.ts @@ -5,6 +5,7 @@ import * as path from 'path'; import { + CfnDBCluster, CfnDBInstance, IDatabaseCluster, } from '@aws-cdk/aws-docdb'; @@ -22,6 +23,7 @@ import { ISecret, } from '@aws-cdk/aws-secretsmanager'; import { + Annotations, IConstruct, Stack, } from '@aws-cdk/core'; @@ -163,6 +165,10 @@ class DocDBDatabaseConnection extends DatabaseConnection { constructor(private readonly props: DocDBConnectionOptions) { super(); + if (!this.isCompatibleDocDBVersion()) { + Annotations.of(props.database).addError('engineVersion must be 3.6.0 to be compatible with Deadline'); + } + this.containerEnvironment = { // The container must fetch the credentials from Secrets Manager DB_CREDENTIALS_URI: this.props.login.secretArn, @@ -256,6 +262,25 @@ class DocDBDatabaseConnection extends DatabaseConnection { throw new Error('The internal implementation of the AWS CDK\'s DocumentDB cluster construct may have changed. Please update to a newer RFDK for an updated implementation, or file a ticket if this is the latest release.'); } } + + /** + * Deadline is only compatible with MongoDB 3.6. This function attempts to determine whether + * the given DocDB version is compatible. + */ + protected isCompatibleDocDBVersion(): boolean { + // The defaultChild of a DocDB DatabaseCluster is a CfnDBCluster, but we only have this + // child if the customer didn't import from attributes. We can check the DB version by + // checking the value of the engineVersion property of that object. + if (this.props.database.node.defaultChild) { + const cluster = this.props.database.node.defaultChild! as CfnDBCluster; + if (cluster.engineVersion === '3.6.0') { + return true; + } + return false; + } + + return true; // No information, assume it's compatible. + } } /** diff --git a/packages/aws-rfdk/lib/deadline/lib/repository.ts b/packages/aws-rfdk/lib/deadline/lib/repository.ts index efd94d503..ab5c65585 100644 --- a/packages/aws-rfdk/lib/deadline/lib/repository.ts +++ b/packages/aws-rfdk/lib/deadline/lib/repository.ts @@ -288,6 +288,7 @@ export interface RepositoryProps { /** * Specify the database where the deadline schema needs to be initialized. + * Note that Deadline supports only databases that are compatible with MongoDB 3.6. * * @default A Document DB Cluster will be created with a single db.r5.large instance. */ diff --git a/packages/aws-rfdk/lib/deadline/test/database-connection.test.ts b/packages/aws-rfdk/lib/deadline/test/database-connection.test.ts index 4d25c4cf6..113c18037 100644 --- a/packages/aws-rfdk/lib/deadline/test/database-connection.test.ts +++ b/packages/aws-rfdk/lib/deadline/test/database-connection.test.ts @@ -31,6 +31,7 @@ import { import { PrivateHostedZone, } from '@aws-cdk/aws-route53'; +import { Secret } from '@aws-cdk/aws-secretsmanager'; import { Duration, Stack, @@ -77,6 +78,7 @@ describe('DocumentDB', () => { backup: { retention: Duration.days(15), }, + engineVersion: '3.6.0', }); if (!database.secret) { @@ -264,6 +266,141 @@ describe('DocumentDB', () => { }); +describe('DocumentDB Version Checks', () => { + let stack: Stack; + let vpc: Vpc; + beforeEach(() => { + stack = new Stack(); + vpc = new Vpc(stack, 'VPC'); + }); + + test('Compatible version', () => { + // GIVEN + const database = new DatabaseCluster(stack, 'DbCluster', { + masterUser: { + username: 'master', + }, + instanceProps: { + instanceType: InstanceType.of( + InstanceClass.R5, + InstanceSize.XLARGE, + ), + vpc, + vpcSubnets: { + onePerAz: true, + subnetType: SubnetType.PRIVATE, + }, + }, + backup: { + retention: Duration.days(15), + }, + engineVersion: '3.6.0', + }); + + // WHEN + DatabaseConnection.forDocDB({database, login: database.secret!}); + + // THEN + expect(database.node.metadata.length).toBe(0); + }); + + test('When from attributes', () => { + // GIVEN + const sg = new SecurityGroup(stack, 'SG', { + vpc, + }); + const secret = new Secret(stack, 'Secret'); + const database = DatabaseCluster.fromDatabaseClusterAttributes(stack, 'DbCluster', { + clusterEndpointAddress: '1.2.3.4', + clusterIdentifier: 'foo', + instanceEndpointAddresses: [ '1.2.3.5' ], + instanceIdentifiers: [ 'i0' ], + port: 27001, + readerEndpointAddress: '1.2.3.6', + securityGroup: sg, + }); + + // WHEN + DatabaseConnection.forDocDB({database, login: secret}); + + // THEN + expect(database.node.metadata.length).toBe(0); + }); + + test('No engineVersion given', () => { + // GIVEN + const database = new DatabaseCluster(stack, 'DbCluster', { + masterUser: { + username: 'master', + }, + instanceProps: { + instanceType: InstanceType.of( + InstanceClass.R5, + InstanceSize.XLARGE, + ), + vpc, + vpcSubnets: { + onePerAz: true, + subnetType: SubnetType.PRIVATE, + }, + }, + backup: { + retention: Duration.days(15), + }, + }); + + // WHEN + DatabaseConnection.forDocDB({database, login: database.secret!}); + + // THEN + expect(database.node.metadata).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'aws:cdk:error', + data: 'engineVersion must be 3.6.0 to be compatible with Deadline', + }), + ]), + ); + }); + + test('engineVersion not 3.6.0', () => { + // GIVEN + const database = new DatabaseCluster(stack, 'DbCluster', { + masterUser: { + username: 'master', + }, + instanceProps: { + instanceType: InstanceType.of( + InstanceClass.R5, + InstanceSize.XLARGE, + ), + vpc, + vpcSubnets: { + onePerAz: true, + subnetType: SubnetType.PRIVATE, + }, + }, + backup: { + retention: Duration.days(15), + }, + engineVersion: '4.0.0', + }); + + // WHEN + DatabaseConnection.forDocDB({database, login: database.secret!}); + + // THEN + expect(database.node.metadata).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'aws:cdk:error', + data: 'engineVersion must be 3.6.0 to be compatible with Deadline', + }), + ]), + ); + }); +}); + describe('MongoDB', () => { let stack: Stack; let vpc: Vpc;