constructor()

in src/proxysql.ts [192:327]


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

    if ((props.rdscluster && props.customBackend) || (!props.rdscluster && !props.customBackend)) {
      throw new Error('You have to specify either dbcluster or customBackend. Atleast one, not both.');
    }

    const vpc = props.vpc ?? props.rdscluster?.vpc ?? getOrCreateVpc(this);

    // generate and store MYSQL_USER1_PASSWORD in the secrets manager
    const auroraMasterSecret = new secretsmanager.Secret(this, 'AuroraMasterSecret', {
      secretName: `${Stack.of(this).stackName}-auroraMasterSecret`,
      generateSecretString: {
        passwordLength: 12,
        excludePunctuation: true,
      },
    });

    // generate and store RADMIN_PASSWORD in the secrets manager
    const radminSecret = new secretsmanager.Secret(this, 'RAdminPassword', {
      secretName: `${Stack.of(this).stackName}-radmin_pwd`,
      generateSecretString: {
        passwordLength: 12,
        excludePunctuation: true,
      },
    });

    const cluster = new ecs.Cluster(this, 'Cluster', {
      vpc,
    });

    const taskDefinition = new ecs.TaskDefinition(this, 'Task', {
      compatibility: ecs.Compatibility.FARGATE,
      memoryMiB: '4096',
      cpu: '1024',
    });
    const proxysql = taskDefinition.addContainer('proxysql', {
      image: ecs.ContainerImage.fromAsset('./dockerAssets.d/proxysql'),
      logging: new ecs.AwsLogDriver({
        streamPrefix: 'proxysql-main',
      }),
      environment: {
        DB_WRITER_HOSTNAME: `writer.${PROXYSQL_PRIVATE_ZONE_NAME}`,
        DB_READER_HOSTNAME: `reader.${PROXYSQL_PRIVATE_ZONE_NAME}`,
        DB_WRITER_PORT: props.rdscluster ? AURORA_LISTENER_PORT.toString() : props.customBackend!.writerPort ? props.customBackend!.writerPort : '3306',
        DB_READER_PORT: props.rdscluster ? AURORA_LISTENER_PORT.toString() : props.customBackend!.readerPort ? props.customBackend!.readerPort : '3306',
        DB_MASTER_USERNAME: props.rdscluster ? DB_MASTER_USERNAME : props.customBackend!.masterUsername ?? 'undefined',
      },
      secrets: {
        DB_MASTER_PASSWORD: ecs.Secret.fromSecretsManager(props.customBackend?.masterSecret ?? auroraMasterSecret),
        RADMIN_PASSWORD: ecs.Secret.fromSecretsManager(radminSecret),
      },
    });

    // proxysql.addPortMappings({ containerPort: 6032 })
    proxysql.addPortMappings({ containerPort: PROXYSQL_TRAFFIC_PORT });
    proxysql.addPortMappings({ containerPort: PROXYSQL_ADMIN_PORT });

    const svc = new ecsPatterns.NetworkLoadBalancedFargateService(this, 'NLBService', {
      assignPublicIp: false,
      cluster,
      taskDefinition,
      publicLoadBalancer: false,
      listenerPort: NLB_LISTENER_PORT,
    });

    // if nlbSubnetIds provided, override the value of NLB subnets
    if (props.nlbSubnetIds) {
      const cfnLoadBalancer = svc.loadBalancer.node.tryFindChild('Resource') as elbv2.CfnLoadBalancer;
      cfnLoadBalancer.addPropertyOverride('Subnets', props.nlbSubnetIds);
    }

    // if custom master secert is provided, grant the ecs task execution role to read this secret
    if (props.customBackend?.masterSecret) {
      // svc.taskDefinition.addToExecutionRolePolicy()
      props.customBackend.masterSecret.grantRead(svc.taskDefinition.executionRole!);
    }

    // allow fargate service connect to dbcluster
    props.rdscluster?.dbcluster.connections.allowDefaultPortFrom(svc.service);

    // allow proxysql to listen on tcp 6033 for traffic
    svc.service.connections.allowFrom(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(PROXYSQL_TRAFFIC_PORT));

    // allow vpc cidr to visit proxysql admin port
    svc.service.connections.allowFrom(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(PROXYSQL_ADMIN_PORT));

    svc.targetGroup.setAttribute('deregistration_delay.timeout_seconds', '30');
    svc.loadBalancer.setAttribute('load_balancing.cross_zone.enabled', 'true');


    // create route53 alias mapping to the NLB
    const zone = new route53.HostedZone(this, 'ProxySQLHZ', {
      zoneName: PROXYSQL_PRIVATE_ZONE_NAME,
      vpcs: [vpc],
    });

    // nlb.proxysql.local ALIAS to the internal NLB
    const ar = new route53.ARecord(this, 'AliasRecord', {
      zone,
      recordName: 'nlb',
      target: route53.RecordTarget.fromAlias(new alias.LoadBalancerTarget(svc.loadBalancer)),
    });

    if (props.rdscluster) {
      // writer.proxysql.local CNAME to Aurora writer
      new route53.CnameRecord(this, 'CnameAuroraWriter', {
        recordName: 'writer',
        domainName: props.rdscluster.clusterEndpointHostname,
        zone,
      });
      // reader.proxysql.local CNAME to Aurora reader
      new route53.CnameRecord(this, 'CnameAuroraReader', {
        recordName: 'reader',
        domainName: props.rdscluster.clusterEndpointHostname,
        zone,
      });
    } else {
      // writer.proxysql.local CNAME to custom writer
      new route53.CnameRecord(this, 'CnameCustomWriter', {
        recordName: 'writer',
        domainName: props.customBackend!.writerHost,
        zone,
      });
      // reader.proxysql.local CNAME to custom reader
      new route53.CnameRecord(this, 'CnameCustomReader', {
        recordName: 'reader',
        domainName: props.customBackend!.readerHost,
        zone,
      });
    }

    printOutput(this, 'NLBAliasDN', ar.domainName);
    printOutput(this, 'ECSClusterName', svc.cluster.clusterName);
    printOutput(this, 'ECSServiceName', svc.service.serviceName);
  }