@fjall/components-infrastructure 0.95.0 → 0.99.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/app.d.ts +90 -107
- package/dist/lib/app.js +149 -139
- package/dist/lib/config/aws/__t17fixture.d.ts +1 -0
- package/dist/lib/config/aws/__t17fixture.js +3 -0
- package/dist/lib/config/aws/__t17fixtureType.d.ts +2 -0
- package/dist/lib/config/aws/__t17fixtureType.js +1 -0
- package/dist/lib/config/aws/alarmTopic.js +8 -4
- package/dist/lib/config/aws/cloudTrail.js +1 -1
- package/dist/lib/config/aws/disasterRecovery.js +11 -16
- package/dist/lib/config/aws/ecrDefaultImage.d.ts +0 -1
- package/dist/lib/config/aws/ecrDefaultImage.js +13 -23
- package/dist/lib/config/aws/identityCenter.d.ts +10 -3
- package/dist/lib/config/aws/identityCenter.js +101 -37
- package/dist/lib/config/aws/identityCenterGroupMembership.js +8 -2
- package/dist/lib/config/aws/identityCenterMembership.d.ts +11 -0
- package/dist/lib/config/aws/identityCenterMembership.js +61 -0
- package/dist/lib/config/aws/index.d.ts +1 -1
- package/dist/lib/config/aws/index.js +1 -1
- package/dist/lib/config/aws/ipam.js +6 -11
- package/dist/lib/config/aws/oidcConnector.js +5 -1
- package/dist/lib/config/aws/scpPreset.js +4 -1
- package/dist/lib/patterns/aws/_eslint_test_tmp/leak.d.ts +1 -0
- package/dist/lib/patterns/aws/_eslint_test_tmp/leak.js +4 -0
- package/dist/lib/patterns/aws/account.js +7 -8
- package/dist/lib/patterns/aws/apexDomainPattern.js +10 -10
- package/dist/lib/patterns/aws/bastionFactory.d.ts +10 -0
- package/dist/lib/patterns/aws/bastionFactory.js +29 -0
- package/dist/lib/patterns/aws/buildkite.d.ts +2 -2
- package/dist/lib/patterns/aws/buildkite.js +51 -97
- package/dist/lib/patterns/aws/cdn.js +1 -1
- package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +172 -0
- package/dist/lib/patterns/aws/clickhouseDatabase.js +600 -0
- package/dist/lib/patterns/aws/compute.d.ts +4 -6
- package/dist/lib/patterns/aws/compute.js +7 -13
- package/dist/lib/patterns/aws/computeEcs.d.ts +95 -396
- package/dist/lib/patterns/aws/computeEcs.js +880 -46
- package/dist/lib/patterns/aws/computeEcsTypes.d.ts +889 -0
- package/dist/lib/patterns/aws/computeEcsTypes.js +12 -0
- package/dist/lib/patterns/aws/computeLambda.d.ts +0 -5
- package/dist/lib/patterns/aws/computeLambda.js +1 -2
- package/dist/lib/patterns/aws/database.d.ts +50 -8
- package/dist/lib/patterns/aws/database.js +183 -27
- package/dist/lib/patterns/aws/domain.js +8 -7
- package/dist/lib/patterns/aws/index.d.ts +3 -0
- package/dist/lib/patterns/aws/index.js +3 -0
- package/dist/lib/patterns/aws/interfaces/compute.d.ts +13 -1
- package/dist/lib/patterns/aws/interfaces/connector.d.ts +1 -1
- package/dist/lib/patterns/aws/interfaces/connector.js +1 -1
- package/dist/lib/patterns/aws/interfaces/database.d.ts +187 -8
- package/dist/lib/patterns/aws/interfaces/database.js +17 -3
- package/dist/lib/patterns/aws/interfaces/index.d.ts +4 -2
- package/dist/lib/patterns/aws/interfaces/index.js +4 -2
- package/dist/lib/patterns/aws/interfaces/messaging.d.ts +7 -0
- package/dist/lib/patterns/aws/interfaces/migrationContributor.d.ts +47 -0
- package/dist/lib/patterns/aws/interfaces/migrationContributor.js +9 -0
- package/dist/lib/patterns/aws/interfaces/vpcPeer.d.ts +7 -0
- package/dist/lib/patterns/aws/interfaces/vpcPeer.js +1 -0
- package/dist/lib/patterns/aws/messaging.d.ts +66 -10
- package/dist/lib/patterns/aws/messaging.js +115 -20
- package/dist/lib/patterns/aws/network.js +16 -7
- package/dist/lib/patterns/aws/organisation.d.ts +4 -0
- package/dist/lib/patterns/aws/organisation.js +24 -5
- package/dist/lib/patterns/aws/storage.d.ts +1 -2
- package/dist/lib/patterns/aws/storage.js +3 -2
- package/dist/lib/patterns/aws/vpcPeer.d.ts +34 -0
- package/dist/lib/patterns/aws/vpcPeer.js +38 -0
- package/dist/lib/patterns/aws/vpcPeerAccepter.d.ts +29 -0
- package/dist/lib/patterns/aws/vpcPeerAccepter.js +196 -0
- package/dist/lib/resources/aws/analytics/clickhouse.js +25 -7
- package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +49 -0
- package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +140 -0
- package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +4 -4
- package/dist/lib/resources/aws/analytics/clickhouseConstants.js +6 -4
- package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +12 -0
- package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -0
- package/dist/lib/resources/aws/analytics/clickhouseUserData.js +56 -5
- package/dist/lib/resources/aws/analytics/index.d.ts +2 -0
- package/dist/lib/resources/aws/analytics/index.js +1 -0
- package/dist/lib/resources/aws/base/awsStack.js +4 -2
- package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +2 -0
- package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +11 -0
- package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.d.ts +52 -0
- package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.js +60 -0
- package/dist/lib/resources/aws/compute/blockDeviceVolume.d.ts +8 -0
- package/dist/lib/resources/aws/compute/blockDeviceVolume.js +10 -0
- package/dist/lib/resources/aws/compute/ec2.d.ts +132 -12
- package/dist/lib/resources/aws/compute/ec2.js +163 -23
- package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.d.ts +41 -0
- package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +194 -0
- package/dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs +458 -0
- package/dist/lib/resources/aws/compute/ecs.d.ts +27 -1
- package/dist/lib/resources/aws/compute/ecs.js +42 -2
- package/dist/lib/resources/aws/compute/ecsConstants.d.ts +9 -0
- package/dist/lib/resources/aws/compute/ecsConstants.js +16 -0
- package/dist/lib/resources/aws/compute/ecsImages.js +32 -20
- package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.d.ts +96 -0
- package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +113 -0
- package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +2 -1
- package/dist/lib/resources/aws/compute/ecsNetworking.js +18 -6
- package/dist/lib/resources/aws/compute/ecsRemoteConnections.d.ts +38 -0
- package/dist/lib/resources/aws/compute/ecsRemoteConnections.js +80 -0
- package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +13 -4
- package/dist/lib/resources/aws/compute/ecsServiceFactory.js +155 -33
- package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +31 -1
- package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +110 -6
- package/dist/lib/resources/aws/compute/ecsTypes.d.ts +180 -13
- package/dist/lib/resources/aws/compute/ecsValidation.d.ts +9 -0
- package/dist/lib/resources/aws/compute/ecsValidation.js +63 -0
- package/dist/lib/resources/aws/compute/index.d.ts +2 -0
- package/dist/lib/resources/aws/compute/index.js +2 -0
- package/dist/lib/resources/aws/compute/lambda.d.ts +7 -13
- package/dist/lib/resources/aws/compute/lambda.js +30 -38
- package/dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs +192 -0
- package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +104 -0
- package/dist/lib/resources/aws/compute/persistentDataVolume.js +245 -0
- package/dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs +398 -0
- package/dist/lib/resources/aws/compute/samApplication.d.ts +15 -0
- package/dist/lib/resources/aws/compute/samApplication.js +27 -0
- package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +159 -0
- package/dist/lib/resources/aws/database/clickhouseConstants.js +181 -0
- package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +71 -0
- package/dist/lib/resources/aws/database/clickhouseSchemas.js +157 -0
- package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +14 -0
- package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +23 -0
- package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +69 -0
- package/dist/lib/resources/aws/database/clickhouseUserData.js +371 -0
- package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +56 -0
- package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +112 -0
- package/dist/lib/resources/aws/database/rdsAurora.d.ts +8 -1
- package/dist/lib/resources/aws/database/rdsAurora.js +42 -32
- package/dist/lib/resources/aws/database/rdsAuroraGlobal.d.ts +15 -2
- package/dist/lib/resources/aws/database/rdsAuroraGlobal.js +39 -43
- package/dist/lib/resources/aws/database/rdsDefaults.d.ts +6 -0
- package/dist/lib/resources/aws/database/rdsDefaults.js +7 -1
- package/dist/lib/resources/aws/database/rdsHelpers.d.ts +3 -3
- package/dist/lib/resources/aws/database/rdsHelpers.js +1 -0
- package/dist/lib/resources/aws/database/rdsInstance.d.ts +8 -1
- package/dist/lib/resources/aws/database/rdsInstance.js +51 -34
- package/dist/lib/resources/aws/database/rdsProxyOutput.d.ts +1 -1
- package/dist/lib/resources/aws/database/rdsProxyOutput.js +1 -1
- package/dist/lib/resources/aws/iam/delegationRole.js +12 -5
- package/dist/lib/resources/aws/iam/identityCenter/groupMembership.d.ts +9 -0
- package/dist/lib/resources/aws/iam/identityCenter/groupMembership.js +12 -0
- package/dist/lib/resources/aws/iam/identityCenter/index.d.ts +1 -0
- package/dist/lib/resources/aws/iam/identityCenter/index.js +1 -0
- package/dist/lib/resources/aws/iam/identityCenter/permissionSet.d.ts +1 -0
- package/dist/lib/resources/aws/iam/identityCenter/permissionSet.js +1 -0
- package/dist/lib/resources/aws/logging/logGroup.d.ts +0 -8
- package/dist/lib/resources/aws/logging/logGroup.js +0 -11
- package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +7 -0
- package/dist/lib/resources/aws/messaging/defaultEventBus.js +21 -0
- package/dist/lib/resources/aws/messaging/eventBridgeRule.d.ts +96 -0
- package/dist/lib/resources/aws/messaging/eventBridgeRule.js +110 -0
- package/dist/lib/resources/aws/messaging/eventTargets.d.ts +84 -0
- package/dist/lib/resources/aws/messaging/eventTargets.js +152 -0
- package/dist/lib/resources/aws/messaging/eventbridge.d.ts +25 -2
- package/dist/lib/resources/aws/messaging/eventbridge.js +22 -10
- package/dist/lib/resources/aws/messaging/index.d.ts +5 -0
- package/dist/lib/resources/aws/messaging/index.js +2 -0
- package/dist/lib/resources/aws/messaging/schedule.d.ts +118 -0
- package/dist/lib/resources/aws/messaging/schedule.js +64 -0
- package/dist/lib/resources/aws/messaging/sns.d.ts +2 -1
- package/dist/lib/resources/aws/messaging/sqs.d.ts +2 -1
- package/dist/lib/resources/aws/messaging/subscription.d.ts +112 -0
- package/dist/lib/resources/aws/messaging/subscription.js +67 -0
- package/dist/lib/resources/aws/messaging/utils.d.ts +6 -0
- package/dist/lib/resources/aws/messaging/utils.js +10 -0
- package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +60 -0
- package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +139 -0
- package/dist/lib/resources/aws/monitoring/index.d.ts +2 -0
- package/dist/lib/resources/aws/monitoring/index.js +2 -0
- package/dist/lib/resources/aws/monitoring/scheduleAlarms.d.ts +47 -0
- package/dist/lib/resources/aws/monitoring/scheduleAlarms.js +106 -0
- package/dist/lib/resources/aws/networking/crossAccountDelegationRecord.js +6 -3
- package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.d.ts +40 -0
- package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +158 -0
- package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -4
- package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
- package/dist/lib/resources/aws/networking/domainCertificate.js +6 -3
- package/dist/lib/resources/aws/networking/hostedZone.js +6 -4
- package/dist/lib/resources/aws/networking/index.d.ts +3 -0
- package/dist/lib/resources/aws/networking/index.js +3 -0
- package/dist/lib/resources/aws/networking/serviceDiscovery.d.ts +96 -0
- package/dist/lib/resources/aws/networking/serviceDiscovery.js +96 -0
- package/dist/lib/resources/aws/networking/vpc.d.ts +4 -1
- package/dist/lib/resources/aws/networking/vpc.js +10 -3
- package/dist/lib/resources/aws/networking/vpcPeeringAccepterRole.d.ts +18 -0
- package/dist/lib/resources/aws/networking/vpcPeeringAccepterRole.js +61 -0
- package/dist/lib/resources/aws/networking/vpcPeeringConnection.d.ts +49 -0
- package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +106 -0
- package/dist/lib/resources/aws/organisation/costAllocationTagActivator.d.ts +16 -5
- package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +17 -3
- package/dist/lib/resources/aws/organisation/index.d.ts +1 -1
- package/dist/lib/resources/aws/organisation/organisationPolicy.d.ts +2 -0
- package/dist/lib/resources/aws/organisation/organisationPolicy.js +3 -2
- package/dist/lib/resources/aws/secrets/secret.d.ts +7 -0
- package/dist/lib/resources/aws/secrets/secret.js +4 -3
- package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +16 -0
- package/dist/lib/resources/aws/storage/bucketDeployment.js +17 -0
- package/dist/lib/resources/aws/storage/ecr.js +5 -5
- package/dist/lib/resources/aws/storage/index.d.ts +1 -0
- package/dist/lib/resources/aws/storage/index.js +1 -0
- package/dist/lib/resources/aws/storage/s3.js +10 -3
- package/dist/lib/resources/aws/utilities/customResource.js +18 -9
- package/dist/lib/synth_dump.d.ts +1 -0
- package/dist/lib/synth_dump.js +42 -0
- package/dist/lib/utils/bastionFactory.d.ts +10 -0
- package/dist/lib/utils/bastionFactory.js +29 -0
- package/dist/lib/utils/capitaliseString.d.ts +1 -1
- package/dist/lib/utils/capitaliseString.js +1 -1
- package/dist/lib/utils/cdkContext.d.ts +10 -0
- package/dist/lib/utils/cdkContext.js +13 -0
- package/dist/lib/utils/connections.d.ts +7 -1
- package/dist/lib/utils/connections.js +21 -0
- package/dist/lib/utils/connector.d.ts +30 -2
- package/dist/lib/utils/connector.js +6 -1
- package/dist/lib/utils/costAllocationTags.d.ts +15 -0
- package/dist/lib/utils/costAllocationTags.js +16 -0
- package/dist/lib/utils/databaseTypes.d.ts +14 -0
- package/dist/lib/utils/getConfig.d.ts +2 -0
- package/dist/lib/utils/getConfig.js +2 -0
- package/dist/lib/utils/index.d.ts +4 -0
- package/dist/lib/utils/index.js +4 -0
- package/dist/lib/utils/manifestWriter.d.ts +6 -89
- package/dist/lib/utils/manifestWriter.js +36 -23
- package/dist/lib/utils/migrationVersionResolvers.d.ts +2 -0
- package/dist/lib/utils/migrationVersionResolvers.js +2 -0
- package/dist/lib/utils/orgConfigParser.js +2 -1
- package/dist/lib/utils/resolveAlertsTopic.d.ts +14 -0
- package/dist/lib/utils/resolveAlertsTopic.js +30 -0
- package/dist/lib/utils/validationLogger.js +6 -3
- package/dist/lib/utils/vpcPeerInterface.d.ts +22 -0
- package/dist/lib/utils/vpcPeerInterface.js +1 -0
- package/package.json +22 -18
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cluster as CdkCluster, type FargateService, type Ec2Service } from "aws-cdk-lib/aws-ecs";
|
|
1
|
+
import { Cluster as CdkCluster, type FargateService, type Ec2Service, type TaskDefinition } from "aws-cdk-lib/aws-ecs";
|
|
2
2
|
import { Connections, type IConnectable } from "aws-cdk-lib/aws-ec2";
|
|
3
3
|
import { Construct } from "constructs";
|
|
4
4
|
import type { StackBuilder } from "../base/awsStack.js";
|
|
@@ -56,6 +56,7 @@ export default class EcsCluster extends Construct implements IConnectable {
|
|
|
56
56
|
private certificate?;
|
|
57
57
|
private asgState;
|
|
58
58
|
private services;
|
|
59
|
+
private scheduledTaskDefinitions;
|
|
59
60
|
private scope;
|
|
60
61
|
private props;
|
|
61
62
|
private outputName;
|
|
@@ -72,8 +73,33 @@ export default class EcsCluster extends Construct implements IConnectable {
|
|
|
72
73
|
getService(name: string): FargateService | Ec2Service | undefined;
|
|
73
74
|
/** Get all services in this cluster. */
|
|
74
75
|
getServices(): Map<string, FargateService | Ec2Service>;
|
|
76
|
+
/**
|
|
77
|
+
* Get the task definition for a service or a registered scheduled task.
|
|
78
|
+
* Used by Schedule / Subscription targets to construct the EcsTask adapter
|
|
79
|
+
* for EventBridge invocation. Returns undefined for unknown names — callers
|
|
80
|
+
* MUST validate before passing to `resolveTarget(...)`.
|
|
81
|
+
*/
|
|
82
|
+
getTaskDefinition(serviceName: string): TaskDefinition | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Register a `Ec2TaskDefinition` under a synthetic name so it can be
|
|
85
|
+
* resolved by `app.addSchedule(...)` against the existing
|
|
86
|
+
* `EcsScheduleTarget` shape (AC34). Throws if `name` collides with any
|
|
87
|
+
* steady-state service name OR a previously-registered scheduled task.
|
|
88
|
+
*/
|
|
89
|
+
registerScheduledTaskDefinition(name: string, taskDefinition: TaskDefinition): void;
|
|
75
90
|
/** Get the ECS cluster construct. */
|
|
76
91
|
getCluster(): CdkCluster;
|
|
92
|
+
/**
|
|
93
|
+
* Get the EC2 instance role for the cluster's ASG (D10). Returns undefined
|
|
94
|
+
* for Fargate-only clusters. Pulled from the shared ASG capacity state.
|
|
95
|
+
*/
|
|
96
|
+
getInstanceRole(): import("aws-cdk-lib/aws-iam").IRole | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Get the underlying ASG's `autoScalingGroupName` token (string-only,
|
|
99
|
+
* not the ASG construct itself — D10 forbids ASG-typed accessors).
|
|
100
|
+
* Used by alarm helpers that need CloudWatch dimension keys.
|
|
101
|
+
*/
|
|
102
|
+
getAutoScalingGroupName(): string | undefined;
|
|
77
103
|
/** Get the ALB URL (http:// or https://). */
|
|
78
104
|
getUrl(): string | undefined;
|
|
79
105
|
/**
|
|
@@ -69,8 +69,8 @@ export default class EcsCluster extends Construct {
|
|
|
69
69
|
autoScalingGroup: undefined,
|
|
70
70
|
asgSecurityGroup: undefined
|
|
71
71
|
};
|
|
72
|
-
// Per-service tracking
|
|
73
72
|
services = new Map();
|
|
73
|
+
scheduledTaskDefinitions = new Map();
|
|
74
74
|
// Configuration
|
|
75
75
|
scope;
|
|
76
76
|
props;
|
|
@@ -147,10 +147,50 @@ export default class EcsCluster extends Construct {
|
|
|
147
147
|
}
|
|
148
148
|
return result;
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Get the task definition for a service or a registered scheduled task.
|
|
152
|
+
* Used by Schedule / Subscription targets to construct the EcsTask adapter
|
|
153
|
+
* for EventBridge invocation. Returns undefined for unknown names — callers
|
|
154
|
+
* MUST validate before passing to `resolveTarget(...)`.
|
|
155
|
+
*/
|
|
156
|
+
getTaskDefinition(serviceName) {
|
|
157
|
+
return (this.services.get(serviceName)?.taskDefinition ??
|
|
158
|
+
this.scheduledTaskDefinitions.get(serviceName));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Register a `Ec2TaskDefinition` under a synthetic name so it can be
|
|
162
|
+
* resolved by `app.addSchedule(...)` against the existing
|
|
163
|
+
* `EcsScheduleTarget` shape (AC34). Throws if `name` collides with any
|
|
164
|
+
* steady-state service name OR a previously-registered scheduled task.
|
|
165
|
+
*/
|
|
166
|
+
registerScheduledTaskDefinition(name, taskDefinition) {
|
|
167
|
+
if (this.services.has(name)) {
|
|
168
|
+
throw new Error(`Scheduled task name '${name}' collides with a steady-state service in cluster '${this.props.clusterName}'.`);
|
|
169
|
+
}
|
|
170
|
+
if (this.scheduledTaskDefinitions.has(name)) {
|
|
171
|
+
throw new Error(`Scheduled task name '${name}' is already registered in cluster '${this.props.clusterName}'.`);
|
|
172
|
+
}
|
|
173
|
+
this.scheduledTaskDefinitions.set(name, taskDefinition);
|
|
174
|
+
}
|
|
150
175
|
/** Get the ECS cluster construct. */
|
|
151
176
|
getCluster() {
|
|
152
177
|
return this.cluster;
|
|
153
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Get the EC2 instance role for the cluster's ASG (D10). Returns undefined
|
|
181
|
+
* for Fargate-only clusters. Pulled from the shared ASG capacity state.
|
|
182
|
+
*/
|
|
183
|
+
getInstanceRole() {
|
|
184
|
+
return this.asgState.autoScalingGroup?.role;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the underlying ASG's `autoScalingGroupName` token (string-only,
|
|
188
|
+
* not the ASG construct itself — D10 forbids ASG-typed accessors).
|
|
189
|
+
* Used by alarm helpers that need CloudWatch dimension keys.
|
|
190
|
+
*/
|
|
191
|
+
getAutoScalingGroupName() {
|
|
192
|
+
return this.asgState.autoScalingGroup?.autoScalingGroupName;
|
|
193
|
+
}
|
|
154
194
|
/** Get the ALB URL (http:// or https://). */
|
|
155
195
|
getUrl() {
|
|
156
196
|
if (!this.loadBalancer)
|
|
@@ -199,7 +239,7 @@ export default class EcsCluster extends Construct {
|
|
|
199
239
|
);
|
|
200
240
|
}
|
|
201
241
|
catch (error) {
|
|
202
|
-
throw new Error(`Failed to process connections for ECS service '${serviceName}': ${error instanceof Error ? error.message : String(error)}
|
|
242
|
+
throw new Error(`Failed to process connections for ECS service '${serviceName}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
203
243
|
}
|
|
204
244
|
}
|
|
205
245
|
// Per-service alarm wiring (shared topic on cluster, thresholds per service)
|
|
@@ -3,6 +3,15 @@ export declare const DEFAULT_EC2_INSTANCE_TYPE = "t4g.micro";
|
|
|
3
3
|
export declare const DEFAULT_WARM_POOL_MIN_SIZE = 1;
|
|
4
4
|
export declare const DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN = true;
|
|
5
5
|
export declare const DEFAULT_LOG_RETENTION_DAYS = 14;
|
|
6
|
+
export declare const DEFAULT_FARGATE_CPU = 256;
|
|
7
|
+
export declare const DEFAULT_FARGATE_MEMORY_MIB = 512;
|
|
8
|
+
export declare const DEFAULT_EC2_CONTAINER_MEMORY_MIB = 1024;
|
|
9
|
+
export declare const DEFAULT_ECS_FALLBACK_IMAGE = "amazon/amazon-ecs-sample";
|
|
10
|
+
export declare const DEFAULT_CUSTOM_RESOURCE_TIMEOUT_SECONDS = 300;
|
|
11
|
+
export declare const DEFAULT_HEALTH_CHECK_GRACE_SECONDS = 120;
|
|
12
|
+
export declare const DEFAULT_MIN_HEALTHY_PERCENT = 100;
|
|
13
|
+
export declare const DEFAULT_MAX_HEALTHY_PERCENT = 200;
|
|
14
|
+
export declare const DEFAULT_DESIRED_COUNT = 2;
|
|
6
15
|
/**
|
|
7
16
|
* Instance type prefixes that use ARM64 architecture (Graviton processors).
|
|
8
17
|
* All other prefixes are assumed to be x86-64 (STANDARD).
|
|
@@ -5,6 +5,22 @@ export const DEFAULT_WARM_POOL_MIN_SIZE = 1;
|
|
|
5
5
|
export const DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN = true;
|
|
6
6
|
// 14 days balances cost against retaining enough history for post-mortem debugging
|
|
7
7
|
export const DEFAULT_LOG_RETENTION_DAYS = 14;
|
|
8
|
+
// Smallest valid (cpu, memory) pair on the Fargate matrix — must move together.
|
|
9
|
+
export const DEFAULT_FARGATE_CPU = 256;
|
|
10
|
+
export const DEFAULT_FARGATE_MEMORY_MIB = 512;
|
|
11
|
+
export const DEFAULT_EC2_CONTAINER_MEMORY_MIB = 1024;
|
|
12
|
+
// AWS sample image used when no ECR repository is provided. Consumed by both
|
|
13
|
+
// the resources/ image resolver and the patterns/ defaults block — keep them
|
|
14
|
+
// in lockstep via this single export.
|
|
15
|
+
export const DEFAULT_ECS_FALLBACK_IMAGE = "amazon/amazon-ecs-sample";
|
|
16
|
+
// 5 minutes matches the Lambda service default; CDK uses it for sync providers.
|
|
17
|
+
export const DEFAULT_CUSTOM_RESOURCE_TIMEOUT_SECONDS = 300;
|
|
18
|
+
// 100/200 must move together — the rolling-deploy window relies on the gap.
|
|
19
|
+
export const DEFAULT_HEALTH_CHECK_GRACE_SECONDS = 120;
|
|
20
|
+
export const DEFAULT_MIN_HEALTHY_PERCENT = 100;
|
|
21
|
+
export const DEFAULT_MAX_HEALTHY_PERCENT = 200;
|
|
22
|
+
// Read at two sites (createService initial count + addServiceScaling derive-base) that must agree.
|
|
23
|
+
export const DEFAULT_DESIRED_COUNT = 2;
|
|
8
24
|
/**
|
|
9
25
|
* Instance type prefixes that use ARM64 architecture (Graviton processors).
|
|
10
26
|
* All other prefixes are assumed to be x86-64 (STANDARD).
|
|
@@ -1,35 +1,47 @@
|
|
|
1
|
+
import { CfnParameter, Stack } from "aws-cdk-lib";
|
|
1
2
|
import { ContainerImage } from "aws-cdk-lib/aws-ecs";
|
|
2
3
|
import { Repository } from "aws-cdk-lib/aws-ecr";
|
|
4
|
+
import { DEFAULT_ECS_FALLBACK_IMAGE } from "./ecsConstants.js";
|
|
5
|
+
import { toPascalCase } from "../../../utils/capitaliseString.js";
|
|
6
|
+
function buildImageTagDescription(serviceName) {
|
|
7
|
+
return `Image tag for ECS service ${serviceName}. Set by fjall deploy to the content-hash tag.`;
|
|
8
|
+
}
|
|
9
|
+
function getOrCreateImageTagParameter(ctx, serviceName) {
|
|
10
|
+
const stack = Stack.of(ctx.scope);
|
|
11
|
+
const paramLogicalId = `${toPascalCase(serviceName)}ImageTag`;
|
|
12
|
+
const description = buildImageTagDescription(serviceName);
|
|
13
|
+
const existing = stack.node.tryFindChild(paramLogicalId);
|
|
14
|
+
if (existing instanceof CfnParameter) {
|
|
15
|
+
if (existing.description !== description) {
|
|
16
|
+
throw new Error(`CfnParameter logical-ID collision: "${paramLogicalId}" is already registered for a different service. ` +
|
|
17
|
+
`Distinct service names cannot share a PascalCase identifier — rename one of the conflicting services.`);
|
|
18
|
+
}
|
|
19
|
+
return existing;
|
|
20
|
+
}
|
|
21
|
+
return new CfnParameter(stack, paramLogicalId, {
|
|
22
|
+
type: "String",
|
|
23
|
+
default: "latest",
|
|
24
|
+
description
|
|
25
|
+
});
|
|
26
|
+
}
|
|
3
27
|
export function getContainerImage(ctx, serviceName, containerConfig, serviceProps) {
|
|
4
28
|
const imageSource = containerConfig.image || serviceProps.image || ctx.props.ecrRepository;
|
|
5
29
|
if (!imageSource) {
|
|
6
|
-
return ContainerImage.fromRegistry(
|
|
30
|
+
return ContainerImage.fromRegistry(DEFAULT_ECS_FALLBACK_IMAGE);
|
|
7
31
|
}
|
|
8
|
-
// Build image tag with optional dockerTarget suffix
|
|
9
|
-
// Format: <service>-[<target>-]<version>
|
|
10
|
-
// imageVersion comes from CDK context (git SHA) to ensure CloudFormation
|
|
11
|
-
// detects template changes when new code is deployed. Falls back to 'latest'
|
|
12
|
-
// for apps without Dockerfiles (welcome image) or local dev.
|
|
13
|
-
const rawVersion = ctx.scope.node.tryGetContext("imageVersion");
|
|
14
|
-
const imageVersion = (typeof rawVersion === "string" ? rawVersion : undefined) || "latest";
|
|
15
|
-
const targetSuffix = serviceProps.dockerTarget
|
|
16
|
-
? `-${serviceProps.dockerTarget.toLowerCase()}`
|
|
17
|
-
: "";
|
|
18
|
-
const imageTag = `${serviceName.toLowerCase()}${targetSuffix}-${imageVersion}`;
|
|
19
32
|
if (typeof imageSource === "string") {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const isFullRegistryUrl = /^(docker\.io|registry\.hub\.docker\.com|ghcr\.io)\//i.test(imageSource) || // Docker Hub / GHCR URLs
|
|
24
|
-
imageSource.startsWith("public.ecr.aws/") || // Public ECR: public.ecr.aws/fjall/welcome
|
|
25
|
-
imageSource.includes(".dkr.ecr."); // Private ECR full URL: 123456789012.dkr.ecr.us-east-2.amazonaws.com/repo:tag
|
|
33
|
+
const isFullRegistryUrl = /^(docker\.io|registry\.hub\.docker\.com|ghcr\.io)\//i.test(imageSource) ||
|
|
34
|
+
imageSource.startsWith("public.ecr.aws/") ||
|
|
35
|
+
imageSource.includes(".dkr.ecr.");
|
|
26
36
|
if (isFullRegistryUrl) {
|
|
27
37
|
return ContainerImage.fromRegistry(imageSource);
|
|
28
38
|
}
|
|
29
|
-
|
|
39
|
+
const param = getOrCreateImageTagParameter(ctx, serviceName);
|
|
40
|
+
return ContainerImage.fromEcrRepository(Repository.fromRepositoryName(ctx.scope, `${serviceName}${containerConfig.name}EcrRepo`, imageSource), param.valueAsString);
|
|
30
41
|
}
|
|
31
42
|
if (imageSource instanceof Repository) {
|
|
32
|
-
|
|
43
|
+
const param = getOrCreateImageTagParameter(ctx, serviceName);
|
|
44
|
+
return ContainerImage.fromEcrRepository(imageSource, param.valueAsString);
|
|
33
45
|
}
|
|
34
46
|
return imageSource;
|
|
35
47
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type BaseService, DeploymentLifecycleStage } from "aws-cdk-lib/aws-ecs";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
import { LambdaFunction } from "./lambda.js";
|
|
4
|
+
/**
|
|
5
|
+
* Awsvpc network configuration consumed by the runner's `RunTask` call.
|
|
6
|
+
*/
|
|
7
|
+
export interface LifecycleHookNetworkConfiguration {
|
|
8
|
+
subnetIds: string[];
|
|
9
|
+
securityGroupIds: string[];
|
|
10
|
+
assignPublicIp: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Optional nested block describing a separate migration task definition.
|
|
14
|
+
*
|
|
15
|
+
* Three lockstep fields enforced by the type system — present-as-a-group or
|
|
16
|
+
* absent-as-a-group. This shape is the resources-layer mirror of
|
|
17
|
+
* `EcsLifecycleHookMigrationsSeparateTaskDef` at the patterns layer.
|
|
18
|
+
*/
|
|
19
|
+
export interface MigrationTaskDef {
|
|
20
|
+
/** ARN of the migration task definition (different from the service task def). */
|
|
21
|
+
definitionArn: string;
|
|
22
|
+
/** Family of the migration task definition — used to scope `ecs:RunTask`. */
|
|
23
|
+
family: string;
|
|
24
|
+
/** Task role ARN for the migration task. */
|
|
25
|
+
taskRoleArn: string;
|
|
26
|
+
/** Execution role ARN for the migration task. */
|
|
27
|
+
executionRoleArn: string;
|
|
28
|
+
}
|
|
29
|
+
export interface EcsLifecycleHookMigrationProps {
|
|
30
|
+
/**
|
|
31
|
+
* The ECS service this hook fires for.
|
|
32
|
+
*/
|
|
33
|
+
service: BaseService;
|
|
34
|
+
/** Cluster ARN — used for both `ecs:RunTask`'s cluster condition and ListTasks scoping. */
|
|
35
|
+
clusterArn: string;
|
|
36
|
+
/** ARN of the task definition the migrate task launches from. */
|
|
37
|
+
taskDefinitionArn: string;
|
|
38
|
+
/** Family of the task definition — used in the `ecs:RunTask` resource ARN. */
|
|
39
|
+
taskDefinitionFamily: string;
|
|
40
|
+
/** Task execution role ARN (the role pulling the image, writing logs). */
|
|
41
|
+
taskExecutionRoleArn: string;
|
|
42
|
+
/** Task role ARN (the role the running container assumes). */
|
|
43
|
+
taskRoleArn: string;
|
|
44
|
+
/** Migration command, passed through to the runner. */
|
|
45
|
+
command: string[];
|
|
46
|
+
/** Synthetic container name used in `RunTask` overrides. */
|
|
47
|
+
containerName: string;
|
|
48
|
+
/** Optional image override (else the task definition's image is used). */
|
|
49
|
+
image?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Optional container entrypoint override (else inherited from the image).
|
|
52
|
+
* Lets the patterns layer carry `["/usr/bin/tini", "--"]` shapes through to
|
|
53
|
+
* the runner's `ContainerOverrides` block.
|
|
54
|
+
*/
|
|
55
|
+
entryPoint?: string[];
|
|
56
|
+
/** Optional environment override. */
|
|
57
|
+
environment?: Record<string, string>;
|
|
58
|
+
/**
|
|
59
|
+
* Lambda timeout in seconds. Defaults to 300, capped at 900 (Lambda max).
|
|
60
|
+
* Should comfortably exceed the migration command's expected runtime
|
|
61
|
+
* including a few re-invocation cycles.
|
|
62
|
+
*/
|
|
63
|
+
timeoutSeconds?: number;
|
|
64
|
+
/** Awsvpc network configuration for the migrate task. */
|
|
65
|
+
networkConfiguration: LifecycleHookNetworkConfiguration;
|
|
66
|
+
/**
|
|
67
|
+
* Optional separate migration task definition. When present, `RunTask`
|
|
68
|
+
* targets THIS task def instead of the service's, and the Lambda's IAM
|
|
69
|
+
* policy expands to cover both task-def families + both role pairs.
|
|
70
|
+
*
|
|
71
|
+
* When absent, behaviour is unchanged — the migration runs in the service
|
|
72
|
+
* task def via `ContainerOverrides`.
|
|
73
|
+
*/
|
|
74
|
+
migrationTaskDef?: MigrationTaskDef;
|
|
75
|
+
/**
|
|
76
|
+
* Which deployment lifecycle stage to fire at. Defaults to
|
|
77
|
+
* `PRE_SCALE_UP` (migration runs BEFORE the new revision scales up;
|
|
78
|
+
* service rollout blocks until the hook returns SUCCEEDED).
|
|
79
|
+
*
|
|
80
|
+
* Set to `POST_SCALE_UP` for post-deploy work (backfills, smoke tests)
|
|
81
|
+
* that should run AFTER new tasks are healthy; a FAILED return triggers
|
|
82
|
+
* deployment circuit-breaker rollback.
|
|
83
|
+
*/
|
|
84
|
+
lifecycleStage?: DeploymentLifecycleStage;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* ECS deployment lifecycle hook (PRE_SCALE_UP) that runs migrations as a
|
|
88
|
+
* one-off `RunTask` before the new revision scales up. Self-wires:
|
|
89
|
+
* synthesises the Lambda + IAM + LogGroup, then attaches the hook to the
|
|
90
|
+
* service via the typed CDK API. CDK auto-generates the hook-invocation
|
|
91
|
+
* role.
|
|
92
|
+
*/
|
|
93
|
+
export declare class EcsLifecycleHookMigration extends Construct {
|
|
94
|
+
readonly lambda: LambdaFunction;
|
|
95
|
+
constructor(scope: Construct, id: string, props: EcsLifecycleHookMigrationProps);
|
|
96
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Stack } from "aws-cdk-lib";
|
|
5
|
+
import { Code, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
6
|
+
import { PolicyStatement, Effect } from "aws-cdk-lib/aws-iam";
|
|
7
|
+
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
8
|
+
import { DeploymentLifecycleLambdaTarget, DeploymentLifecycleStage } from "aws-cdk-lib/aws-ecs";
|
|
9
|
+
import { Construct } from "constructs";
|
|
10
|
+
import { LambdaFunction } from "./lambda.js";
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const LAMBDA_SOURCE_FILE = "lifecycleHookLambda.source.cjs";
|
|
13
|
+
const LAMBDA_TIMEOUT_DEFAULT_SECONDS = 300;
|
|
14
|
+
const LAMBDA_TIMEOUT_MAX_SECONDS = 900;
|
|
15
|
+
/**
|
|
16
|
+
* ECS deployment lifecycle hook (PRE_SCALE_UP) that runs migrations as a
|
|
17
|
+
* one-off `RunTask` before the new revision scales up. Self-wires:
|
|
18
|
+
* synthesises the Lambda + IAM + LogGroup, then attaches the hook to the
|
|
19
|
+
* service via the typed CDK API. CDK auto-generates the hook-invocation
|
|
20
|
+
* role.
|
|
21
|
+
*/
|
|
22
|
+
export class EcsLifecycleHookMigration extends Construct {
|
|
23
|
+
lambda;
|
|
24
|
+
constructor(scope, id, props) {
|
|
25
|
+
super(scope, id);
|
|
26
|
+
const stack = Stack.of(scope);
|
|
27
|
+
const timeoutSeconds = Math.min(props.timeoutSeconds ?? LAMBDA_TIMEOUT_DEFAULT_SECONDS, LAMBDA_TIMEOUT_MAX_SECONDS);
|
|
28
|
+
const sourcePath = path.resolve(__dirname, LAMBDA_SOURCE_FILE);
|
|
29
|
+
const source = readFileSync(sourcePath, "utf-8");
|
|
30
|
+
const migrationTaskDef = props.migrationTaskDef;
|
|
31
|
+
const targetTaskDefinitionArn = migrationTaskDef?.definitionArn ?? props.taskDefinitionArn;
|
|
32
|
+
const runTaskResources = [
|
|
33
|
+
`arn:aws:ecs:${stack.region}:${stack.account}:task-definition/${props.taskDefinitionFamily}:*`
|
|
34
|
+
];
|
|
35
|
+
if (migrationTaskDef !== undefined) {
|
|
36
|
+
runTaskResources.push(`arn:aws:ecs:${stack.region}:${stack.account}:task-definition/${migrationTaskDef.family}:*`);
|
|
37
|
+
}
|
|
38
|
+
const passRoleResources = [props.taskExecutionRoleArn, props.taskRoleArn];
|
|
39
|
+
if (migrationTaskDef !== undefined) {
|
|
40
|
+
passRoleResources.push(migrationTaskDef.taskRoleArn, migrationTaskDef.executionRoleArn);
|
|
41
|
+
}
|
|
42
|
+
// ECS RunTask awsvpcConfiguration uses subnets/securityGroups and
|
|
43
|
+
// assignPublicIp as "ENABLED"/"DISABLED" string, not boolean.
|
|
44
|
+
const awsvpcConfiguration = {
|
|
45
|
+
subnets: props.networkConfiguration.subnetIds,
|
|
46
|
+
securityGroups: props.networkConfiguration.securityGroupIds,
|
|
47
|
+
assignPublicIp: props.networkConfiguration.assignPublicIp
|
|
48
|
+
? "ENABLED"
|
|
49
|
+
: "DISABLED"
|
|
50
|
+
};
|
|
51
|
+
this.lambda = new LambdaFunction(this, `${id}Fn`, {
|
|
52
|
+
runtime: Runtime.NODEJS_22_X,
|
|
53
|
+
handler: "index.handler",
|
|
54
|
+
code: Code.fromInline(source),
|
|
55
|
+
lambdaDescription: `${id} ECS deployment lifecycle hook for migration`,
|
|
56
|
+
roleDescription: `Execution role for ${id} migration lifecycle-hook runner`,
|
|
57
|
+
timeout: timeoutSeconds,
|
|
58
|
+
memorySize: 256,
|
|
59
|
+
logGroupRetention: RetentionDays.ONE_MONTH,
|
|
60
|
+
environment: {
|
|
61
|
+
MIGRATE_CONFIG: JSON.stringify({
|
|
62
|
+
clusterArn: props.clusterArn,
|
|
63
|
+
taskDefinitionArn: targetTaskDefinitionArn,
|
|
64
|
+
name: props.containerName,
|
|
65
|
+
command: props.command,
|
|
66
|
+
...(props.image !== undefined ? { image: props.image } : {}),
|
|
67
|
+
...(props.entryPoint !== undefined
|
|
68
|
+
? { entryPoint: props.entryPoint }
|
|
69
|
+
: {}),
|
|
70
|
+
...(props.environment !== undefined
|
|
71
|
+
? { environment: props.environment }
|
|
72
|
+
: {}),
|
|
73
|
+
networkConfiguration: awsvpcConfiguration,
|
|
74
|
+
...(migrationTaskDef !== undefined
|
|
75
|
+
? { hasSeparateMigrationTaskDef: true }
|
|
76
|
+
: {})
|
|
77
|
+
})
|
|
78
|
+
},
|
|
79
|
+
inlinePolicy: [
|
|
80
|
+
new PolicyStatement({
|
|
81
|
+
effect: Effect.ALLOW,
|
|
82
|
+
actions: ["ecs:RunTask"],
|
|
83
|
+
resources: runTaskResources,
|
|
84
|
+
conditions: {
|
|
85
|
+
ArnEquals: { "ecs:cluster": props.clusterArn }
|
|
86
|
+
}
|
|
87
|
+
}),
|
|
88
|
+
// ListTasks/DescribeTasks do not support resource-level ARN scoping;
|
|
89
|
+
// restrict via the `ecs:cluster` condition key instead.
|
|
90
|
+
new PolicyStatement({
|
|
91
|
+
effect: Effect.ALLOW,
|
|
92
|
+
actions: ["ecs:ListTasks", "ecs:DescribeTasks"],
|
|
93
|
+
resources: ["*"],
|
|
94
|
+
conditions: {
|
|
95
|
+
ArnEquals: { "ecs:cluster": props.clusterArn }
|
|
96
|
+
}
|
|
97
|
+
}),
|
|
98
|
+
new PolicyStatement({
|
|
99
|
+
effect: Effect.ALLOW,
|
|
100
|
+
actions: ["iam:PassRole"],
|
|
101
|
+
resources: passRoleResources,
|
|
102
|
+
conditions: {
|
|
103
|
+
StringEquals: { "iam:PassedToService": "ecs-tasks.amazonaws.com" }
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
const lifecycleStage = props.lifecycleStage ?? DeploymentLifecycleStage.PRE_SCALE_UP;
|
|
109
|
+
props.service.addLifecycleHook(new DeploymentLifecycleLambdaTarget(this.lambda, id, {
|
|
110
|
+
lifecycleStages: [lifecycleStage]
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ApplicationListener, ApplicationLoadBalancer, type IApplicationTargetGroup, ListenerCondition } from "aws-cdk-lib/aws-elasticloadbalancingv2";
|
|
2
|
+
import { type ISecurityGroup } from "aws-cdk-lib/aws-ec2";
|
|
2
3
|
import { type ICertificate } from "aws-cdk-lib/aws-certificatemanager";
|
|
3
4
|
import { ARecord, type IHostedZone } from "aws-cdk-lib/aws-route53";
|
|
4
5
|
import type { AutoScalingGroup } from "aws-cdk-lib/aws-autoscaling";
|
|
@@ -14,7 +15,7 @@ export interface PriorityState {
|
|
|
14
15
|
/** Returns the next unused auto-incremented ALB priority, skipping any manually assigned values. */
|
|
15
16
|
export declare function getNextPriority(state: PriorityState): number;
|
|
16
17
|
export declare function buildRoutingConditions(rule: EcsRoutingConfig | undefined): ListenerCondition[];
|
|
17
|
-
export declare function addLoadBalancer(ctx: EcsConstructContext, anyServiceUsesEc2: boolean, asgSecurityGroup?:
|
|
18
|
+
export declare function addLoadBalancer(ctx: EcsConstructContext, anyServiceUsesEc2: boolean, asgSecurityGroup?: ISecurityGroup): {
|
|
18
19
|
loadBalancer: ApplicationLoadBalancer;
|
|
19
20
|
loadBalancerSecurityGroup?: SecurityGroup;
|
|
20
21
|
};
|
|
@@ -100,21 +100,33 @@ export function addLoadBalancer(ctx, anyServiceUsesEc2, asgSecurityGroup) {
|
|
|
100
100
|
}
|
|
101
101
|
export function addLoadBalancerListener(ctx, loadBalancer, certificate) {
|
|
102
102
|
const port = certificate ? 443 : 80;
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
const servicesWithPorts = ctx.props.services.filter((s) => s.containers.some((c) => c.port !== undefined));
|
|
104
|
+
const willHaveMultipleRoutes = servicesWithPorts.length > 1 ||
|
|
105
|
+
servicesWithPorts.some((s) => {
|
|
106
|
+
const rules = Array.isArray(s.routing)
|
|
107
|
+
? s.routing
|
|
108
|
+
: s.routing
|
|
109
|
+
? [s.routing]
|
|
110
|
+
: [];
|
|
111
|
+
return rules.length > 1;
|
|
112
|
+
});
|
|
113
|
+
const defaultAction = willHaveMultipleRoutes
|
|
114
|
+
? ListenerAction.fixedResponse(404, {
|
|
115
|
+
contentType: "text/plain",
|
|
116
|
+
messageBody: "Not Found"
|
|
117
|
+
})
|
|
118
|
+
: undefined;
|
|
107
119
|
if (certificate) {
|
|
108
120
|
return loadBalancer.addListener(`${ctx.props.clusterName}Listener`, {
|
|
109
121
|
port,
|
|
110
122
|
certificates: [certificate],
|
|
111
|
-
defaultAction
|
|
123
|
+
...(defaultAction !== undefined && { defaultAction })
|
|
112
124
|
});
|
|
113
125
|
}
|
|
114
126
|
else {
|
|
115
127
|
return loadBalancer.addListener(`${ctx.props.clusterName}Listener`, {
|
|
116
128
|
port,
|
|
117
|
-
defaultAction
|
|
129
|
+
...(defaultAction !== undefined && { defaultAction })
|
|
118
130
|
});
|
|
119
131
|
}
|
|
120
132
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synth-time resolver for ECS `remoteConnections` — turns each declared
|
|
3
|
+
* cross-app resource into a pair of `${PREFIX}_HOST` / `${PREFIX}_PORT` env
|
|
4
|
+
* vars resolved from SSM (`StringParameter.valueForStringParameter`) and
|
|
5
|
+
* indexed by service name for downstream merge into container environments.
|
|
6
|
+
*
|
|
7
|
+
* SSM path layout (matches the accepter side's publishing layout in
|
|
8
|
+
* `vpcPeerAccepter.ts`):
|
|
9
|
+
* /fjall/{orgId ?? "default"}/{peer.peerAppName}/resources/{resource}/endpoint
|
|
10
|
+
* /fjall/{orgId ?? "default"}/{peer.peerAppName}/resources/{resource}/port
|
|
11
|
+
*
|
|
12
|
+
* Default env-var prefix: `${toScreamingSnake(peer.peerAppName)}_${toScreamingSnake(resource)}`.
|
|
13
|
+
* Override via `RemoteConnectionSpec.envPrefix`.
|
|
14
|
+
*/
|
|
15
|
+
import { type Construct } from "constructs";
|
|
16
|
+
import { type IVpcPeer } from "../../../utils/vpcPeerInterface.js";
|
|
17
|
+
export interface RemoteConnectionSpec {
|
|
18
|
+
peer: IVpcPeer;
|
|
19
|
+
resource: string;
|
|
20
|
+
envPrefix?: string;
|
|
21
|
+
}
|
|
22
|
+
interface ServiceWithRemoteConnections {
|
|
23
|
+
name: string;
|
|
24
|
+
remoteConnections?: RemoteConnectionSpec[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve `remoteConnections` for every service into a per-service env-var
|
|
28
|
+
* bag. Returns `Record<serviceName, Record<envVarName, value>>`.
|
|
29
|
+
*
|
|
30
|
+
* Validates each `RemoteConnectionSpec` at synth time:
|
|
31
|
+
* - `resource` matches `RESOURCE_NAME_PATTERN` (PascalCase).
|
|
32
|
+
* - `envPrefix` (when supplied) matches `ENV_PREFIX_PATTERN`.
|
|
33
|
+
* - `peer.peerAppName` is defined and is not a CFN token — token-derived app
|
|
34
|
+
* names produce `${Token[…]}` literals in env-var names, breaking ECS task
|
|
35
|
+
* definitions silently.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveRemoteConnections(services: ServiceWithRemoteConnections[], scope: Construct, orgId: string | undefined): Record<string, Record<string, string>>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synth-time resolver for ECS `remoteConnections` — turns each declared
|
|
3
|
+
* cross-app resource into a pair of `${PREFIX}_HOST` / `${PREFIX}_PORT` env
|
|
4
|
+
* vars resolved from SSM (`StringParameter.valueForStringParameter`) and
|
|
5
|
+
* indexed by service name for downstream merge into container environments.
|
|
6
|
+
*
|
|
7
|
+
* SSM path layout (matches the accepter side's publishing layout in
|
|
8
|
+
* `vpcPeerAccepter.ts`):
|
|
9
|
+
* /fjall/{orgId ?? "default"}/{peer.peerAppName}/resources/{resource}/endpoint
|
|
10
|
+
* /fjall/{orgId ?? "default"}/{peer.peerAppName}/resources/{resource}/port
|
|
11
|
+
*
|
|
12
|
+
* Default env-var prefix: `${toScreamingSnake(peer.peerAppName)}_${toScreamingSnake(resource)}`.
|
|
13
|
+
* Override via `RemoteConnectionSpec.envPrefix`.
|
|
14
|
+
*/
|
|
15
|
+
import { Token } from "aws-cdk-lib";
|
|
16
|
+
import { StringParameter } from "aws-cdk-lib/aws-ssm";
|
|
17
|
+
import { VALIDATION_PATTERNS } from "@fjall/generator";
|
|
18
|
+
import { buildSsmPrefix, DEFAULT_ORG_ID } from "../../../utils/cdkContext.js";
|
|
19
|
+
import { toScreamingSnake } from "../../../utils/capitaliseString.js";
|
|
20
|
+
const RESOURCE_NAME_PATTERN = VALIDATION_PATTERNS.RESOURCE_NAME;
|
|
21
|
+
/** SCREAMING_SNAKE_CASE env-var prefix (digits allowed, must start with letter). */
|
|
22
|
+
const ENV_PREFIX_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve `remoteConnections` for every service into a per-service env-var
|
|
25
|
+
* bag. Returns `Record<serviceName, Record<envVarName, value>>`.
|
|
26
|
+
*
|
|
27
|
+
* Validates each `RemoteConnectionSpec` at synth time:
|
|
28
|
+
* - `resource` matches `RESOURCE_NAME_PATTERN` (PascalCase).
|
|
29
|
+
* - `envPrefix` (when supplied) matches `ENV_PREFIX_PATTERN`.
|
|
30
|
+
* - `peer.peerAppName` is defined and is not a CFN token — token-derived app
|
|
31
|
+
* names produce `${Token[…]}` literals in env-var names, breaking ECS task
|
|
32
|
+
* definitions silently.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveRemoteConnections(services, scope, orgId) {
|
|
35
|
+
const byService = {};
|
|
36
|
+
for (const service of services) {
|
|
37
|
+
const specs = service.remoteConnections ?? [];
|
|
38
|
+
if (specs.length === 0)
|
|
39
|
+
continue;
|
|
40
|
+
const envVars = {};
|
|
41
|
+
const seenPrefixes = new Set();
|
|
42
|
+
for (const spec of specs) {
|
|
43
|
+
const peerAppName = validateSpec(service.name, spec);
|
|
44
|
+
const peerOrgId = spec.peer.peerOrgId ?? orgId ?? DEFAULT_ORG_ID;
|
|
45
|
+
const ssmPrefix = `${buildSsmPrefix(peerOrgId, peerAppName)}/resources/${spec.resource}`;
|
|
46
|
+
const endpoint = StringParameter.valueForStringParameter(scope, `${ssmPrefix}/endpoint`);
|
|
47
|
+
const port = StringParameter.valueForStringParameter(scope, `${ssmPrefix}/port`);
|
|
48
|
+
const prefix = spec.envPrefix ??
|
|
49
|
+
`${toScreamingSnake(peerAppName)}_${toScreamingSnake(spec.resource)}`;
|
|
50
|
+
if (seenPrefixes.has(prefix)) {
|
|
51
|
+
throw new Error(`remoteConnections on service '${service.name}': duplicate env-var prefix '${prefix}' — set distinct envPrefix on each spec to avoid silently clobbering ${prefix}_HOST and ${prefix}_PORT.`);
|
|
52
|
+
}
|
|
53
|
+
seenPrefixes.add(prefix);
|
|
54
|
+
envVars[`${prefix}_HOST`] = endpoint;
|
|
55
|
+
envVars[`${prefix}_PORT`] = port;
|
|
56
|
+
}
|
|
57
|
+
byService[service.name] = envVars;
|
|
58
|
+
}
|
|
59
|
+
return byService;
|
|
60
|
+
}
|
|
61
|
+
function validateSpec(serviceName, spec) {
|
|
62
|
+
if (!RESOURCE_NAME_PATTERN.test(spec.resource)) {
|
|
63
|
+
throw new Error(`remoteConnections on service '${serviceName}': resource '${spec.resource}' is not a valid PascalCase resource name (must match ${RESOURCE_NAME_PATTERN.source}).`);
|
|
64
|
+
}
|
|
65
|
+
if (spec.envPrefix !== undefined &&
|
|
66
|
+
!ENV_PREFIX_PATTERN.test(spec.envPrefix)) {
|
|
67
|
+
throw new Error(`remoteConnections on service '${serviceName}': envPrefix '${spec.envPrefix}' is not SCREAMING_SNAKE_CASE (must match ${ENV_PREFIX_PATTERN.source}).`);
|
|
68
|
+
}
|
|
69
|
+
const peerAppName = spec.peer.peerAppName;
|
|
70
|
+
if (peerAppName === undefined) {
|
|
71
|
+
throw new Error(`remoteConnections on service '${serviceName}': peer.peerAppName is undefined — pass a peer built via VpcPeerFactory.build() so the SSM path can be resolved at synth.`);
|
|
72
|
+
}
|
|
73
|
+
if (typeof peerAppName === "string" && peerAppName.length === 0) {
|
|
74
|
+
throw new Error(`remoteConnections on service '${serviceName}': peer.peerAppName is an empty string, which would produce malformed env-var names and an invalid SSM path. Pass a literal app name (typically the Fjall app's name string).`);
|
|
75
|
+
}
|
|
76
|
+
if (Token.isUnresolved(peerAppName)) {
|
|
77
|
+
throw new Error(`remoteConnections on service '${serviceName}': peer.peerAppName is a CFN token, which would corrupt env-var names. Pass a literal app name (typically the Fjall app's name string).`);
|
|
78
|
+
}
|
|
79
|
+
return peerAppName;
|
|
80
|
+
}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { FargateService, Ec2Service, AsgCapacityProvider } from "aws-cdk-lib/aws-ecs";
|
|
1
|
+
import { FargateService, Ec2Service, AsgCapacityProvider, type DeploymentCircuitBreaker } from "aws-cdk-lib/aws-ecs";
|
|
2
2
|
import type { FargateTaskDefinition, Ec2TaskDefinition } from "aws-cdk-lib/aws-ecs";
|
|
3
|
+
import { type ISecurityGroup } from "aws-cdk-lib/aws-ec2";
|
|
3
4
|
import { TargetTrackingScalingPolicy } from "aws-cdk-lib/aws-applicationautoscaling";
|
|
4
|
-
import { AutoScalingGroup } from "aws-cdk-lib/aws-autoscaling";
|
|
5
|
-
import { SecurityGroup } from "../networking/securityGroup.js";
|
|
5
|
+
import { type AutoScalingGroup } from "aws-cdk-lib/aws-autoscaling";
|
|
6
6
|
import { type EcsServiceProps, type Ec2CapacityConfig } from "./ecsTypes.js";
|
|
7
7
|
import type { EcsConstructContext } from "./ecsContext.js";
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the user's `circuitBreaker` config to the CDK
|
|
10
|
+
* `DeploymentCircuitBreaker` shape. `undefined` resolves to the safe default
|
|
11
|
+
* `{ enable: true, rollback: true }`; `false` resolves to `undefined` (CDK
|
|
12
|
+
* omits the breaker block from CFN output entirely).
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveCircuitBreaker(config: false | {
|
|
15
|
+
rollback?: boolean;
|
|
16
|
+
} | undefined): DeploymentCircuitBreaker | undefined;
|
|
8
17
|
/** Mutable state for ASG capacity provider deduplication. */
|
|
9
18
|
export interface AsgCapacityState {
|
|
10
19
|
providers: Map<string, AsgCapacityProvider>;
|
|
11
20
|
autoScalingGroup?: AutoScalingGroup;
|
|
12
|
-
asgSecurityGroup?:
|
|
21
|
+
asgSecurityGroup?: ISecurityGroup;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
24
|
* Generates a unique key for EC2 config so services with matching
|