Fix cluster ASG signal failure, DNS duplication, type hacks, and health check default

- Remove duplicate shebang, set -e, and redundant SSM agent install from user data
  script so cfn-signal always runs (root cause of "0 SUCCESS signals" deploy failure)
- Remove DNS record creation from service stack's configureBlueGreenDns() to avoid
  CloudFormation conflicts with the persistent ALB stack that owns those records
- Replace readonly type assertion hacks with direct property assignments on 6 ALB/listener fields
- Change default health check path from /health to / for universal compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 16:36:18 -08:00
parent fa1e865f50
commit 21f4fef6a3
2 changed files with 23 additions and 56 deletions

View File

@@ -365,7 +365,7 @@ export class SpicyEcsService extends Construct {
};
const healthCheck = {
path: props.healthCheck?.path ?? '/health',
path: props.healthCheck?.path ?? '/',
interval: props.healthCheck?.interval ?? 15,
timeout: props.healthCheck?.timeout ?? 14,
healthyThresholdCount: props.healthCheck?.healthyThresholdCount ?? 2,
@@ -835,33 +835,14 @@ export class SpicyEcsService extends Construct {
* The isActive flag in BlueGreenDnsConfig is informational only - both records are always created.
* The actual routing is determined by which service stack has the lower priority listener rule.
*/
private configureBlueGreenDns(dns: BlueGreenDnsConfig, loadBalancer: elbv2.IApplicationLoadBalancer): void {
// Extract domain from hostname (e.g., "api.example.com" -> "example.com")
const domainParts = dns.activeHostname.split('.');
const zoneName = domainParts.slice(-2).join('.');
const activeRecordName = domainParts.slice(0, -2).join('.') || '@';
const inactiveRecordName = dns.inactiveHostname.split('.').slice(0, -2).join('.') || '@';
const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
hostedZoneId: dns.hostedZoneId,
zoneName: zoneName,
});
// Active hostname DNS record - always points to the ALB
// Note: This record is created by both blue and green stacks, but routing is via listener priorities
new route53.ARecord(this, 'ActiveDnsRecord', {
zone: hostedZone,
recordName: activeRecordName === '@' ? undefined : activeRecordName,
target: route53.RecordTarget.fromAlias(new route53Targets.LoadBalancerTarget(loadBalancer)),
});
// Inactive hostname DNS record - always points to the ALB
// Note: This record is created by both blue and green stacks, but routing is via listener priorities
new route53.ARecord(this, 'InactiveDnsRecord', {
zone: hostedZone,
recordName: inactiveRecordName === '@' ? undefined : inactiveRecordName,
target: route53.RecordTarget.fromAlias(new route53Targets.LoadBalancerTarget(loadBalancer)),
});
private configureBlueGreenDns(_dns: BlueGreenDnsConfig, _loadBalancer: elbv2.IApplicationLoadBalancer): void {
// DNS records are NOT created here. They belong to the persistent ALB stack (spicy-alb.ts),
// which owns the Route53 A records pointing to the load balancer. Creating them here would
// cause CloudFormation conflicts when both blue and green service stacks attempt to manage
// the same DNS records.
//
// Traffic routing between blue/green services is controlled entirely by ALB listener rule
// priorities — not by DNS changes. Both hostnames always resolve to the same ALB.
}
/**