in packages/aws-rfdk/lib/deadline/lib/repository.ts [578:805]
constructor(scope: Construct, id: string, props: RepositoryProps) {
super(scope, id);
if (props.database && props.backupOptions?.databaseRetention) {
Annotations.of(this).addWarning('Backup retention for database will not be applied since a database is not being created by this construct');
}
if (props.fileSystem && props.removalPolicy?.filesystem) {
Annotations.of(this).addWarning('RemovalPolicy for filesystem will not be applied since a filesystem is not being created by this construct');
}
if (props.database && props.removalPolicy?.database) {
Annotations.of(this).addWarning('RemovalPolicy for database will not be applied since a database is not being created by this construct');
}
if (props.fileSystem instanceof MountableEfs && !props.fileSystem.accessPoint) {
throw new Error('When using EFS with the Repository, you must provide an EFS Access Point');
}
if ((props.secretsManagementSettings?.enabled ?? true) && props.database && !props.database.databaseConstruct) {
throw new Error('Admin credentials for Deadline Secrets Management cannot be generated when using an imported database. For setting up your own credentials, please refer to https://github.com/aws/aws-rfdk/tree/mainline/packages/aws-rfdk/lib/deadline#configuring-deadline-secrets-management-on-the-repository.');
}
this.version = props.version;
const meetsMinSecretsVersion = !this.version.isLessThan(Version.MINIMUM_SECRETS_MANAGEMENT_VERSION);
const secretsManagementIsEnabled = props.secretsManagementSettings?.enabled ?? meetsMinSecretsVersion;
if (secretsManagementIsEnabled && !meetsMinSecretsVersion) {
throw new Error(`The supplied Deadline version (${props.version.versionString}) does not support Deadline Secrets Management in RFDK. Either upgrade Deadline to the minimum required version (${Version.MINIMUM_SECRETS_MANAGEMENT_VERSION.versionString}) or disable the feature in the Repository's construct properties.`);
}
this.secretsManagementSettings = {
enabled: secretsManagementIsEnabled,
credentials: props.secretsManagementSettings?.credentials ??
(secretsManagementIsEnabled ? new Secret( props.database?.databaseConstruct ? Stack.of(props.database?.databaseConstruct) : this, 'SMAdminUser', {
description: 'Admin credentials for Deadline Secrets Management',
generateSecretString: {
excludeCharacters: '\"$&\'()/<>[\\]\`{|}',
includeSpace: false,
passwordLength: 24,
requireEachIncludedType: true,
generateStringKey: 'password',
secretStringTemplate: JSON.stringify({ username: Repository.DEFAULT_SECRETS_MANAGEMENT_USERNAME }),
},
removalPolicy: props.secretsManagementSettings?.credentialsRemovalPolicy ?? RemovalPolicy.RETAIN,
}) : undefined),
};
this.fileSystem = props.fileSystem ?? (() => {
const fs = new EfsFileSystem(this, 'FileSystem', {
vpc: props.vpc,
vpcSubnets: props.vpcSubnets ?? { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
encrypted: true,
lifecyclePolicy: EfsLifecyclePolicy.AFTER_14_DAYS,
removalPolicy: props.removalPolicy?.filesystem ?? RemovalPolicy.RETAIN,
securityGroup: props.securityGroupsOptions?.fileSystem,
});
const paddingAccess = fs.addAccessPoint('PaddingAccessPoint', {
createAcl: {
ownerGid: '0',
ownerUid: '0',
permissions: '744',
},
path: '/RFDK_PaddingFiles',
});
new PadEfsStorage(this, 'PadEfsStorage', {
vpc: props.vpc,
vpcSubnets: props.vpcSubnets ?? { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
accessPoint: paddingAccess,
desiredPadding: Size.gibibytes(40),
});
const accessPoint = fs.addAccessPoint('AccessPoint', {
posixUser: {
uid: '0',
gid: '0',
},
});
return new MountableEfs(this, {
filesystem: fs,
accessPoint,
});
})();
// Set up the Database of the repository
if (props.database) {
this.databaseConnection = props.database;
if (props.databaseAuditLogging !== undefined){
const warningMsg = 'The parameter databaseAuditLogging only has an effect when the Repository is creating its own database.\n' +
'Please ensure that the Database provided is configured correctly.';
Annotations.of(this).addWarning(warningMsg);
}
} else {
const databaseAuditLogging = props.databaseAuditLogging ?? true;
/**
* This option is part of enabling audit logging for DocumentDB; the other required part is the enabling of the CloudWatch exports below.
*
* For more information about audit logging in DocumentDB, see: https://docs.aws.amazon.com/documentdb/latest/developerguide/event-auditing.html
*/
const parameterGroup = databaseAuditLogging ? new ClusterParameterGroup(this, 'ParameterGroup', {
description: 'DocDB cluster parameter group with enabled audit logs',
family: 'docdb5.0',
parameters: {
audit_logs: 'enabled',
},
}) : undefined;
const instances = props.documentDbInstanceCount ?? Repository.DEFAULT_NUM_DOCDB_INSTANCES;
const dbCluster = new DatabaseCluster(this, 'DocumentDatabase', {
masterUser: {username: 'DocDBUser'},
engineVersion: '5.0.0',
instanceType: InstanceType.of(InstanceClass.R5, InstanceSize.LARGE),
vpc: props.vpc,
vpcSubnets: props.vpcSubnets ?? { subnetType: SubnetType.PRIVATE_WITH_EGRESS, onePerAz: true },
securityGroup: props.securityGroupsOptions?.database,
instances,
backup: {
retention: props.backupOptions?.databaseRetention ?? Repository.DEFAULT_DATABASE_RETENTION_PERIOD,
},
parameterGroup,
removalPolicy: props.removalPolicy?.database ?? RemovalPolicy.RETAIN,
});
if (databaseAuditLogging) {
/**
* This option enable export audit logs to Amazon CloudWatch.
* This is second options that required for enable audit log.
*/
const cfnDB = dbCluster.node.findChild('Resource') as CfnDBCluster;
cfnDB.enableCloudwatchLogsExports = ['audit'];
}
/* istanbul ignore next */
if (!dbCluster.secret) {
/* istanbul ignore next */
throw new Error('DBCluster failed to get set up properly -- missing login secret.');
}
// This is a workaround because of the bug in CDK implementation:
// autoMinorVersionUpgrade should be true by default but it's not.
// This code can be removed once fixed in CDK.
for (let i = 1; i <= instances; i++) {
const docdbInstance = dbCluster.node.tryFindChild(`Instance${ i }`) as CfnDBInstance;
docdbInstance.autoMinorVersionUpgrade = true;
}
this.databaseConnection = DatabaseConnection.forDocDB({
database: dbCluster,
login: dbCluster.secret,
});
}
// Launching the instance which installs the deadline repository in the stack.
this.installerGroup = new AutoScalingGroup(this, 'Installer', {
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE),
machineImage: new AmazonLinuxImage({
generation: this.getAmazonLinuxGenerationForDeadlineVersion(this.version),
}),
vpc: props.vpc,
vpcSubnets: props.vpcSubnets ?? {
subnetType: SubnetType.PRIVATE_WITH_EGRESS,
},
minCapacity: 1,
maxCapacity: 1,
updatePolicy: UpdatePolicy.replacingUpdate(),
signals: Signals.waitForAll({
timeout: (props.repositoryInstallationTimeout || Duration.minutes(30)),
}),
securityGroup: props.securityGroupsOptions?.installer,
});
this.node.defaultChild = this.installerGroup;
// Ensure the DB is serving before we try to connect to it.
this.databaseConnection.addChildDependency(this.installerGroup);
// Updating the user data with installation logs stream -- ALWAYS DO THIS FIRST.
this.configureCloudWatchLogStream(this.installerGroup, `${id}`, props.logGroupProps);
this.setupDirectConnect(this.installerGroup, Repository.DEFAULT_FILE_SYSTEM_MOUNT_PATH);
this.rootPrefix = props.repositoryInstallationPrefix || Repository.DEFAULT_REPO_PREFIX;
if (path.posix.isAbsolute(this.rootPrefix)) {
// If the input path is absolute, then we make it relative (to the root of the repo file-system)
this.rootPrefix = path.posix.relative(
path.posix.sep,
this.rootPrefix,
);
}
const repositoryInstallationPath = path.posix.normalize(path.posix.join(Repository.DEFAULT_FILE_SYSTEM_MOUNT_PATH, this.rootPrefix));
// Updating the user data with deadline repository installation commands.
this.configureRepositoryInstallerScript(
this.installerGroup,
repositoryInstallationPath,
props.version,
props.repositorySettings,
// Change ownership of the Deadline repository files if-and-only-if the mounted file-system
// uses the POSIX permissions based on the process' user UID/GID
this.fileSystem.usesUserPosixPermissions() ? Repository.REPOSITORY_OWNER : undefined,
);
this.configureSelfTermination();
// Updating the user data with successful cfn-signal commands.
this.installerGroup.userData.addSignalOnExitCommand(this.installerGroup);
// Tag deployed resources with RFDK meta-data
tagConstruct(this);
const thisConstruct = this;
this.node.addValidation({
validate(): string[] {
const validationErrors = [];
// Using the output of VersionQuery across stacks can cause issues. CloudFormation stack outputs cannot change if
// a resource in another stack is referencing it.
if (thisConstruct.version instanceof VersionQuery) {
const versionStack = Stack.of(thisConstruct.version);
const thisStack = Stack.of(thisConstruct);
if (versionStack != thisStack) {
validationErrors.push('A VersionQuery can not be supplied from a different stack');
}
}
return validationErrors;
},
});
}