Skip to content

Commit

Permalink
Merge pull request #53 from supabase-community/refactor-cfn-parameter
Browse files Browse the repository at this point in the history
feat: Add HighAvailability option
  • Loading branch information
mats16 authored Jul 2, 2023
2 parents 4a9e4a2 + e60780d commit db7c8dd
Show file tree
Hide file tree
Showing 5 changed files with 415 additions and 543 deletions.
74 changes: 35 additions & 39 deletions src/ecs-patterns.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as cdk from 'aws-cdk-lib';
import { ScalableTarget, CfnScalableTarget, CfnScalingPolicy } from 'aws-cdk-lib/aws-applicationautoscaling';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import { NetworkLoadBalancedTaskImageOptions } from 'aws-cdk-lib/aws-ecs-patterns';
Expand All @@ -8,7 +9,6 @@ import * as logs from 'aws-cdk-lib/aws-logs';
import * as cloudMap from 'aws-cdk-lib/aws-servicediscovery';
import { Construct } from 'constructs';
import { AuthProvider } from './supabase-auth-provider';
import { SupabaseDatabase } from './supabase-db';
import { FargateStack } from './supabase-stack';

interface SupabaseTaskImageOptions extends NetworkLoadBalancedTaskImageOptions {
Expand All @@ -30,6 +30,7 @@ export interface BaseFargateServiceProps {
export interface AutoScalingFargateServiceProps extends BaseFargateServiceProps {
minTaskCount?: number;
maxTaskCount?: number;
highAvailability?: cdk.CfnCondition;
}

export interface TargetGroupProps {
Expand Down Expand Up @@ -162,55 +163,50 @@ export class BaseFargateService extends Construct {
}

export class AutoScalingFargateService extends BaseFargateService {
readonly cfnParameters: {
taskSize: cdk.CfnParameter;
minTaskCount: cdk.CfnParameter;
maxTaskCount: cdk.CfnParameter;
};
readonly taskSize: cdk.CfnParameter;

constructor(scope: FargateStack, id: string, props: AutoScalingFargateServiceProps) {
super(scope, id, props);

const { minTaskCount, maxTaskCount } = props;

this.cfnParameters = {
taskSize: new cdk.CfnParameter(this, 'TaskSize', {
description: 'Fargare task size',
type: 'String',
default: 'micro',
allowedValues: ['micro', 'small', 'medium', 'large', 'xlarge', '2xlarge', '4xlarge'],
}),
minTaskCount: new cdk.CfnParameter(this, 'MinTaskCount', {
description: 'Minimum fargate task count',
type: 'Number',
default: (typeof minTaskCount == 'undefined') ? 1 : minTaskCount,
minValue: 0,
}),
maxTaskCount: new cdk.CfnParameter(this, 'MaxTaskCount', {
description: 'Maximum fargate task count',
type: 'Number',
default: (typeof maxTaskCount == 'undefined') ? 20 : maxTaskCount,
minValue: 0,
}),
};

const cpu = scope.taskSizeMapping.findInMap(this.cfnParameters.taskSize.valueAsString, 'cpu');
const memory = scope.taskSizeMapping.findInMap(this.cfnParameters.taskSize.valueAsString, 'memory');

(this.service.taskDefinition.node.defaultChild as ecs.CfnTaskDefinition).addPropertyOverride('Cpu', cpu);
(this.service.taskDefinition.node.defaultChild as ecs.CfnTaskDefinition).addPropertyOverride('Memory', memory);

const serviceDisabled = new cdk.CfnCondition(this, 'ServiceDisabled', { expression: cdk.Fn.conditionEquals(this.cfnParameters.minTaskCount, '0') });
(this.service.node.defaultChild as ecs.CfnService).addPropertyOverride('DesiredCount', cdk.Fn.conditionIf(serviceDisabled.logicalId, 0, cdk.Aws.NO_VALUE));
const { minTaskCount, maxTaskCount, highAvailability } = props;

this.taskSize = new cdk.CfnParameter(this, 'TaskSize', {
description: 'Fargare task size',
type: 'String',
default: 'medium',
allowedValues: ['none', 'micro', 'small', 'medium', 'large', 'xlarge', '2xlarge', '4xlarge'],
});

/** CFn task definition to override */
const taskDef = this.service.taskDefinition.node.defaultChild as ecs.CfnTaskDefinition;

const cpu = scope.taskSizeMapping.findInMap(this.taskSize.valueAsString, 'cpu');
const memory = scope.taskSizeMapping.findInMap(this.taskSize.valueAsString, 'memory');

taskDef.addPropertyOverride('Cpu', cpu);
taskDef.addPropertyOverride('Memory', memory);

const autoScaling = this.service.autoScaleTaskCount({
minCapacity: this.cfnParameters.minTaskCount.valueAsNumber,
maxCapacity: this.cfnParameters.maxTaskCount.valueAsNumber,
minCapacity: minTaskCount ?? 2,
maxCapacity: maxTaskCount ?? 20,
});

autoScaling.scaleOnCpuUtilization('ScaleOnCpu', {
targetUtilizationPercent: 50,
scaleInCooldown: cdk.Duration.seconds(60),
scaleOutCooldown: cdk.Duration.seconds(60),
});

/** CFn condition for ECS service */
const serviceEnabled = new cdk.CfnCondition(this, 'ServiceEnabled', { expression: cdk.Fn.conditionNot(cdk.Fn.conditionEquals(this.taskSize, 'none')) });
(this.service.node.defaultChild as ecs.CfnService).addPropertyOverride('DesiredCount', cdk.Fn.conditionIf(serviceEnabled.logicalId, cdk.Aws.NO_VALUE, 0));

if (typeof highAvailability != 'undefined') {
/** CFn condition for auto-scaling */
const autoScalingEnabled = new cdk.CfnCondition(this, 'AutoScalingEnabled', { expression: cdk.Fn.conditionAnd(serviceEnabled, highAvailability) });
const target = autoScaling.node.findChild('Target') as ScalableTarget;
(target.node.defaultChild as CfnScalableTarget).cfnOptions.condition = autoScalingEnabled;
(target.node.findChild('ScaleOnCpu').node.defaultChild as CfnScalingPolicy).cfnOptions.condition = autoScalingEnabled;
}
}
}
21 changes: 5 additions & 16 deletions src/supabase-cdn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { WebAcl } from '../aws-waf';

interface SupabaseCdnProps {
origin: string|elb.ILoadBalancerV2;
webAclArn: cdk.CfnParameter;
}

interface BehaviorProps {
Expand All @@ -26,9 +27,6 @@ interface BehaviorProps {
export class SupabaseCdn extends Construct {
distribution: cf.Distribution;
defaultBehaviorOptions: cf.AddBehaviorOptions;
cfnParameters: {
webAclArn: cdk.CfnParameter;
};

/** Construct for CloudFront and WAF */
constructor(scope: Construct, id: string, props: SupabaseCdnProps) {
Expand All @@ -39,23 +37,14 @@ export class SupabaseCdn extends Construct {
? new HttpOrigin(props.origin, { protocolPolicy: cf.OriginProtocolPolicy.HTTPS_ONLY })
: new LoadBalancerV2Origin(props.origin, { protocolPolicy: cf.OriginProtocolPolicy.HTTP_ONLY });

this.cfnParameters = {
webAclArn: new cdk.CfnParameter(this, 'WebAclArn', {
description: 'Web ACL for CloudFront.',
type: 'String',
default: '',
allowedPattern: '^arn:aws:wafv2:us-east-1:[0-9]{12}:global/webacl/[\\w-]+/[\\w]{8}-[\\w]{4}-[\\w]{4}-[\\w]{4}-[\\w]{12}$|',
}),
};

const webAclUndefined = new cdk.CfnCondition(this, 'WebAclUndefined', { expression: cdk.Fn.conditionEquals(this.cfnParameters.webAclArn, '') });
const defaultWebAclEnabled = new cdk.CfnCondition(this, 'DefaultWebAclEnabled', { expression: cdk.Fn.conditionEquals(props.webAclArn, '') });

/** Default Web ACL */
const webAcl = new WebAcl(this, 'WebAcl', { description: 'Supabase Standard WebAcl' });
(webAcl.node.defaultChild as cdk.CfnStack).cfnOptions.condition = webAclUndefined;
const defaultWebAcl = new WebAcl(this, 'DefaultWebAcl', { description: 'Default Web ACL' });
(defaultWebAcl.node.defaultChild as cdk.CfnStack).cfnOptions.condition = defaultWebAclEnabled;

/** Web ACL ID */
const webAclId = cdk.Fn.conditionIf(webAclUndefined.logicalId, webAcl.webAclArn, this.cfnParameters.webAclArn.valueAsString);
const webAclId = cdk.Fn.conditionIf(defaultWebAclEnabled.logicalId, defaultWebAcl.webAclArn, props.webAclArn.valueAsString);

const cachePolicy = new cf.CachePolicy(this, 'CachePolicy', {
cachePolicyName: `${cdk.Aws.STACK_NAME}-CachePolicy-${cdk.Aws.REGION}`,
Expand Down
11 changes: 8 additions & 3 deletions src/supabase-db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const excludeCharacters = '%+~`#$&*()|[]{}:;<>?!\'/@\"\\=^,'; // for Password

interface SupabaseDatabaseProps {
vpc: ec2.IVpc;
highAvailability?: cdk.CfnCondition;
}

export class SupabaseDatabase extends Construct {
Expand All @@ -31,7 +32,7 @@ export class SupabaseDatabase extends Construct {
constructor(scope: Construct, id: string, props: SupabaseDatabaseProps) {
super(scope, id);

const { vpc } = props;
const { vpc, highAvailability } = props;

/** Database Engine */
//const engine = rds.DatabaseInstanceEngine.postgres({ version: rds.PostgresEngineVersion.VER_15 });
Expand Down Expand Up @@ -65,8 +66,12 @@ export class SupabaseDatabase extends Construct {
storageEncrypted: true,
});

const instance1 = this.cluster.node.findChild('Instance1').node.defaultChild as rds.CfnDBInstance;
const instance2 = this.cluster.node.findChild('Instance2').node.defaultChild as rds.CfnDBInstance;

const dbInstance = this.cluster.node.findChild('Instance1') as rds.CfnDBInstance;
if (typeof highAvailability !== 'undefined') {
instance2.cfnOptions.condition = highAvailability;
}

//this.instance = new rds.DatabaseInstance(this, 'Instance', {
// engine,
Expand Down Expand Up @@ -137,7 +142,7 @@ export class SupabaseDatabase extends Construct {
});

// Wait until the database is ready.
this.migration.node.addDependency(dbInstance);
this.migration.node.addDependency(instance1);

/** Custom resource handler to modify db user password */
const userPasswordFunction = new NodejsFunction(this, 'UserPasswordFunction', {
Expand Down
Loading

0 comments on commit db7c8dd

Please sign in to comment.