constructor()

in packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts [576:727]


  constructor(scope: Construct, id: string, props: ClusterProps) {
    super(scope, id);
    // Enhanced CDK Analytics Telemetry
    addConstructMetadata(this, props);

    this.vpc = props.vpc;
    this.vpcSubnets = props.vpcSubnets ?? {
      subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
    };
    this.parameterGroup = props.parameterGroup;
    this.roles = props?.roles ? [...props.roles] : [];

    const removalPolicy = props.removalPolicy ?? RemovalPolicy.RETAIN;

    const subnetGroup = props.subnetGroup ?? new ClusterSubnetGroup(this, 'Subnets', {
      description: `Subnets for ${id} Redshift cluster`,
      vpc: this.vpc,
      vpcSubnets: this.vpcSubnets,
      removalPolicy: removalPolicy,
    });

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

    const securityGroupIds = securityGroups.map(sg => sg.securityGroupId);

    let secret: DatabaseSecret | undefined;
    if (!props.masterUser.masterPassword) {
      secret = new DatabaseSecret(this, 'Secret', {
        username: props.masterUser.masterUsername,
        encryptionKey: props.masterUser.encryptionKey,
        excludeCharacters: props.masterUser.excludeCharacters,
      });
    }

    const clusterType = props.clusterType || ClusterType.MULTI_NODE;
    const nodeCount = this.validateNodeCount(clusterType, props.numberOfNodes);

    if (props.encrypted === false && props.encryptionKey !== undefined) {
      throw new Error('Cannot set property encryptionKey without enabling encryption!');
    }

    this.singleUserRotationApplication = secretsmanager.SecretRotationApplication.REDSHIFT_ROTATION_SINGLE_USER;
    this.multiUserRotationApplication = secretsmanager.SecretRotationApplication.REDSHIFT_ROTATION_MULTI_USER;

    let loggingProperties;
    if (props.loggingProperties) {
      loggingProperties = {
        bucketName: props.loggingProperties.loggingBucket.bucketName,
        s3KeyPrefix: props.loggingProperties.loggingKeyPrefix,
      };
      props.loggingProperties.loggingBucket.addToResourcePolicy(
        new iam.PolicyStatement(
          {
            actions: [
              's3:GetBucketAcl',
              's3:PutObject',
            ],
            resources: [
              props.loggingProperties.loggingBucket.arnForObjects('*'),
              props.loggingProperties.loggingBucket.bucketArn,
            ],
            principals: [
              new iam.ServicePrincipal('redshift.amazonaws.com'),
            ],
          },
        ),
      );
    }

    const nodeType = props.nodeType || NodeType.DC2_LARGE;

    if (props.multiAz) {
      if (!nodeType.startsWith('ra3')) {
        throw new Error(`Multi-AZ cluster is only supported for RA3 node types, got: ${props.nodeType}`);
      }
      if (clusterType === ClusterType.SINGLE_NODE) {
        throw new Error('Multi-AZ cluster is not supported for `clusterType` single-node');
      }
    }

    if (props.resourceAction === ResourceAction.FAILOVER_PRIMARY_COMPUTE && !props.multiAz) {
      throw new Error('ResourceAction.FAILOVER_PRIMARY_COMPUTE can only be used with multi-AZ clusters.');
    }
    if (props.availabilityZoneRelocation && !nodeType.startsWith('ra3')) {
      throw new Error(`Availability zone relocation is supported for only RA3 node types, got: ${props.nodeType}`);
    }

    this.cluster = new CfnCluster(this, 'Resource', {
      // Basic
      allowVersionUpgrade: true,
      maintenanceTrackName: props.maintenanceTrackName,
      automatedSnapshotRetentionPeriod: 1,
      clusterType,
      clusterIdentifier: props.clusterName,
      clusterSubnetGroupName: subnetGroup.clusterSubnetGroupName,
      vpcSecurityGroupIds: securityGroupIds,
      port: props.port,
      clusterParameterGroupName: props.parameterGroup && props.parameterGroup.clusterParameterGroupName,
      // Admin (unsafeUnwrap here is safe)
      masterUsername: secret?.secretValueFromJson('username').unsafeUnwrap() ?? props.masterUser.masterUsername,
      masterUserPassword: secret?.secretValueFromJson('password').unsafeUnwrap()
        ?? props.masterUser.masterPassword?.unsafeUnwrap()
        ?? 'default',
      preferredMaintenanceWindow: props.preferredMaintenanceWindow,
      nodeType,
      numberOfNodes: nodeCount,
      loggingProperties,
      iamRoles: Lazy.list({ produce: () => this.roles.map(role => role.roleArn) }, { omitEmpty: true }),
      dbName: props.defaultDatabaseName || 'default_db',
      publiclyAccessible: props.publiclyAccessible || false,
      // Encryption
      kmsKeyId: props.encryptionKey?.keyId,
      encrypted: props.encrypted ?? true,
      classic: props.classicResizing,
      elasticIp: props.elasticIp,
      enhancedVpcRouting: props.enhancedVpcRouting,
      multiAz: props.multiAz,
      resourceAction: props.resourceAction,
      availabilityZoneRelocation: props.availabilityZoneRelocation,
    });

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

    this.clusterName = this.cluster.ref;

    // create a number token that represents the port of the cluster
    const portAttribute = Token.asNumber(this.cluster.attrEndpointPort);
    this.clusterEndpoint = new Endpoint(this.cluster.attrEndpointAddress, portAttribute);

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

    const defaultPort = ec2.Port.tcp(this.clusterEndpoint.port);
    this.connections = new ec2.Connections({ securityGroups, defaultPort });
    if (props.rebootForParameterChanges) {
      this.enableRebootForParameterChanges();
    }
    // Add default role if specified and also available in the roles list
    if (props.defaultRole) {
      if (props.roles?.some(x => x === props.defaultRole)) {
        this.addDefaultIamRole(props.defaultRole);
      } else {
        throw new Error('Default role must be included in role list.');
      }
    }
  }