constructor()

in packages/aws-cdk-lib/aws-rds/lib/cluster.ts [788:975]


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

    if (props.clusterScalabilityType !== undefined && props.clusterScailabilityType !== undefined) {
      throw new ValidationError('You cannot specify both clusterScalabilityType and clusterScailabilityType (deprecated). Use clusterScalabilityType.', this);
    }

    if ((props.vpc && props.instanceProps?.vpc) || (!props.vpc && !props.instanceProps?.vpc)) {
      throw new ValidationError('Provide either vpc or instanceProps.vpc, but not both', this);
    }
    if ((props.vpcSubnets && props.instanceProps?.vpcSubnets)) {
      throw new ValidationError('Provide either vpcSubnets or instanceProps.vpcSubnets, but not both', this);
    }
    this.vpc = props.instanceProps?.vpc ?? props.vpc!;
    this.vpcSubnets = props.instanceProps?.vpcSubnets ?? props.vpcSubnets;

    this.cloudwatchLogGroups = {};

    this.singleUserRotationApplication = props.engine.singleUserRotationApplication;
    this.multiUserRotationApplication = props.engine.multiUserRotationApplication;

    this.serverlessV2MaxCapacity = props.serverlessV2MaxCapacity ?? 2;
    this.serverlessV2MinCapacity = props.serverlessV2MinCapacity ?? 0.5;
    this.validateServerlessScalingConfig();

    this.enableDataApi = props.enableDataApi;

    const { subnetIds } = this.vpc.selectSubnets(this.vpcSubnets);

    // Cannot test whether the subnets are in different AZs, but at least we can test the amount.
    if (subnetIds.length < 2) {
      Annotations.of(this).addError(`Cluster requires at least 2 subnets, got ${subnetIds.length}`);
    }

    this.subnetGroup = props.subnetGroup ?? new SubnetGroup(this, 'Subnets', {
      description: `Subnets for ${id} database`,
      vpc: this.vpc,
      vpcSubnets: this.vpcSubnets,
      removalPolicy: renderUnless(helperRemovalPolicy(props.removalPolicy), RemovalPolicy.DESTROY),
    });

    this.securityGroups = props.instanceProps?.securityGroups ?? props.securityGroups ?? [
      new ec2.SecurityGroup(this, 'SecurityGroup', {
        description: 'RDS security group',
        vpc: this.vpc,
      }),
    ];

    const combineRoles = props.engine.combineImportAndExportRoles ?? false;
    let { s3ImportRole, s3ExportRole } = setupS3ImportExport(this, props, combineRoles);

    if (props.parameterGroup && props.parameters) {
      throw new ValidationError('You cannot specify both parameterGroup and parameters', this);
    }
    const parameterGroup = props.parameterGroup ?? (
      props.parameters
        ? new ParameterGroup(this, 'ParameterGroup', {
          engine: props.engine,
          parameters: props.parameters,
        })
        : undefined
    );
    // bind the engine to the Cluster
    const clusterEngineBindConfig = props.engine.bindToCluster(this, {
      s3ImportRole,
      s3ExportRole,
      parameterGroup,
    });

    const clusterAssociatedRoles: CfnDBCluster.DBClusterRoleProperty[] = [];
    if (s3ImportRole) {
      clusterAssociatedRoles.push({ roleArn: s3ImportRole.roleArn, featureName: clusterEngineBindConfig.features?.s3Import });
    }
    if (s3ExportRole &&
        // only add the second associated Role if it's different than the first
        // (duplicates in the associated Roles array are not allowed by the RDS service)
        (s3ExportRole !== s3ImportRole ||
        clusterEngineBindConfig.features?.s3Import !== clusterEngineBindConfig.features?.s3Export)) {
      clusterAssociatedRoles.push({ roleArn: s3ExportRole.roleArn, featureName: clusterEngineBindConfig.features?.s3Export });
    }

    const clusterParameterGroup = props.parameterGroup ?? clusterEngineBindConfig.parameterGroup;
    const clusterParameterGroupConfig = clusterParameterGroup?.bindToCluster({});
    this.engine = props.engine;

    const clusterIdentifier = FeatureFlags.of(this).isEnabled(cxapi.RDS_LOWERCASE_DB_IDENTIFIER) && !Token.isUnresolved(props.clusterIdentifier)
      ? props.clusterIdentifier?.toLowerCase()
      : props.clusterIdentifier;

    if (props.domain) {
      this.domainId = props.domain;
      this.domainRole = props.domainRole ?? new iam.Role(this, 'RDSClusterDirectoryServiceRole', {
        assumedBy: new iam.CompositePrincipal(
          new iam.ServicePrincipal('rds.amazonaws.com'),
          new iam.ServicePrincipal('directoryservice.rds.amazonaws.com'),
        ),
        managedPolicies: [
          iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonRDSDirectoryServiceAccess'),
        ],
      });
    }

    validateDatabaseClusterProps(this, props);

    const enablePerformanceInsights = props.enablePerformanceInsights
      || props.performanceInsightRetention !== undefined
      || props.performanceInsightEncryptionKey !== undefined
      || props.databaseInsightsMode === DatabaseInsightsMode.ADVANCED;
    this.performanceInsightsEnabled = enablePerformanceInsights;
    this.performanceInsightRetention = enablePerformanceInsights
      ? (props.performanceInsightRetention || PerformanceInsightRetention.DEFAULT)
      : undefined;
    this.performanceInsightEncryptionKey = props.performanceInsightEncryptionKey;
    this.databaseInsightsMode = props.databaseInsightsMode;

    // configure enhanced monitoring role for the cluster or instance
    this.monitoringRole = props.monitoringRole;
    if (!props.monitoringRole && props.monitoringInterval && props.monitoringInterval.toSeconds()) {
      this.monitoringRole = new Role(this, 'MonitoringRole', {
        assumedBy: new ServicePrincipal('monitoring.rds.amazonaws.com'),
        managedPolicies: [
          ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonRDSEnhancedMonitoringRole'),
        ],
      });
    }

    if (props.enableClusterLevelEnhancedMonitoring && !props.monitoringInterval) {
      throw new ValidationError('`monitoringInterval` must be set when `enableClusterLevelEnhancedMonitoring` is true.', this);
    }
    if (
      props.monitoringInterval && !props.monitoringInterval.isUnresolved() &&
      [0, 1, 5, 10, 15, 30, 60].indexOf(props.monitoringInterval.toSeconds()) === -1
    ) {
      throw new ValidationError(`'monitoringInterval' must be one of 0, 1, 5, 10, 15, 30, or 60 seconds, got: ${props.monitoringInterval.toSeconds()} seconds.`, this);
    }

    this.newCfnProps = {
      // Basic
      engine: props.engine.engineType,
      engineVersion: props.engine.engineVersion?.fullVersion,
      dbClusterIdentifier: clusterIdentifier,
      dbSubnetGroupName: this.subnetGroup.subnetGroupName,
      vpcSecurityGroupIds: this.securityGroups.map(sg => sg.securityGroupId),
      port: props.port ?? clusterEngineBindConfig.port,
      dbClusterParameterGroupName: clusterParameterGroupConfig?.parameterGroupName,
      associatedRoles: clusterAssociatedRoles.length > 0 ? clusterAssociatedRoles : undefined,
      deletionProtection: defaultDeletionProtection(props.deletionProtection, props.removalPolicy),
      enableIamDatabaseAuthentication: props.iamAuthentication,
      enableHttpEndpoint: Lazy.any({ produce: () => this.enableDataApi }),
      networkType: props.networkType,
      serverlessV2ScalingConfiguration: Lazy.any({
        produce: () => {
          if (this.hasServerlessInstance) {
            return {
              minCapacity: this.serverlessV2MinCapacity,
              maxCapacity: this.serverlessV2MaxCapacity,
            };
          }
          return undefined;
        },
      }),
      storageType: props.storageType?.toString(),
      enableLocalWriteForwarding: props.enableLocalWriteForwarding,
      clusterScalabilityType: props.clusterScalabilityType ?? props.clusterScailabilityType,
      // Admin
      backtrackWindow: props.backtrackWindow?.toSeconds(),
      backupRetentionPeriod: props.backup?.retention?.toDays(),
      preferredBackupWindow: props.backup?.preferredWindow,
      preferredMaintenanceWindow: props.preferredMaintenanceWindow,
      databaseName: props.defaultDatabaseName,
      enableCloudwatchLogsExports: props.cloudwatchLogsExports,
      // Encryption
      kmsKeyId: props.storageEncryptionKey?.keyArn,
      storageEncrypted: props.storageEncryptionKey ? true : props.storageEncrypted,
      // Tags
      copyTagsToSnapshot: props.copyTagsToSnapshot ?? true,
      domain: this.domainId,
      domainIamRoleName: this.domainRole?.roleName,
      performanceInsightsEnabled: this.performanceInsightsEnabled || props.enablePerformanceInsights, // fall back to undefined if not set
      performanceInsightsKmsKeyId: this.performanceInsightEncryptionKey?.keyArn,
      performanceInsightsRetentionPeriod: this.performanceInsightRetention,
      databaseInsightsMode: this.databaseInsightsMode,
      autoMinorVersionUpgrade: props.autoMinorVersionUpgrade,
      monitoringInterval: props.enableClusterLevelEnhancedMonitoring ? props.monitoringInterval?.toSeconds() : undefined,
      monitoringRoleArn: props.enableClusterLevelEnhancedMonitoring ? this.monitoringRole?.roleArn : undefined,
      engineLifecycleSupport: props.engineLifecycleSupport,
    };
  }