Fix ALB listener default action, auto-import numberOfAzs, and correct docs
- Fix HTTP listener in spicy-alb.ts missing default action when no certificate is provided, which would cause CDK synth to fail - Auto-import numberOfAzs from VPC stack exports (NumberOfAZs) in cluster, service, and ALB stacks when not provided via context - Fix CDK_SYNTH_EXAMPLES.md ALB examples using raw vpcId/subnetIds that don't match the actual fromContext() implementation (requires clusterName) - Fix docs overstating "only clusterName required" to list actual required params - Remove package-lock.json and add to .gitignore (project uses pnpm) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,3 +8,6 @@ node_modules
|
||||
cdk.out
|
||||
OLD_STUFF/
|
||||
OLDER_STUFF/
|
||||
|
||||
# Use pnpm only
|
||||
package-lock.json
|
||||
|
||||
@@ -111,7 +111,7 @@ pnpm exec cdk synth \
|
||||
|
||||
### Minimal Cluster (No Load Balancers) - Using CloudFormation Imports
|
||||
|
||||
**Minimal props approach:** Only `vpcStackName` required. All VPC details auto-import from VPC stack exports.
|
||||
**Minimal required props:** `vpcStackName` and tags (`ownerTag`, `productTag`). All VPC details auto-import from VPC stack exports, including `numberOfAzs`.
|
||||
|
||||
```bash
|
||||
pnpm exec cdk synth \
|
||||
@@ -128,7 +128,7 @@ pnpm exec cdk synth \
|
||||
|
||||
- VPC ID from `${vpcStackName}-VPCID`
|
||||
- VPC CIDR from `${vpcStackName}-VPCCIDR`
|
||||
- Number of AZs from `${vpcStackName}-NumberOfAZs`
|
||||
- Number of AZs from `${vpcStackName}-NumberOfAZs` (or override with `-c numberOfAzs=N`)
|
||||
- Private subnet IDs from `${vpcStackName}-PrivateSubnetA1ID`, `${vpcStackName}-PrivateSubnetB1ID`, etc.
|
||||
- Public subnet IDs from `${vpcStackName}-PublicSubnetAID`, `${vpcStackName}-PublicSubnetBID`, etc. (if `createExternalLoadBalancer=true`)
|
||||
- Availability zones auto-derived from region and number of AZs
|
||||
@@ -216,14 +216,13 @@ The ALB stack creates a persistent Application Load Balancer that can be shared
|
||||
|
||||
### Internet-Facing ALB
|
||||
|
||||
**Uses CloudFormation imports:** Only `clusterName` (or `clusterStackName`) and `numberOfAzs` required. VPC details and subnets are auto-imported from cluster/VPC stack exports. `numberOfAzs` is also auto-imported from the VPC stack if not provided.
|
||||
|
||||
```bash
|
||||
pnpm exec cdk synth \
|
||||
-c stackType=alb \
|
||||
-c stackName=my-service-alb \
|
||||
-c vpcId=vpc-12345678 \
|
||||
-c vpcCidrBlock=10.0.0.0/16 \
|
||||
-c availabilityZones=ca-central-1a,ca-central-1b \
|
||||
-c subnetIds=subnet-xxx,subnet-yyy \
|
||||
-c clusterName=my-cluster \
|
||||
-c scheme=internet-facing \
|
||||
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
|
||||
-c logsBucketName=my-cluster-logs \
|
||||
@@ -233,13 +232,21 @@ pnpm exec cdk synth \
|
||||
-c environment=dev
|
||||
```
|
||||
|
||||
**What auto-imports:**
|
||||
|
||||
- VPC stack name from `${clusterStackName}-VPCStackName` (cluster stack export)
|
||||
- VPC ID from `${clusterStackName}-VPC` (cluster stack export)
|
||||
- VPC CIDR from `${vpcStackName}-VPCCIDR` (VPC stack export)
|
||||
- Number of AZs from `${vpcStackName}-NumberOfAZs` (VPC stack export)
|
||||
- Public subnet IDs from `${vpcStackName}-PublicSubnetAID`, etc. (for internet-facing)
|
||||
- Private subnet IDs from `${vpcStackName}-PrivateSubnetA1ID`, etc. (for internal)
|
||||
|
||||
**Creates:**
|
||||
|
||||
- Application Load Balancer (internet-facing)
|
||||
- Security Group for ALB
|
||||
- HTTP Listener (port 80)
|
||||
- HTTP Listener (port 80) with HTTP→HTTPS redirect
|
||||
- HTTPS Listener (port 443) with SSL certificate
|
||||
- HTTP→HTTPS redirect rule
|
||||
- ALB access logs to S3 (if logsBucketName provided)
|
||||
|
||||
### Internal ALB
|
||||
@@ -248,10 +255,7 @@ pnpm exec cdk synth \
|
||||
pnpm exec cdk synth \
|
||||
-c stackType=alb \
|
||||
-c stackName=my-service-alb \
|
||||
-c vpcId=vpc-12345678 \
|
||||
-c vpcCidrBlock=10.0.0.0/16 \
|
||||
-c availabilityZones=ca-central-1a,ca-central-1b \
|
||||
-c subnetIds=subnet-aaa,subnet-bbb \
|
||||
-c clusterName=my-cluster \
|
||||
-c scheme=internal \
|
||||
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
|
||||
-c logsBucketName=my-cluster-logs \
|
||||
@@ -267,10 +271,7 @@ pnpm exec cdk synth \
|
||||
pnpm exec cdk synth \
|
||||
-c stackType=alb \
|
||||
-c stackName=my-service-alb \
|
||||
-c vpcId=vpc-12345678 \
|
||||
-c vpcCidrBlock=10.0.0.0/16 \
|
||||
-c availabilityZones=ca-central-1a,ca-central-1b \
|
||||
-c subnetIds=subnet-xxx,subnet-yyy \
|
||||
-c clusterName=my-cluster \
|
||||
-c scheme=internet-facing \
|
||||
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
|
||||
-c logsBucketName=my-cluster-logs \
|
||||
@@ -295,7 +296,7 @@ pnpm exec cdk synth \
|
||||
|
||||
### Minimal Service (No Load Balancer) - Using CloudFormation Imports
|
||||
|
||||
**Minimal props approach:** Only `clusterName` is required. VPC info auto-imports from cluster stack exports.
|
||||
**Minimal required props:** `clusterName`, `serviceName`, `image`, `containerPort`, and tags (`ownerTag`, `productTag`). All VPC info auto-imports from cluster/VPC stack exports.
|
||||
|
||||
```bash
|
||||
pnpm exec cdk synth \
|
||||
@@ -316,11 +317,11 @@ pnpm exec cdk synth \
|
||||
- VPC stack name from `${clusterStackName}-VPCStackName` (cluster stack export)
|
||||
- VPC ID from `${clusterStackName}-VPC` (cluster stack export)
|
||||
- VPC CIDR from `${vpcStackName}-VPCCIDR` (VPC stack export, using imported VPC stack name)
|
||||
- Private subnets from `${vpcStackName}-PrivateSubnetA1ID`, etc. (VPC stack exports)
|
||||
- Number of AZs from `${vpcStackName}-NumberOfAZs` (VPC stack export)
|
||||
- Private subnets from `${vpcStackName}-PrivateSubnetA1ID`, etc. (VPC stack exports)
|
||||
- Logs bucket from `${clusterStackName}-logs-s3-bucket` (cluster stack export)
|
||||
|
||||
**Note:** The service stack imports the VPC stack name from the cluster stack export `${clusterStackName}-VPCStackName`, then uses that to import all VPC details (CIDR, subnets, AZs) from the VPC stack exports.
|
||||
**Note:** The service stack imports the VPC stack name from the cluster stack export `${clusterStackName}-VPCStackName`, then uses that to import all VPC details (CIDR, subnets, AZs) from the VPC stack exports. `numberOfAzs` can be provided explicitly via `-c numberOfAzs=N` but will auto-import from the VPC stack if omitted.
|
||||
|
||||
### Service with Cluster ALB - Using CloudFormation Imports
|
||||
|
||||
@@ -784,7 +785,7 @@ pnpm exec cdk synth \
|
||||
| `vpcCidrBlock` | - | VPC CIDR (required, or auto-imported from `${vpcStackName}-VPCCIDR` using imported VPC stack name) |
|
||||
| `availabilityZones` | - | Comma-separated AZs (required, or auto-derived from VPC stack imports with `numberOfAzs`) |
|
||||
| `privateSubnetIds` | - | Private subnet IDs (required, or auto-imported from `${vpcStackName}-PrivateSubnetA1ID`, etc.) |
|
||||
| `numberOfAzs` | - | Number of AZs (required when importing subnets from VPC stack) |
|
||||
| `numberOfAzs` | auto | Number of AZs (auto-imported from VPC stack, or override explicitly) |
|
||||
| `publicSubnetIds` | - | Public subnet IDs (for individual ALB) |
|
||||
| `serviceName` | - | Service name (required) |
|
||||
| `image` | - | Docker image URI (required) |
|
||||
@@ -918,8 +919,8 @@ pnpm exec cdk synth -c stackType=vpc -c stackName=test -c ownerTag=T -c productT
|
||||
pnpm exec cdk synth -c stackType=ecs-cluster -c stackName=test -c vpcStackName=test-vpc -c ownerTag=T -c productTag=t -c componentTag=c -c environment=d
|
||||
```
|
||||
|
||||
**Service:**
|
||||
**Service (using CloudFormation imports):**
|
||||
|
||||
```bash
|
||||
pnpm exec cdk synth -c stackType=ecs-service -c stackName=test -c clusterName=test -c vpcId=vpc-123 -c vpcCidrBlock=10.0.0.0/16 -c availabilityZones=ca-central-1a -c privateSubnetIds=subnet-123 -c serviceName=test -c image=nginx:latest -c containerPort=80 -c ownerTag=T -c productTag=t -c componentTag=s -c environment=d
|
||||
pnpm exec cdk synth -c stackType=ecs-service -c stackName=test -c clusterName=test -c serviceName=test -c image=nginx:latest -c containerPort=80 -c ownerTag=T -c productTag=t -c componentTag=s -c environment=d
|
||||
```
|
||||
|
||||
@@ -18,7 +18,7 @@ Deploy ECS services with mixed capacity provider strategies for EC2 + Fargate bu
|
||||
|
||||
### Minimal Jenkinsfile (EC2 Only) - Using CloudFormation Imports
|
||||
|
||||
**Minimal props:** Only `clusterName` required. VPC info auto-imports from cluster stack exports.
|
||||
**Minimal required props:** `clusterName`, `numberOfAzs`, `serviceName`, `image`, `containerPort`, and tags (`ownerTag`, `productTag`). VPC info auto-imports from cluster/VPC stack exports.
|
||||
|
||||
```groovy
|
||||
@Library(["spicy-automation@main"]) _
|
||||
@@ -49,11 +49,11 @@ spicyECSService(
|
||||
- VPC stack name from `${clusterStackName}-VPCStackName` (cluster stack export)
|
||||
- VPC ID from `${clusterStackName}-VPC` (cluster stack export)
|
||||
- VPC CIDR from `${vpcStackName}-VPCCIDR` (VPC stack export, using imported VPC stack name)
|
||||
- Number of AZs from `${vpcStackName}-NumberOfAZs` (VPC stack export, or override with `numberOfAzs` parameter)
|
||||
- Private subnets from `${vpcStackName}-PrivateSubnetA1ID`, etc. (VPC stack exports)
|
||||
- Number of AZs from `${vpcStackName}-NumberOfAZs` (VPC stack export)
|
||||
- Logs bucket from `${clusterStackName}-logs-s3-bucket` (cluster stack export)
|
||||
|
||||
**Note:** The service stack imports the VPC stack name from the cluster stack export `${clusterStackName}-VPCStackName`, then uses that to import all VPC details (CIDR, subnets, AZs) from the VPC stack exports.
|
||||
**Note:** The service stack imports the VPC stack name from the cluster stack export `${clusterStackName}-VPCStackName`, then uses that to import all VPC details (CIDR, subnets, AZs) from the VPC stack exports. `numberOfAzs` is required by the Jenkins pipeline but auto-imports from VPC stack exports when using CDK directly.
|
||||
|
||||
### Mixed Capacity Strategy (EC2 + Fargate Burst)
|
||||
|
||||
|
||||
@@ -161,12 +161,6 @@ export class SpicyAlb extends Construct {
|
||||
this.loadBalancer.logAccessLogs(props.logsBucket, prefix);
|
||||
}
|
||||
|
||||
// HTTP Listener
|
||||
this.httpListener = this.loadBalancer.addListener('HTTPListener', {
|
||||
port: 80,
|
||||
protocol: elbv2.ApplicationProtocol.HTTP,
|
||||
});
|
||||
|
||||
// HTTPS Listener (if certificate provided)
|
||||
if (props.certificateArn) {
|
||||
this.httpsListener = this.loadBalancer.addListener('HTTPSListener', {
|
||||
@@ -178,17 +172,28 @@ export class SpicyAlb extends Construct {
|
||||
messageBody: 'No target groups configured',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// HTTP→HTTPS redirect (if enabled, default true)
|
||||
if (props.redirectHttpToHttps !== false) {
|
||||
this.httpListener.addAction('RedirectToHTTPS', {
|
||||
action: elbv2.ListenerAction.redirect({
|
||||
protocol: 'HTTPS',
|
||||
port: '443',
|
||||
permanent: true,
|
||||
}),
|
||||
});
|
||||
}
|
||||
// HTTP Listener - redirect to HTTPS if certificate provided, otherwise serve as primary
|
||||
if (props.certificateArn && props.redirectHttpToHttps !== false) {
|
||||
this.httpListener = this.loadBalancer.addListener('HTTPListener', {
|
||||
port: 80,
|
||||
protocol: elbv2.ApplicationProtocol.HTTP,
|
||||
defaultAction: elbv2.ListenerAction.redirect({
|
||||
protocol: 'HTTPS',
|
||||
port: '443',
|
||||
permanent: true,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
this.httpListener = this.loadBalancer.addListener('HTTPListener', {
|
||||
port: 80,
|
||||
protocol: elbv2.ApplicationProtocol.HTTP,
|
||||
defaultAction: elbv2.ListenerAction.fixedResponse(404, {
|
||||
contentType: 'text/plain',
|
||||
messageBody: 'No target groups configured',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// Blue/Green DNS configuration
|
||||
|
||||
@@ -129,10 +129,18 @@ export class SpicyAlbStack extends cdk.Stack {
|
||||
|
||||
// Import all VPC details from VPC stack exports
|
||||
const vpcCidrBlock = cdk.Fn.importValue(`${vpcStackName}-VPCCIDR`).toString();
|
||||
|
||||
// numberOfAzs: use context value if provided, otherwise auto-import from VPC stack export
|
||||
const numberOfAzsRaw = app.node.tryGetContext('numberOfAzs');
|
||||
const numberOfAzs = numberOfAzsRaw ? parseInt(numberOfAzsRaw, 10) : NaN;
|
||||
let numberOfAzs: number;
|
||||
if (numberOfAzsRaw) {
|
||||
numberOfAzs = parseInt(numberOfAzsRaw, 10);
|
||||
} else {
|
||||
const imported = cdk.Fn.importValue(`${vpcStackName}-NumberOfAZs`).toString();
|
||||
numberOfAzs = parseInt(imported, 10);
|
||||
}
|
||||
if (!numberOfAzs || Number.isNaN(numberOfAzs)) {
|
||||
throw new Error('numberOfAzs is required in context (2-4) to import VPC subnets for ALB.');
|
||||
throw new Error('numberOfAzs is required (2-4). Provide via context or ensure VPC stack exports NumberOfAZs.');
|
||||
}
|
||||
const azs = ['A', 'B', 'C', 'D'].slice(0, Math.min(Math.max(numberOfAzs, 1), 4));
|
||||
|
||||
|
||||
@@ -243,10 +243,18 @@ export class SpicyEcsClusterStack extends cdk.Stack {
|
||||
'vpcStackName is required. Provide vpcStackName to import all VPC details from VPC stack exports.'
|
||||
);
|
||||
}
|
||||
// numberOfAzs: use context value if provided, otherwise auto-import from VPC stack export
|
||||
const numberOfAzsRaw = app.node.tryGetContext('numberOfAzs');
|
||||
const numberOfAzs = numberOfAzsRaw ? parseInt(numberOfAzsRaw, 10) : NaN;
|
||||
let numberOfAzs: number;
|
||||
if (numberOfAzsRaw) {
|
||||
numberOfAzs = parseInt(numberOfAzsRaw, 10);
|
||||
} else {
|
||||
// Auto-import from VPC stack export
|
||||
const imported = cdk.Fn.importValue(`${vpcStackName}-NumberOfAZs`).toString();
|
||||
numberOfAzs = parseInt(imported, 10);
|
||||
}
|
||||
if (!numberOfAzs || Number.isNaN(numberOfAzs)) {
|
||||
throw new Error('numberOfAzs is required in context (2-4) to import subnets from the VPC stack.');
|
||||
throw new Error('numberOfAzs is required (2-4). Provide via context or ensure VPC stack exports NumberOfAZs.');
|
||||
}
|
||||
|
||||
// Tags
|
||||
|
||||
@@ -187,10 +187,18 @@ export class SpicyEcsServiceStack extends cdk.Stack {
|
||||
|
||||
// Import all VPC details from VPC stack exports
|
||||
const vpcCidrBlock = cdk.Fn.importValue(`${vpcStackName}-VPCCIDR`).toString();
|
||||
|
||||
// numberOfAzs: use context value if provided, otherwise auto-import from VPC stack export
|
||||
const numberOfAzsRaw = app.node.tryGetContext('numberOfAzs');
|
||||
const numberOfAzs = numberOfAzsRaw ? parseInt(numberOfAzsRaw, 10) : NaN;
|
||||
let numberOfAzs: number;
|
||||
if (numberOfAzsRaw) {
|
||||
numberOfAzs = parseInt(numberOfAzsRaw, 10);
|
||||
} else {
|
||||
const imported = cdk.Fn.importValue(`${vpcStackName}-NumberOfAZs`).toString();
|
||||
numberOfAzs = parseInt(imported, 10);
|
||||
}
|
||||
if (!numberOfAzs || Number.isNaN(numberOfAzs)) {
|
||||
throw new Error('numberOfAzs is required in context (2-4) to import VPC subnets.');
|
||||
throw new Error('numberOfAzs is required (2-4). Provide via context or ensure VPC stack exports NumberOfAZs.');
|
||||
}
|
||||
const azs = ['A', 'B', 'C', 'D'].slice(0, Math.min(Math.max(numberOfAzs, 1), 4));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user