integ/lib/storage-struct.ts (208 lines of code) (raw):

/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ import { Duration, RemovalPolicy, Stack, } from 'aws-cdk-lib'; import { DatabaseCluster } from 'aws-cdk-lib/aws-docdb'; import { InstanceClass, InstanceSize, InstanceType, Vpc, SubnetType, Volume, } from 'aws-cdk-lib/aws-ec2'; import { AccessPoint, FileSystem, } from 'aws-cdk-lib/aws-efs'; import { RetentionDays, } from 'aws-cdk-lib/aws-logs'; import { PrivateHostedZone } from 'aws-cdk-lib/aws-route53'; import { ISecret } from 'aws-cdk-lib/aws-secretsmanager'; import { MongoDbInstance, MongoDbPostInstallSetup, MongoDbSsplLicenseAcceptance, MongoDbVersion, MountableEfs, X509CertificatePem, X509CertificatePkcs12, } from 'aws-rfdk'; import { DatabaseConnection, IVersion, Repository, } from 'aws-rfdk/deadline'; import { Construct } from 'constructs'; // Interface for supplying database connection and accompanying secret for credentials export interface IRenderFarmDb { db: DatabaseCluster | MongoDbInstance, secret: ISecret, cert?: X509CertificatePem, } export enum DatabaseType { DocDB = 1, MongoDB = 2, } export interface StorageStructProps { readonly integStackTag: string; readonly version: IVersion; readonly databaseType?: DatabaseType; /** * @default false */ readonly enableSecretsManagement?: boolean; } export class StorageStruct extends Construct { public readonly repo: Repository; public readonly database: IRenderFarmDb; public readonly efs: FileSystem; constructor(scope: Construct, id: string, props: StorageStructProps) { super(scope, id); // Confirm that user has accepted SSPL license to use mongoDB const userAcceptsSSPL = process.env.USER_ACCEPTS_SSPL_FOR_RFDK_TESTS!.toString(); const userSsplAcceptance = userAcceptsSSPL === 'true' ? MongoDbSsplLicenseAcceptance.USER_ACCEPTS_SSPL : MongoDbSsplLicenseAcceptance.USER_REJECTS_SSPL; const infrastructureStackName = 'RFDKIntegInfrastructure' + props.integStackTag; // Get farm VPC from lookup const vpc = Vpc.fromLookup(this, 'Vpc', { tags: { StackName: infrastructureStackName }}) as Vpc; // Create EFS filesystem here since both MongoDB and DocDB will be backed by an EFS filesystem. const deadlineEfs = new FileSystem(this, 'FileSystem', { vpc, vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS, // We must limit the subnets to one per AZ to avoid creating duplicate EFS mount targets for the same AZ, // causing the stack deployment to fail. onePerAz: true, }, removalPolicy: RemovalPolicy.DESTROY, }); const accessPoint = new AccessPoint(this, 'AccessPoint', { fileSystem: deadlineEfs, posixUser: { uid: '0', gid: '0', }, }); const deadlineMountableEfs = new MountableEfs(this, { filesystem: deadlineEfs, accessPoint, }); let cacert; let database; let databaseConnection; let databaseSecret: ISecret; // Check if the test requires a DocDB or MongoDB to be created. If neither is provided, the Repository construct will create a DocDB itself. if (props.databaseType == DatabaseType.DocDB) { // Create a DocDB database cluster on the VPC database = new DatabaseCluster(this, 'DocumentDatabase', { instanceType: InstanceType.of(InstanceClass.R5, InstanceSize.LARGE), vpc, vpcSubnets: { onePerAz: true, subnetType: SubnetType.PRIVATE_WITH_EGRESS, }, masterUser: { username: 'DocDBUser', }, engineVersion: '5.0.0', backup: { retention: Duration.days(15), }, removalPolicy: RemovalPolicy.DESTROY, }); databaseSecret = database.secret!; // Create a database connection for the DocDB databaseConnection = DatabaseConnection.forDocDB({ database: database, login: databaseSecret, }); } // If databaseType is MongoDB, a MongoDB instance is created in place of the DocDB else if (props.databaseType == DatabaseType.MongoDB) { // Create CA signing certificate cacert = new X509CertificatePem(this, 'CaCert', { subject: { cn: 'ca.renderfarm.local', }, }); // Create server-side certificate signed with the CA cert const serverCert = new X509CertificatePem(this, 'MongoCert', { subject: { cn: 'mongo.renderfarm.local', o: 'RFDK-Integ', ou: 'MongoServer', }, signingCertificate: cacert, }); // Create client-side certificate signed with the CA cert const clientCert = new X509CertificatePem(this, 'DeadlineMongoCert', { subject: { cn: 'MongoUser', o: 'RFDK-Integ', ou: 'MongoClient', }, signingCertificate: cacert, }); // Create PKCS12 certificate from the client certificate const clientPkcs12 = new X509CertificatePkcs12(this, 'DeadlineMongoPkcs12', { sourceCertificate: clientCert, }); // Create the mongoDB instance database = new MongoDbInstance(this, 'MongoDB', { logGroupProps: { logGroupPrefix: `/${Stack.of(this).stackName}-${id}/`, retention: RetentionDays.TWO_MONTHS, }, mongoDb: { userSsplAcceptance, version: MongoDbVersion.COMMUNITY_8_0, dnsZone: new PrivateHostedZone(this, 'Zone', { zoneName: 'renderfarm.local', vpc, }), hostname: 'mongo', serverCertificate: serverCert, }, vpc, }); databaseSecret = database.adminUser!; // Ensure that the EBS Volume created by the MongoDB construct is deleted. const dbDataEBS = database.node.findChild('MongoDbData') as Volume; dbDataEBS.applyRemovalPolicy(RemovalPolicy.DESTROY); new MongoDbPostInstallSetup(this, 'MongoDbPostInstall', { vpc, vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS }, mongoDb: database, users: { x509AuthUsers: [ { certificate: clientCert.cert, roles: JSON.stringify([ { role: 'readWriteAnyDatabase', db: 'admin' }, {role: 'clusterMonitor', db: 'admin' }]), }, ], }, }); databaseConnection = DatabaseConnection.forMongoDbInstance({ database, clientCertificate: clientPkcs12, }); } else { // Otherwise the repository installer will handle creating a DocDB database = undefined; databaseConnection = undefined; } // Define properties for Deadline installer. A unique log group name is created so that logstreams are not assigned // to the same log group across tests this.repo = new Repository(this, 'Repository', { vpc, database: databaseConnection, fileSystem: deadlineMountableEfs, version: props.version, repositoryInstallationTimeout: Duration.minutes(30), logGroupProps: { logGroupPrefix: `/${Stack.of(this).stackName}-${id}/`, retention: RetentionDays.TWO_MONTHS, }, removalPolicy: { database: RemovalPolicy.DESTROY, filesystem: RemovalPolicy.DESTROY, }, secretsManagementSettings: { enabled: props.enableSecretsManagement ?? false, credentialsRemovalPolicy: RemovalPolicy.DESTROY, }, }); if( !database ) { database = this.repo.node.findChild('DocumentDatabase') as DatabaseCluster; databaseSecret = database.secret!; } this.database = { db: database, secret: databaseSecret!, cert: cacert, }; this.efs = ( deadlineEfs || this.repo.node.findChild('FileSystem') as FileSystem ); } }