constructor()

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;
      },
    });
  }