constructor()

in packages/@aws-cdk/aws-docdb/lib/cluster.ts [348:492]


  constructor(scope: Construct, id: string, props: DatabaseClusterProps) {
    super(scope, id);

    this.vpc = props.vpc;
    this.vpcSubnets = props.vpcSubnets;

    // Determine the subnet(s) to deploy the DocDB cluster to
    const { subnetIds, internetConnectivityEstablished } = this.vpc.selectSubnets(this.vpcSubnets);

    // DocDB clusters require a subnet group with subnets from at least two AZs.
    // We cannot test whether the subnets are in different AZs, but at least we can test the amount.
    // See https://docs.aws.amazon.com/documentdb/latest/developerguide/replication.html#replication.high-availability
    if (subnetIds.length < 2) {
      throw new Error(`Cluster requires at least 2 subnets, got ${subnetIds.length}`);
    }

    const subnetGroup = new CfnDBSubnetGroup(this, 'Subnets', {
      dbSubnetGroupDescription: `Subnets for ${id} database`,
      subnetIds,
    });

    // Create the security group for the DB cluster
    let securityGroup: ec2.ISecurityGroup;
    if (props.securityGroup) {
      securityGroup = props.securityGroup;
    } else {
      securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', {
        description: 'DocumentDB security group',
        vpc: this.vpc,
      });
      // HACK: Use an escape-hatch to apply a consistent removal policy to the
      // security group so we don't get errors when trying to delete the stack
      (securityGroup.node.defaultChild as CfnResource).applyRemovalPolicy(props.removalPolicy, {
        applyToUpdateReplacePolicy: true,
      });
    }
    this.securityGroupId = securityGroup.securityGroupId;

    // Create the CloudwatchLogsConfiguratoin
    const enableCloudwatchLogsExports: string[] = [];
    if (props.exportAuditLogsToCloudWatch) {
      enableCloudwatchLogsExports.push('audit');
    }
    if (props.exportProfilerLogsToCloudWatch) {
      enableCloudwatchLogsExports.push('profiler');
    }

    // Create the secret manager secret if no password is specified
    let secret: DatabaseSecret | undefined;
    if (!props.masterUser.password) {
      secret = new DatabaseSecret(this, 'Secret', {
        username: props.masterUser.username,
        encryptionKey: props.masterUser.kmsKey,
        excludeCharacters: props.masterUser.excludeCharacters,
        secretName: props.masterUser.secretName,
      });
    }

    // Default to encrypted storage
    const storageEncrypted = props.storageEncrypted ?? true;

    if (props.kmsKey && !storageEncrypted) {
      throw new Error('KMS key supplied but storageEncrypted is false');
    }

    // Create the DocDB cluster
    this.cluster = new CfnDBCluster(this, 'Resource', {
      // Basic
      engineVersion: props.engineVersion,
      dbClusterIdentifier: props.dbClusterName,
      dbSubnetGroupName: subnetGroup.ref,
      port: props.port,
      vpcSecurityGroupIds: [this.securityGroupId],
      dbClusterParameterGroupName: props.parameterGroup?.parameterGroupName,
      deletionProtection: props.deletionProtection,
      // Admin
      masterUsername: secret ? secret.secretValueFromJson('username').toString() : props.masterUser.username,
      masterUserPassword: secret
        ? secret.secretValueFromJson('password').toString()
        : props.masterUser.password!.toString(),
      // Backup
      backupRetentionPeriod: props.backup?.retention?.toDays(),
      preferredBackupWindow: props.backup?.preferredWindow,
      preferredMaintenanceWindow: props.preferredMaintenanceWindow,
      // EnableCloudwatchLogsExports
      enableCloudwatchLogsExports: enableCloudwatchLogsExports.length > 0 ? enableCloudwatchLogsExports : undefined,
      // Encryption
      kmsKeyId: props.kmsKey?.keyArn,
      storageEncrypted,
    });

    this.cluster.applyRemovalPolicy(props.removalPolicy, {
      applyToUpdateReplacePolicy: true,
    });

    this.clusterIdentifier = this.cluster.ref;
    this.clusterResourceIdentifier = this.cluster.attrClusterResourceId;

    const port = Token.asNumber(this.cluster.attrPort);
    this.clusterEndpoint = new Endpoint(this.cluster.attrEndpoint, port);
    this.clusterReadEndpoint = new Endpoint(this.cluster.attrReadEndpoint, port);

    this.setLogRetention(this, props, enableCloudwatchLogsExports);

    if (secret) {
      this.secret = secret.attach(this);
    }

    // Create the instances
    const instanceCount = props.instances ?? DatabaseCluster.DEFAULT_NUM_INSTANCES;
    if (instanceCount < 1) {
      throw new Error('At least one instance is required');
    }

    for (let i = 0; i < instanceCount; i++) {
      const instanceIndex = i + 1;

      const instanceIdentifier = props.instanceIdentifierBase != null ? `${props.instanceIdentifierBase}${instanceIndex}`
        : props.dbClusterName != null ? `${props.dbClusterName}instance${instanceIndex}` : undefined;

      const instance = new CfnDBInstance(this, `Instance${instanceIndex}`, {
        // Link to cluster
        dbClusterIdentifier: this.cluster.ref,
        dbInstanceIdentifier: instanceIdentifier,
        // Instance properties
        dbInstanceClass: databaseInstanceType(props.instanceType),
      });

      instance.applyRemovalPolicy(props.removalPolicy, {
        applyToUpdateReplacePolicy: true,
      });

      // We must have a dependency on the NAT gateway provider here to create
      // things in the right order.
      instance.node.addDependency(internetConnectivityEstablished);

      this.instanceIdentifiers.push(instance.ref);
      this.instanceEndpoints.push(new Endpoint(instance.attrEndpoint, port));
    }

    this.connections = new ec2.Connections({
      defaultPort: ec2.Port.tcp(port),
      securityGroups: [securityGroup],
    });
  }