@fjall/components-infrastructure 0.96.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 +68 -1
- package/dist/lib/app.js +113 -4
- 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 +2 -4
- 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 +93 -5
- package/dist/lib/patterns/aws/computeEcs.js +867 -37
- package/dist/lib/patterns/aws/computeEcsTypes.d.ts +528 -25
- package/dist/lib/patterns/aws/computeEcsTypes.js +10 -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 +6 -4
- package/dist/lib/patterns/aws/index.d.ts +1 -0
- package/dist/lib/patterns/aws/index.js +1 -0
- package/dist/lib/patterns/aws/interfaces/compute.d.ts +7 -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 +2 -1
- package/dist/lib/patterns/aws/interfaces/index.js +3 -1
- 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/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 +22 -4
- 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.js +3 -1
- package/dist/lib/resources/aws/analytics/clickhouse.js +18 -9
- package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +24 -9
- package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +61 -10
- package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +3 -3
- package/dist/lib/resources/aws/analytics/clickhouseConstants.js +3 -3
- package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +7 -1
- package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -1
- package/dist/lib/resources/aws/analytics/clickhouseUserData.js +53 -3
- 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/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 +102 -6
- package/dist/lib/resources/aws/compute/ecsTypes.d.ts +173 -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 +1 -1
- 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 -4
- package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +17 -13
- package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -5
- package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
- package/dist/lib/resources/aws/networking/domainCertificate.js +6 -4
- package/dist/lib/resources/aws/networking/hostedZone.js +6 -5
- 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 +4 -1
- package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +21 -3
- 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/cdkContext.d.ts +2 -0
- package/dist/lib/utils/cdkContext.js +4 -2
- package/dist/lib/utils/connections.js +6 -0
- package/dist/lib/utils/connector.d.ts +12 -0
- package/dist/lib/utils/costAllocationTags.d.ts +9 -0
- package/dist/lib/utils/costAllocationTags.js +11 -1
- 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 +1 -0
- package/dist/lib/utils/index.js +1 -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/package.json +22 -19
|
@@ -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
|
}
|
|
@@ -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
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
-
import { FargateService, Ec2Service, PropagatedTagSource, PlacementStrategy, AsgCapacityProvider, EcsOptimizedImage, AmiHardwareType } from "aws-cdk-lib/aws-ecs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { FargateService, Ec2Service, PropagatedTagSource, PlacementStrategy, AsgCapacityProvider, EcsOptimizedImage, AmiHardwareType, NetworkMode } from "aws-cdk-lib/aws-ecs";
|
|
2
|
+
import { Peer, Port, SubnetType, UserData } from "aws-cdk-lib/aws-ec2";
|
|
3
|
+
import { ServicePrincipal } from "aws-cdk-lib/aws-iam";
|
|
4
|
+
import { Role } from "../iam/role.js";
|
|
5
|
+
import { CfnOutput, Duration, Token } from "aws-cdk-lib";
|
|
4
6
|
import { PredefinedMetric, ScalableTarget, ServiceNamespace, TargetTrackingScalingPolicy } from "aws-cdk-lib/aws-applicationautoscaling";
|
|
5
|
-
import {
|
|
7
|
+
import { Monitoring } from "aws-cdk-lib/aws-autoscaling";
|
|
6
8
|
import { SecurityGroup } from "../networking/securityGroup.js";
|
|
9
|
+
import { Ec2Instance } from "./ec2.js";
|
|
7
10
|
import { vpcHasNatGateways } from "../../../utils/vpcUtils.js";
|
|
8
11
|
import { toPascalCase } from "../../../utils/capitaliseString.js";
|
|
9
|
-
import { DEFAULT_EC2_INSTANCE_TYPE, DEFAULT_WARM_POOL_MIN_SIZE, DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN, inferAmiHardwareType } from "./ecsConstants.js";
|
|
12
|
+
import { DEFAULT_EC2_INSTANCE_TYPE, DEFAULT_WARM_POOL_MIN_SIZE, DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN, DEFAULT_HEALTH_CHECK_GRACE_SECONDS, DEFAULT_MIN_HEALTHY_PERCENT, DEFAULT_MAX_HEALTHY_PERCENT, DEFAULT_DESIRED_COUNT, inferAmiHardwareType } from "./ecsConstants.js";
|
|
10
13
|
import { ScalingType } from "./ecsTypes.js";
|
|
11
14
|
import { isServiceFargate, isServiceEc2 } from "./ecsTaskDefinition.js";
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the user's `circuitBreaker` config to the CDK
|
|
17
|
+
* `DeploymentCircuitBreaker` shape. `undefined` resolves to the safe default
|
|
18
|
+
* `{ enable: true, rollback: true }`; `false` resolves to `undefined` (CDK
|
|
19
|
+
* omits the breaker block from CFN output entirely).
|
|
20
|
+
*/
|
|
21
|
+
export function resolveCircuitBreaker(config) {
|
|
22
|
+
if (config === false)
|
|
23
|
+
return undefined;
|
|
24
|
+
const rollback = config?.rollback ?? true;
|
|
25
|
+
return { enable: true, rollback };
|
|
26
|
+
}
|
|
12
27
|
/**
|
|
13
28
|
* Generates a unique key for EC2 config so services with matching
|
|
14
29
|
* configurations share an ASG.
|
|
@@ -22,7 +37,26 @@ export function getEc2ConfigKey(ec2Config) {
|
|
|
22
37
|
const warmPoolKey = ec2Config.warmPool
|
|
23
38
|
? `wp${ec2Config.warmPool.minSize ?? DEFAULT_WARM_POOL_MIN_SIZE}-${ec2Config.warmPool.reuseOnScaleIn ?? DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN}`
|
|
24
39
|
: "nowp";
|
|
25
|
-
|
|
40
|
+
const baseKey = `${instanceType}-${amiHardwareType}-${warmPoolKey}`;
|
|
41
|
+
// PDV services are implicit singletons — sharing an ASG would let two tasks race for the same EBS volume.
|
|
42
|
+
const pdv = ec2Config.persistentDataVolume;
|
|
43
|
+
const extras = [];
|
|
44
|
+
if (pdv !== undefined) {
|
|
45
|
+
extras.push(`pdv-${pdv.deviceName}-${pdv.sizeGb}-${stableAzSegment(pdv.availabilityZone)}`);
|
|
46
|
+
}
|
|
47
|
+
// Serialise actual AZ names — `az${length}` would let services pinned to different AZs collide on `az1`.
|
|
48
|
+
// CDK Tokens (env-agnostic stacks) substitute a stable sentinel to keep logical IDs deterministic.
|
|
49
|
+
if (ec2Config.availabilityZones !== undefined) {
|
|
50
|
+
const azKey = [...ec2Config.availabilityZones]
|
|
51
|
+
.map(stableAzSegment)
|
|
52
|
+
.sort()
|
|
53
|
+
.join(",");
|
|
54
|
+
extras.push(`az-${azKey}`);
|
|
55
|
+
}
|
|
56
|
+
return extras.length === 0 ? baseKey : `${baseKey}-${extras.join("-")}`;
|
|
57
|
+
}
|
|
58
|
+
function stableAzSegment(az) {
|
|
59
|
+
return Token.isUnresolved(az) ? "synthAz" : az;
|
|
26
60
|
}
|
|
27
61
|
/**
|
|
28
62
|
* Gets or creates an ASG capacity provider for an EC2-backed service.
|
|
@@ -46,10 +80,11 @@ export function getOrCreateAsgCapacityProvider(ctx, serviceProps, state) {
|
|
|
46
80
|
: inferAmiHardwareType(instanceType);
|
|
47
81
|
const minCapacity = ec2Config.minCapacity ?? 2;
|
|
48
82
|
const maxCapacity = ec2Config.maxCapacity ?? 3;
|
|
49
|
-
const asgSecurityGroup =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
83
|
+
const asgSecurityGroup = ctx.props.cluster?.securityGroup ??
|
|
84
|
+
new SecurityGroup(ctx.scope, `${safeKey}AsgSecurityGroup`, {
|
|
85
|
+
vpc: ctx.cluster.vpc,
|
|
86
|
+
description: `Security group for ${key} auto scaling group`
|
|
87
|
+
});
|
|
53
88
|
if (ctx.directAccessEnabled) {
|
|
54
89
|
for (const service of ctx.props.services) {
|
|
55
90
|
if (isServiceEc2(service)) {
|
|
@@ -62,29 +97,61 @@ export function getOrCreateAsgCapacityProvider(ctx, serviceProps, state) {
|
|
|
62
97
|
}
|
|
63
98
|
}
|
|
64
99
|
const hasNat = vpcHasNatGateways(ctx.cluster.vpc);
|
|
65
|
-
const
|
|
66
|
-
|
|
100
|
+
const resolvedWarmPool = ec2Config.warmPool
|
|
101
|
+
? {
|
|
102
|
+
minSize: ec2Config.warmPool.minSize ?? DEFAULT_WARM_POOL_MIN_SIZE,
|
|
103
|
+
reuseOnScaleIn: ec2Config.warmPool.reuseOnScaleIn ??
|
|
104
|
+
DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN
|
|
105
|
+
}
|
|
106
|
+
: undefined;
|
|
107
|
+
// userData + role must be set on the LaunchTemplate or AsgCapacityProvider
|
|
108
|
+
// throws at synth (ProvidedLaunchTemplateExposeUser/ExposeDefine).
|
|
109
|
+
const instanceRole = new Role(ctx.scope, `${safeKey}InstanceRole`, {
|
|
110
|
+
assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
|
|
111
|
+
description: `EC2 instance role for ${key} ECS capacity`
|
|
112
|
+
});
|
|
113
|
+
const ec2Instance = new Ec2Instance(ctx.scope, `${safeKey}Ec2Instance`, {
|
|
114
|
+
serviceName: `${ctx.props.clusterName}${safeKey}`,
|
|
67
115
|
vpc: ctx.cluster.vpc,
|
|
68
116
|
vpcSubnets: {
|
|
69
|
-
subnetType: hasNat ? SubnetType.PRIVATE_WITH_EGRESS : SubnetType.PUBLIC
|
|
117
|
+
subnetType: hasNat ? SubnetType.PRIVATE_WITH_EGRESS : SubnetType.PUBLIC,
|
|
118
|
+
...(ec2Config.availabilityZones !== undefined && {
|
|
119
|
+
availabilityZones: ec2Config.availabilityZones
|
|
120
|
+
})
|
|
70
121
|
},
|
|
71
122
|
securityGroup: asgSecurityGroup,
|
|
72
123
|
minCapacity,
|
|
73
124
|
maxCapacity,
|
|
74
|
-
instanceType
|
|
125
|
+
instanceType,
|
|
126
|
+
machineImage: ec2Config.machineImage ??
|
|
127
|
+
EcsOptimizedImage.amazonLinux2023(amiHardwareType),
|
|
128
|
+
userData: ec2Config.userData ?? UserData.forLinux(),
|
|
129
|
+
role: instanceRole,
|
|
130
|
+
instanceMonitoring: ec2Config.instanceMonitoring ?? Monitoring.BASIC,
|
|
75
131
|
capacityRebalance: true,
|
|
76
|
-
|
|
77
|
-
|
|
132
|
+
ecsClusterArn: ctx.cluster.clusterArn,
|
|
133
|
+
...(ec2Config.desiredCapacity !== undefined && {
|
|
134
|
+
desiredCapacity: ec2Config.desiredCapacity
|
|
135
|
+
}),
|
|
136
|
+
...(ec2Config.blockDevices !== undefined && {
|
|
137
|
+
blockDevices: ec2Config.blockDevices
|
|
138
|
+
}),
|
|
139
|
+
...(ec2Config.associatePublicIpAddress !== undefined && {
|
|
140
|
+
associatePublicIpAddress: ec2Config.associatePublicIpAddress
|
|
141
|
+
}),
|
|
142
|
+
...(resolvedWarmPool !== undefined && { warmPool: resolvedWarmPool }),
|
|
143
|
+
...(ec2Config.persistentDataVolume !== undefined && {
|
|
144
|
+
persistentDataVolume: ec2Config.persistentDataVolume
|
|
145
|
+
}),
|
|
146
|
+
...(ec2Config.tags !== undefined && { tags: ec2Config.tags })
|
|
78
147
|
});
|
|
79
|
-
|
|
80
|
-
asg.addWarmPool({
|
|
81
|
-
minSize: ec2Config.warmPool.minSize ?? DEFAULT_WARM_POOL_MIN_SIZE,
|
|
82
|
-
reuseOnScaleIn: ec2Config.warmPool.reuseOnScaleIn ?? DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN
|
|
83
|
-
});
|
|
84
|
-
}
|
|
148
|
+
const asg = ec2Instance.getAutoScalingGroup();
|
|
85
149
|
const provider = new AsgCapacityProvider(ctx.scope, `${safeKey}AsgCapacityProvider`, {
|
|
86
150
|
autoScalingGroup: asg,
|
|
87
151
|
enableManagedDraining: true,
|
|
152
|
+
// MTP's ProtectedFromScaleIn flag does NOT clear on CP deletion, so
|
|
153
|
+
// CFN rollback wedges on a stranded protected instance. The drain
|
|
154
|
+
// path is Ec2GracefulTerminationHandler instead.
|
|
88
155
|
enableManagedTerminationProtection: false
|
|
89
156
|
});
|
|
90
157
|
ctx.cluster.addAsgCapacityProvider(provider);
|
|
@@ -101,8 +168,13 @@ export function getOrCreateAsgCapacityProvider(ctx, serviceProps, state) {
|
|
|
101
168
|
* Creates a Fargate or EC2 service and emits a CfnOutput for its ARN.
|
|
102
169
|
*/
|
|
103
170
|
export function createService(ctx, serviceName, serviceProps, taskDefinition, asgState) {
|
|
104
|
-
const desiredCount = serviceProps.desiredCount ??
|
|
171
|
+
const desiredCount = serviceProps.desiredCount ?? DEFAULT_DESIRED_COUNT;
|
|
105
172
|
let service;
|
|
173
|
+
// CDK's Ec2Service rejects `securityGroups` unless networkMode is AWS_VPC
|
|
174
|
+
// (HOST/BRIDGE share the instance ENI). FargateService is always awsvpc.
|
|
175
|
+
const explicitSecurityGroups = serviceProps.securityGroups && serviceProps.securityGroups.length > 0
|
|
176
|
+
? serviceProps.securityGroups
|
|
177
|
+
: undefined;
|
|
106
178
|
if (isServiceFargate(serviceProps)) {
|
|
107
179
|
const hasNat = vpcHasNatGateways(ctx.cluster.vpc);
|
|
108
180
|
service = new FargateService(ctx.scope, `${serviceName}Service`, {
|
|
@@ -121,12 +193,17 @@ export function createService(ctx, serviceName, serviceProps, taskDefinition, as
|
|
|
121
193
|
}
|
|
122
194
|
],
|
|
123
195
|
propagateTags: PropagatedTagSource.SERVICE,
|
|
124
|
-
circuitBreaker:
|
|
196
|
+
circuitBreaker: resolveCircuitBreaker(serviceProps.circuitBreaker),
|
|
125
197
|
enableECSManagedTags: true,
|
|
126
198
|
enableExecuteCommand: true,
|
|
127
|
-
healthCheckGracePeriod: Duration.seconds(
|
|
128
|
-
minHealthyPercent:
|
|
129
|
-
|
|
199
|
+
healthCheckGracePeriod: Duration.seconds(DEFAULT_HEALTH_CHECK_GRACE_SECONDS),
|
|
200
|
+
minHealthyPercent: serviceProps.deployment?.minHealthyPercent ??
|
|
201
|
+
DEFAULT_MIN_HEALTHY_PERCENT,
|
|
202
|
+
maxHealthyPercent: serviceProps.deployment?.maxHealthyPercent ??
|
|
203
|
+
DEFAULT_MAX_HEALTHY_PERCENT,
|
|
204
|
+
...(explicitSecurityGroups !== undefined && {
|
|
205
|
+
securityGroups: explicitSecurityGroups
|
|
206
|
+
})
|
|
130
207
|
});
|
|
131
208
|
}
|
|
132
209
|
else {
|
|
@@ -143,15 +220,59 @@ export function createService(ctx, serviceName, serviceProps, taskDefinition, as
|
|
|
143
220
|
}
|
|
144
221
|
],
|
|
145
222
|
propagateTags: PropagatedTagSource.SERVICE,
|
|
146
|
-
circuitBreaker:
|
|
223
|
+
circuitBreaker: resolveCircuitBreaker(serviceProps.circuitBreaker),
|
|
147
224
|
placementStrategies: [PlacementStrategy.spreadAcrossInstances()],
|
|
148
225
|
enableECSManagedTags: true,
|
|
149
226
|
enableExecuteCommand: true,
|
|
150
|
-
healthCheckGracePeriod: Duration.seconds(
|
|
151
|
-
minHealthyPercent:
|
|
152
|
-
|
|
227
|
+
healthCheckGracePeriod: Duration.seconds(DEFAULT_HEALTH_CHECK_GRACE_SECONDS),
|
|
228
|
+
minHealthyPercent: serviceProps.deployment?.minHealthyPercent ??
|
|
229
|
+
DEFAULT_MIN_HEALTHY_PERCENT,
|
|
230
|
+
maxHealthyPercent: serviceProps.deployment?.maxHealthyPercent ??
|
|
231
|
+
DEFAULT_MAX_HEALTHY_PERCENT,
|
|
232
|
+
...(explicitSecurityGroups !== undefined && {
|
|
233
|
+
securityGroups: explicitSecurityGroups
|
|
234
|
+
})
|
|
153
235
|
});
|
|
154
236
|
}
|
|
237
|
+
if (serviceProps.cloudMapService !== undefined) {
|
|
238
|
+
const isSrv = serviceProps.cloudMapDnsRecordType === "SRV";
|
|
239
|
+
const isHostOrBridge = taskDefinition.networkMode === NetworkMode.HOST ||
|
|
240
|
+
taskDefinition.networkMode === NetworkMode.BRIDGE;
|
|
241
|
+
// AWS forbids A-typed serviceRegistries under HOST/BRIDGE networkMode.
|
|
242
|
+
if (isHostOrBridge && !isSrv) {
|
|
243
|
+
throw new Error(`Service '${serviceName}': cloudMapDnsRecordType '${serviceProps.cloudMapDnsRecordType ?? "A"}' ` +
|
|
244
|
+
`is not supported with networkMode '${taskDefinition.networkMode}'. ` +
|
|
245
|
+
'Use dnsRecordType: "SRV" or change networkMode to AWS_VPC.');
|
|
246
|
+
}
|
|
247
|
+
const primaryWithPort = serviceProps.containers.find((c) => c.port !== undefined);
|
|
248
|
+
const primaryWithMappings = serviceProps.containers.find((c) => c.portMappings !== undefined &&
|
|
249
|
+
c.portMappings[0]?.containerPort !== undefined);
|
|
250
|
+
const primary = primaryWithPort ?? primaryWithMappings;
|
|
251
|
+
const primaryPort = primaryWithPort?.port ??
|
|
252
|
+
primaryWithMappings?.portMappings?.[0]?.containerPort;
|
|
253
|
+
if (isSrv && (primary === undefined || primaryPort === undefined)) {
|
|
254
|
+
throw new Error(`Service '${serviceName}': cloudMapDnsRecordType: "SRV" requires at ` +
|
|
255
|
+
"least one container with a port — SRV records carry the published " +
|
|
256
|
+
"port and cannot be registered without one.");
|
|
257
|
+
}
|
|
258
|
+
if (primary !== undefined && primaryPort !== undefined) {
|
|
259
|
+
const containerDef = taskDefinition.findContainer(primary.name);
|
|
260
|
+
if (containerDef === undefined) {
|
|
261
|
+
throw new Error(`Service '${serviceName}': could not resolve container '${primary.name}' ` +
|
|
262
|
+
"from task definition for Cloud Map registration.");
|
|
263
|
+
}
|
|
264
|
+
service.associateCloudMapService({
|
|
265
|
+
service: serviceProps.cloudMapService,
|
|
266
|
+
container: containerDef,
|
|
267
|
+
containerPort: primaryPort
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
service.associateCloudMapService({
|
|
272
|
+
service: serviceProps.cloudMapService
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
155
276
|
new CfnOutput(ctx.scope, `${ctx.outputName}${toPascalCase(serviceName)}ServiceArn`, {
|
|
156
277
|
key: `${ctx.outputName}${toPascalCase(serviceName)}ServiceArn`,
|
|
157
278
|
exportName: `${ctx.props.clusterName}${serviceName}ServiceArn`,
|
|
@@ -164,12 +285,13 @@ export function createService(ctx, serviceName, serviceProps, taskDefinition, as
|
|
|
164
285
|
* Adds auto-scaling to an ECS service based on CPU or memory utilisation.
|
|
165
286
|
*/
|
|
166
287
|
export function addServiceScaling(ctx, serviceName, serviceProps, service) {
|
|
288
|
+
const desiredCount = serviceProps.desiredCount ?? DEFAULT_DESIRED_COUNT;
|
|
167
289
|
const scalableTarget = new ScalableTarget(ctx.scope, `${serviceName}ScalableTarget`, {
|
|
168
290
|
serviceNamespace: ServiceNamespace.ECS,
|
|
169
291
|
resourceId: `service/${ctx.cluster.clusterName}/${service.serviceName}`,
|
|
170
292
|
scalableDimension: "ecs:service:DesiredCount",
|
|
171
|
-
minCapacity: serviceProps.minCapacity ??
|
|
172
|
-
maxCapacity: serviceProps.maxCapacity ??
|
|
293
|
+
minCapacity: serviceProps.minCapacity ?? desiredCount,
|
|
294
|
+
maxCapacity: serviceProps.maxCapacity ?? Math.max(desiredCount + 1, 3)
|
|
173
295
|
});
|
|
174
296
|
return new TargetTrackingScalingPolicy(ctx.scope, `${serviceName}ScalingPolicy`, {
|
|
175
297
|
scalingTarget: scalableTarget,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { FargateTaskDefinition, Ec2TaskDefinition, type ContainerDefinition } from "aws-cdk-lib/aws-ecs";
|
|
1
|
+
import { FargateTaskDefinition, Ec2TaskDefinition, NetworkMode, type ContainerDefinition } from "aws-cdk-lib/aws-ecs";
|
|
2
|
+
import type { Construct } from "constructs";
|
|
2
3
|
import { type Role } from "aws-cdk-lib/aws-iam";
|
|
3
4
|
import type { EcsConstructContext } from "./ecsContext.js";
|
|
4
5
|
import type { EcsClusterProps, EcsServiceProps, EcsCapacityProvider } from "./ecsTypes.js";
|
|
@@ -24,6 +25,35 @@ export declare function collectSecretsManagerSecretNames(props: EcsClusterProps,
|
|
|
24
25
|
*/
|
|
25
26
|
export declare function deriveSsmSecretsPath(props: EcsClusterProps, serviceName: string, explicitPath?: string): string;
|
|
26
27
|
export declare function createTaskDefinition(ctx: EcsConstructContext, serviceName: string, serviceProps: EcsServiceProps, executionRole: Role, taskRole: Role): FargateTaskDefinition | Ec2TaskDefinition;
|
|
28
|
+
/**
|
|
29
|
+
* Routing point for `Ec2TaskDefinition`s used by EventBridge-triggered scheduled
|
|
30
|
+
* tasks (per `EcsClusterConfig.scheduledTasks`). Pattern-layer callers MUST
|
|
31
|
+
* reach `Ec2TaskDefinition` through this wrapper rather than instantiating the
|
|
32
|
+
* raw CDK class — a future SOC2-relevant default (encryption, retention,
|
|
33
|
+
* uniform tags) added here propagates to every scheduled-task task def at once.
|
|
34
|
+
*
|
|
35
|
+
* Compare with `createTaskDefinition()` above, which is the per-service path;
|
|
36
|
+
* this is the per-schedule path and intentionally kept narrow (no execution /
|
|
37
|
+
* task roles — scheduled tasks use task-definition defaults).
|
|
38
|
+
*/
|
|
39
|
+
export declare function createScheduledTaskDefinition(scope: Construct, id: string, props: {
|
|
40
|
+
family: string;
|
|
41
|
+
networkMode?: NetworkMode;
|
|
42
|
+
}): Ec2TaskDefinition;
|
|
43
|
+
/**
|
|
44
|
+
* Routing point for `FargateTaskDefinition`s used by the lifecycle-hook
|
|
45
|
+
* migration runner when `migrations.separateTaskDef` is set. Synthesises a
|
|
46
|
+
* dedicated Fargate task definition with caller-supplied CPU/memory and the
|
|
47
|
+
* supplied execution + task roles. Runtime platform pinned to ARM64/Linux to
|
|
48
|
+
* match the service task def default.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createMigrationTaskDefinition(scope: Construct, id: string, props: {
|
|
51
|
+
family: string;
|
|
52
|
+
cpu: number;
|
|
53
|
+
memoryLimitMiB: number;
|
|
54
|
+
executionRole: Role;
|
|
55
|
+
taskRole: Role;
|
|
56
|
+
}): FargateTaskDefinition;
|
|
27
57
|
export declare function addContainersToTask(ctx: EcsConstructContext, serviceName: string, serviceProps: EcsServiceProps, taskDefinition: FargateTaskDefinition | Ec2TaskDefinition): {
|
|
28
58
|
containers: ContainerDefinition[];
|
|
29
59
|
primaryContainer?: ContainerDefinition;
|