@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
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PersistentDataVolume LAUNCHING handler. Inlined into a Lambda at synth
|
|
3
|
+
* time by persistentDataVolume.ts. Triggered by an SQS queue fed by an
|
|
4
|
+
* EventBridge rule subscribed to the ASG EC2_INSTANCE_LAUNCHING lifecycle
|
|
5
|
+
* event on the default bus.
|
|
6
|
+
*
|
|
7
|
+
* Wire format: the SQS message body is the EventBridge event envelope
|
|
8
|
+
* `{ version, "detail-type", source, account, time, region, resources,
|
|
9
|
+
* detail: { LifecycleActionToken, AutoScalingGroupName,
|
|
10
|
+
* LifecycleHookName, EC2InstanceId, LifecycleTransition,
|
|
11
|
+
* NotificationMetadata, ... } }`.
|
|
12
|
+
* The lifecycle payload lives at `body.detail`. Records whose envelope does
|
|
13
|
+
* not match (wrong source, missing detail, non-Lifecycle Action detail-type)
|
|
14
|
+
* are logged and skipped — they shouldn't reach this queue but a defensive
|
|
15
|
+
* guard is cheap and avoids crashing the loop on stray test events.
|
|
16
|
+
*
|
|
17
|
+
* Behaviour:
|
|
18
|
+
* 1. Unwrap the EventBridge envelope; reject non-lifecycle records.
|
|
19
|
+
* 2. DescribeVolumes filtered by THREE tags: `tag:fjall:Lifecycle ==
|
|
20
|
+
* data-volume` AND `tag:fjall:OwnerLogicalId == OWNER_LOGICAL_ID` AND
|
|
21
|
+
* `tag:fjall:StackId == STACK_ID`. The StackId filter discriminates
|
|
22
|
+
* this deploy's volume from any prior-iteration orphans (CREATE_ROLLBACK
|
|
23
|
+
* failures, aborted destroys) that share the OwnerLogicalId but carry
|
|
24
|
+
* a stale StackId. No match → ABANDON so the ASG kills the new instance
|
|
25
|
+
* and retries; multiple matches → ABANDON and surface to the
|
|
26
|
+
* Lambda-Errors alarm (a tag-uniqueness invariant violation; silently
|
|
27
|
+
* picking one risks attaching the wrong data and requires operator
|
|
28
|
+
* investigation).
|
|
29
|
+
* 3. AttachVolume(Device: DEVICE_NAME, InstanceId, VolumeId).
|
|
30
|
+
* 4. Poll DescribeVolumes until State === "in-use" AND the matching
|
|
31
|
+
* Attachments[].State === "attached", deadline 90s. Timeout → ABANDON.
|
|
32
|
+
* 5. CompleteLifecycleAction(CONTINUE) on success.
|
|
33
|
+
*
|
|
34
|
+
* The Lambda is paired with the TERMINATING-side DetachVolume branch in
|
|
35
|
+
* ec2GracefulTerminationLambda.source.cjs. The two Lambdas form a closed
|
|
36
|
+
* re-attach contract: TERMINATING detaches the tagged volume from the
|
|
37
|
+
* outgoing instance, LAUNCHING (this file) re-attaches it to the incoming
|
|
38
|
+
* instance.
|
|
39
|
+
*
|
|
40
|
+
* CommonJS rather than ESM because Code.fromInline lands the source as
|
|
41
|
+
* `index.js`, which Lambda treats as CommonJS by default.
|
|
42
|
+
*/
|
|
43
|
+
const {
|
|
44
|
+
EC2Client,
|
|
45
|
+
DescribeVolumesCommand,
|
|
46
|
+
AttachVolumeCommand
|
|
47
|
+
} = require("@aws-sdk/client-ec2");
|
|
48
|
+
const {
|
|
49
|
+
AutoScalingClient,
|
|
50
|
+
CompleteLifecycleActionCommand
|
|
51
|
+
} = require("@aws-sdk/client-auto-scaling");
|
|
52
|
+
|
|
53
|
+
const ATTACH_POLL_INTERVAL_MS = 5_000;
|
|
54
|
+
// 90s attach deadline. AWS volume attach is usually < 30s; the bootstrap
|
|
55
|
+
// script waits 60s for /dev/xvdf so the hook MUST complete inside that.
|
|
56
|
+
const ATTACH_DEADLINE_MS = 90_000;
|
|
57
|
+
const TAG_OWNER_LOGICAL_ID = "fjall:OwnerLogicalId";
|
|
58
|
+
const TAG_LIFECYCLE = "fjall:Lifecycle";
|
|
59
|
+
const TAG_LIFECYCLE_VALUE = "data-volume";
|
|
60
|
+
const TAG_STACK_ID = "fjall:StackId";
|
|
61
|
+
|
|
62
|
+
function errMessage(err) {
|
|
63
|
+
return err && err.message ? err.message : String(err);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let _ec2;
|
|
67
|
+
let _asg;
|
|
68
|
+
function getEc2() {
|
|
69
|
+
if (!_ec2) _ec2 = new EC2Client({});
|
|
70
|
+
return _ec2;
|
|
71
|
+
}
|
|
72
|
+
function getAsg() {
|
|
73
|
+
if (!_asg) _asg = new AutoScalingClient({});
|
|
74
|
+
return _asg;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseLifecycleMessage(record) {
|
|
78
|
+
const envelope = JSON.parse(record.body);
|
|
79
|
+
const detail = envelope && envelope.detail;
|
|
80
|
+
const source = envelope && envelope.source;
|
|
81
|
+
const detailType = envelope && envelope["detail-type"];
|
|
82
|
+
const valid =
|
|
83
|
+
detail !== undefined &&
|
|
84
|
+
detail !== null &&
|
|
85
|
+
typeof detail === "object" &&
|
|
86
|
+
source === "aws.autoscaling" &&
|
|
87
|
+
typeof detailType === "string" &&
|
|
88
|
+
detailType.endsWith("Lifecycle Action");
|
|
89
|
+
if (!valid) {
|
|
90
|
+
return { valid: false };
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
valid: true,
|
|
94
|
+
instanceId: detail.EC2InstanceId,
|
|
95
|
+
asgName: detail.AutoScalingGroupName,
|
|
96
|
+
actionToken: detail.LifecycleActionToken,
|
|
97
|
+
hookName: detail.LifecycleHookName,
|
|
98
|
+
isTest: false
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function readEnv(env, name) {
|
|
103
|
+
const value = env[name];
|
|
104
|
+
if (typeof value !== "string" || value === "") return undefined;
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function findOwnedVolume(ec2Client, ownerLogicalId, stackId) {
|
|
109
|
+
const result = await ec2Client.send(
|
|
110
|
+
new DescribeVolumesCommand({
|
|
111
|
+
Filters: [
|
|
112
|
+
{ Name: `tag:${TAG_OWNER_LOGICAL_ID}`, Values: [ownerLogicalId] },
|
|
113
|
+
{ Name: `tag:${TAG_LIFECYCLE}`, Values: [TAG_LIFECYCLE_VALUE] },
|
|
114
|
+
{ Name: `tag:${TAG_STACK_ID}`, Values: [stackId] }
|
|
115
|
+
]
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
const volumes = result.Volumes || [];
|
|
119
|
+
return volumes;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function pollUntilAttached(
|
|
123
|
+
ec2Client,
|
|
124
|
+
volumeId,
|
|
125
|
+
instanceId,
|
|
126
|
+
sleepFn,
|
|
127
|
+
nowFn
|
|
128
|
+
) {
|
|
129
|
+
const deadline = nowFn() + ATTACH_DEADLINE_MS;
|
|
130
|
+
while (nowFn() < deadline) {
|
|
131
|
+
const desc = await ec2Client.send(
|
|
132
|
+
new DescribeVolumesCommand({ VolumeIds: [volumeId] })
|
|
133
|
+
);
|
|
134
|
+
const volume =
|
|
135
|
+
desc.Volumes && desc.Volumes[0] ? desc.Volumes[0] : undefined;
|
|
136
|
+
if (volume) {
|
|
137
|
+
const attachment = (volume.Attachments || []).find(
|
|
138
|
+
(a) => a.InstanceId === instanceId
|
|
139
|
+
);
|
|
140
|
+
if (volume.State === "in-use" && attachment?.State === "attached") {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
await sleepFn(ATTACH_POLL_INTERVAL_MS);
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function completeLifecycle(
|
|
150
|
+
asgClient,
|
|
151
|
+
asgName,
|
|
152
|
+
hookName,
|
|
153
|
+
actionToken,
|
|
154
|
+
result
|
|
155
|
+
) {
|
|
156
|
+
await asgClient.send(
|
|
157
|
+
new CompleteLifecycleActionCommand({
|
|
158
|
+
AutoScalingGroupName: asgName,
|
|
159
|
+
LifecycleHookName: hookName,
|
|
160
|
+
LifecycleActionToken: actionToken,
|
|
161
|
+
LifecycleActionResult: result
|
|
162
|
+
})
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function processRecord(record, deps) {
|
|
167
|
+
const ec2Client = (deps && deps.ec2Client) || getEc2();
|
|
168
|
+
const asgClient = (deps && deps.asgClient) || getAsg();
|
|
169
|
+
const sleepFn =
|
|
170
|
+
(deps && deps.sleep) ||
|
|
171
|
+
((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
172
|
+
const nowFn = (deps && deps.now) || (() => Date.now());
|
|
173
|
+
const env = (deps && deps.env) || process.env;
|
|
174
|
+
const log =
|
|
175
|
+
(deps && deps.log) ||
|
|
176
|
+
((msg, fields) => {
|
|
177
|
+
console.log(JSON.stringify({ message: msg, ...(fields || {}) }));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const parsed = parseLifecycleMessage(record);
|
|
181
|
+
if (!parsed.valid) {
|
|
182
|
+
log("Record is not an aws.autoscaling Lifecycle Action; skipping", {
|
|
183
|
+
bodyPreview: record.body ? String(record.body).slice(0, 200) : undefined
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const { instanceId, asgName, actionToken, hookName } = parsed;
|
|
188
|
+
|
|
189
|
+
const ownerLogicalId = readEnv(env, "OWNER_LOGICAL_ID");
|
|
190
|
+
const stackId = readEnv(env, "STACK_ID");
|
|
191
|
+
const deviceName = readEnv(env, "DEVICE_NAME");
|
|
192
|
+
if (
|
|
193
|
+
ownerLogicalId === undefined ||
|
|
194
|
+
stackId === undefined ||
|
|
195
|
+
deviceName === undefined
|
|
196
|
+
) {
|
|
197
|
+
log("OWNER_LOGICAL_ID, STACK_ID or DEVICE_NAME unset; abandoning", {
|
|
198
|
+
asgName,
|
|
199
|
+
instanceId
|
|
200
|
+
});
|
|
201
|
+
await completeLifecycle(
|
|
202
|
+
asgClient,
|
|
203
|
+
asgName,
|
|
204
|
+
hookName,
|
|
205
|
+
actionToken,
|
|
206
|
+
"ABANDON"
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let volumes;
|
|
212
|
+
try {
|
|
213
|
+
volumes = await findOwnedVolume(ec2Client, ownerLogicalId, stackId);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
log("DescribeVolumes failed; abandoning", {
|
|
216
|
+
asgName,
|
|
217
|
+
instanceId,
|
|
218
|
+
error: errMessage(err)
|
|
219
|
+
});
|
|
220
|
+
await completeLifecycle(
|
|
221
|
+
asgClient,
|
|
222
|
+
asgName,
|
|
223
|
+
hookName,
|
|
224
|
+
actionToken,
|
|
225
|
+
"ABANDON"
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (volumes.length === 0) {
|
|
231
|
+
log("No tagged data volume found; abandoning launch", {
|
|
232
|
+
ownerLogicalId,
|
|
233
|
+
instanceId
|
|
234
|
+
});
|
|
235
|
+
await completeLifecycle(
|
|
236
|
+
asgClient,
|
|
237
|
+
asgName,
|
|
238
|
+
hookName,
|
|
239
|
+
actionToken,
|
|
240
|
+
"ABANDON"
|
|
241
|
+
);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (volumes.length > 1) {
|
|
246
|
+
log(
|
|
247
|
+
"Multiple tagged data volumes found; abandoning launch — tag-uniqueness invariant violated",
|
|
248
|
+
{
|
|
249
|
+
ownerLogicalId,
|
|
250
|
+
volumeIds: volumes.map((v) => v.VolumeId)
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
await completeLifecycle(
|
|
254
|
+
asgClient,
|
|
255
|
+
asgName,
|
|
256
|
+
hookName,
|
|
257
|
+
actionToken,
|
|
258
|
+
"ABANDON"
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const volume = volumes[0];
|
|
264
|
+
const volumeId = volume.VolumeId;
|
|
265
|
+
|
|
266
|
+
// Already attached to the launching instance (e.g. previous Lambda invocation
|
|
267
|
+
// succeeded before the hook timeout fired and ASG re-fired the message).
|
|
268
|
+
const existingAttachment = (volume.Attachments || []).find(
|
|
269
|
+
(a) => a.InstanceId === instanceId && a.State === "attached"
|
|
270
|
+
);
|
|
271
|
+
if (existingAttachment !== undefined) {
|
|
272
|
+
log("Volume already attached to launching instance; completing CONTINUE", {
|
|
273
|
+
volumeId,
|
|
274
|
+
instanceId
|
|
275
|
+
});
|
|
276
|
+
await completeLifecycle(
|
|
277
|
+
asgClient,
|
|
278
|
+
asgName,
|
|
279
|
+
hookName,
|
|
280
|
+
actionToken,
|
|
281
|
+
"CONTINUE"
|
|
282
|
+
);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
await ec2Client.send(
|
|
288
|
+
new AttachVolumeCommand({
|
|
289
|
+
VolumeId: volumeId,
|
|
290
|
+
InstanceId: instanceId,
|
|
291
|
+
Device: deviceName
|
|
292
|
+
})
|
|
293
|
+
);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
log("AttachVolume failed; abandoning", {
|
|
296
|
+
volumeId,
|
|
297
|
+
instanceId,
|
|
298
|
+
error: errMessage(err)
|
|
299
|
+
});
|
|
300
|
+
await completeLifecycle(
|
|
301
|
+
asgClient,
|
|
302
|
+
asgName,
|
|
303
|
+
hookName,
|
|
304
|
+
actionToken,
|
|
305
|
+
"ABANDON"
|
|
306
|
+
);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const attached = await pollUntilAttached(
|
|
311
|
+
ec2Client,
|
|
312
|
+
volumeId,
|
|
313
|
+
instanceId,
|
|
314
|
+
sleepFn,
|
|
315
|
+
nowFn
|
|
316
|
+
);
|
|
317
|
+
if (!attached) {
|
|
318
|
+
log(
|
|
319
|
+
"Volume attach did not reach in-use/attached within deadline; abandoning",
|
|
320
|
+
{
|
|
321
|
+
volumeId,
|
|
322
|
+
instanceId,
|
|
323
|
+
deadlineMs: ATTACH_DEADLINE_MS
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
await completeLifecycle(
|
|
327
|
+
asgClient,
|
|
328
|
+
asgName,
|
|
329
|
+
hookName,
|
|
330
|
+
actionToken,
|
|
331
|
+
"ABANDON"
|
|
332
|
+
);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
log("Volume attached successfully; completing CONTINUE", {
|
|
337
|
+
volumeId,
|
|
338
|
+
instanceId
|
|
339
|
+
});
|
|
340
|
+
await completeLifecycle(
|
|
341
|
+
asgClient,
|
|
342
|
+
asgName,
|
|
343
|
+
hookName,
|
|
344
|
+
actionToken,
|
|
345
|
+
"CONTINUE"
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
exports.handler = async (event) => {
|
|
350
|
+
const records = (event && event.Records) || [];
|
|
351
|
+
for (const record of records) {
|
|
352
|
+
try {
|
|
353
|
+
await processRecord(record);
|
|
354
|
+
} catch (err) {
|
|
355
|
+
console.error(
|
|
356
|
+
JSON.stringify({
|
|
357
|
+
message:
|
|
358
|
+
"PersistentDataVolume LAUNCHING handler crashed; attempting ABANDON",
|
|
359
|
+
error: errMessage(err)
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
try {
|
|
363
|
+
const parsed = parseLifecycleMessage(record);
|
|
364
|
+
if (!parsed.valid) continue;
|
|
365
|
+
await completeLifecycle(
|
|
366
|
+
getAsg(),
|
|
367
|
+
parsed.asgName,
|
|
368
|
+
parsed.hookName,
|
|
369
|
+
parsed.actionToken,
|
|
370
|
+
"ABANDON"
|
|
371
|
+
);
|
|
372
|
+
} catch (innerErr) {
|
|
373
|
+
console.error(
|
|
374
|
+
JSON.stringify({
|
|
375
|
+
message: "CompleteLifecycleAction also failed",
|
|
376
|
+
error: errMessage(innerErr)
|
|
377
|
+
})
|
|
378
|
+
);
|
|
379
|
+
throw err;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
exports._internals = {
|
|
386
|
+
parseLifecycleMessage,
|
|
387
|
+
readEnv,
|
|
388
|
+
findOwnedVolume,
|
|
389
|
+
pollUntilAttached,
|
|
390
|
+
completeLifecycle,
|
|
391
|
+
processRecord,
|
|
392
|
+
ATTACH_POLL_INTERVAL_MS,
|
|
393
|
+
ATTACH_DEADLINE_MS,
|
|
394
|
+
TAG_OWNER_LOGICAL_ID,
|
|
395
|
+
TAG_LIFECYCLE,
|
|
396
|
+
TAG_LIFECYCLE_VALUE,
|
|
397
|
+
TAG_STACK_ID
|
|
398
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Construct } from "constructs";
|
|
2
|
+
export interface SamApplicationProps {
|
|
3
|
+
readonly applicationId: string;
|
|
4
|
+
readonly semanticVersion: string;
|
|
5
|
+
readonly parameters: Record<string, string>;
|
|
6
|
+
readonly description?: string;
|
|
7
|
+
readonly costAllocationService: string;
|
|
8
|
+
readonly costAllocationDomain: string;
|
|
9
|
+
readonly costAllocationEnvironment?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class SamApplication extends Construct {
|
|
12
|
+
private readonly application;
|
|
13
|
+
readonly description: string;
|
|
14
|
+
constructor(scope: Construct, id: string, props: SamApplicationProps);
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Tags } from "aws-cdk-lib";
|
|
2
|
+
import { CfnApplication } from "aws-cdk-lib/aws-sam";
|
|
3
|
+
import { Construct } from "constructs";
|
|
4
|
+
import { applyCostAllocationTags } from "../../../utils/costAllocationTags.js";
|
|
5
|
+
export class SamApplication extends Construct {
|
|
6
|
+
application;
|
|
7
|
+
description;
|
|
8
|
+
constructor(scope, id, props) {
|
|
9
|
+
super(scope, id);
|
|
10
|
+
this.description =
|
|
11
|
+
props.description ??
|
|
12
|
+
`Fjall-managed SAR application: ${props.applicationId}`;
|
|
13
|
+
this.application = new CfnApplication(this, "Application", {
|
|
14
|
+
location: {
|
|
15
|
+
applicationId: props.applicationId,
|
|
16
|
+
semanticVersion: props.semanticVersion
|
|
17
|
+
},
|
|
18
|
+
parameters: props.parameters
|
|
19
|
+
});
|
|
20
|
+
Tags.of(this).add("fjall:description", this.description);
|
|
21
|
+
applyCostAllocationTags(this, {
|
|
22
|
+
service: props.costAllocationService,
|
|
23
|
+
domain: props.costAllocationDomain,
|
|
24
|
+
environment: props.costAllocationEnvironment
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/** Database name created at ClickHouse bootstrap; consumed by BACKUP DATABASE,
|
|
2
|
+
* OPTIMIZE TABLE, and the DatabaseName CfnOutput so all four sites share one source. */
|
|
3
|
+
export declare const CLICKHOUSE_DATABASE_NAME = "analytics";
|
|
4
|
+
/** Default EC2 instance type for ClickHouse (Graviton3 — fixed-spec cores).
|
|
5
|
+
*
|
|
6
|
+
* `m7g.medium` (1 vCPU sustained, 4 GiB) chosen over `t4g.medium` (2 vCPU
|
|
7
|
+
* burst, 4 GiB) because CH's steady-state merge/scan/aggregation profile is
|
|
8
|
+
* not bursty — burst credits run out under the per-5min metrics ingest plus
|
|
9
|
+
* the OPTIMIZE FINAL sidecar, leaving the box throttled at baseline (10–20%
|
|
10
|
+
* of 2 vCPU) for the rest of the hour. Graviton3 is also ~25% faster on
|
|
11
|
+
* columnar scans than Graviton2.
|
|
12
|
+
*
|
|
13
|
+
* Settings are tuned for 1 vCPU sustained (max_threads=1 across all profiles,
|
|
14
|
+
* max_concurrent_queries=4) — see clickhouseUserData.ts. Bumping to a larger
|
|
15
|
+
* instance MUST be paired with raising those thread/concurrency caps.
|
|
16
|
+
*
|
|
17
|
+
* Next size up: `m7g.large` (2 vCPU, 8 GiB) for sustained workloads, or
|
|
18
|
+
* `r7g.medium` (1 vCPU, 8 GiB) when memory-bound. Set via
|
|
19
|
+
* `clickhouseInstanceType` CDK context or the `instanceType` prop, no code
|
|
20
|
+
* change needed (see clickhouseDatabase.ts:162). */
|
|
21
|
+
export declare const DEFAULT_CLICKHOUSE_INSTANCE_TYPE = "m7g.medium";
|
|
22
|
+
/** ClickHouse container image. Explicit `docker.io/` prefix is required so
|
|
23
|
+
* string-form consumers in `ecsImages.ts#getContainerImage()` route through
|
|
24
|
+
* `ContainerImage.fromRegistry(...)` instead of the ECR-repo fallback (which
|
|
25
|
+
* would otherwise append a service-tag suffix and produce a double-colon
|
|
26
|
+
* malformed image reference).
|
|
27
|
+
*
|
|
28
|
+
* Pin the full patch (not the floating `26.3`) so prod and local containers
|
|
29
|
+
* always run the same digest. The floating tag bit us once when CH 26.3.10
|
|
30
|
+
* tightened config.xml validation overnight while local Docker caches still
|
|
31
|
+
* held the prior patch — keep this pin in lockstep with
|
|
32
|
+
* `webapp/docker-compose.yaml`.
|
|
33
|
+
*
|
|
34
|
+
* Default Ubuntu (glibc) variant chosen over `-alpine` (musl): musl's
|
|
35
|
+
* threading/NPTL implementation is materially slower under CH's per-merge
|
|
36
|
+
* / per-query-stage thread fan-out, and musl's 128 KB default thread stack
|
|
37
|
+
* has a recurring history of stack-overflow incidents on deep query plans.
|
|
38
|
+
* Image size is ~250 MB larger but irrelevant on a single-instance EC2 ASG
|
|
39
|
+
* that pulls once per launch. Upstream CH CI runs its full perf + stress
|
|
40
|
+
* matrix on the Ubuntu build; Alpine is community-tier coverage. */
|
|
41
|
+
export declare const CLICKHOUSE_IMAGE = "docker.io/clickhouse/clickhouse-server:26.3.10.60";
|
|
42
|
+
/** EBS volume configuration. */
|
|
43
|
+
export declare const CLICKHOUSE_EBS_VOLUME_SIZE_GB = 80;
|
|
44
|
+
export declare const CLICKHOUSE_EBS_IOPS = 3000;
|
|
45
|
+
export declare const CLICKHOUSE_EBS_THROUGHPUT_MBPS = 125;
|
|
46
|
+
/** ECS task resource allocation. m7g.medium ships with 4 GB; reserve 1 GB
|
|
47
|
+
* for the host (kernel + ECS agent + cloudwatch agent + IMDS) and give
|
|
48
|
+
* ClickHouse the remaining 3 GB. Stays right when bumping to m7g.large
|
|
49
|
+
* (8 GB) — raise this value in lockstep, or fall back to deriving from
|
|
50
|
+
* the instance type if we ever support multiple sizes. */
|
|
51
|
+
export declare const CLICKHOUSE_TASK_MEMORY_MIB = 3072;
|
|
52
|
+
/** ClickHouse ports. */
|
|
53
|
+
export declare const CLICKHOUSE_HTTP_PORT = 8123;
|
|
54
|
+
export declare const CLICKHOUSE_NATIVE_PORT = 9000;
|
|
55
|
+
export declare const CLICKHOUSE_PROMETHEUS_PORT = 9363;
|
|
56
|
+
/** EBS device name for the data volume (must match user data script). */
|
|
57
|
+
export declare const CLICKHOUSE_EBS_DEVICE_NAME = "/dev/xvdf";
|
|
58
|
+
/** EBS mount path on the EC2 host. */
|
|
59
|
+
export declare const CLICKHOUSE_DATA_MOUNT_PATH = "/mnt/clickhouse-data";
|
|
60
|
+
/** Secrets Manager path prefix. */
|
|
61
|
+
export declare const CLICKHOUSE_SECRETS_PREFIX = "fjall/clickhouse";
|
|
62
|
+
/** Canonical Secrets Manager secret name for a ClickHouse user.
|
|
63
|
+
* Two sites compute this name and they MUST match:
|
|
64
|
+
* (a) the secret mint inside `createClickHouseUserSecret(...)` in
|
|
65
|
+
* `clickhouseDatabase.ts` — calls this helper directly;
|
|
66
|
+
* (b) the SSM live-reload script that fetches the admin password via
|
|
67
|
+
* `aws secretsmanager get-secret-value --secret-id` — also calls
|
|
68
|
+
* this helper directly.
|
|
69
|
+
* Drift between the two would silently break the live-reload path post-
|
|
70
|
+
* deploy on a users.d S3 change (no compile or test signal). */
|
|
71
|
+
export declare function clickHouseUserSecretName(userName: string): string;
|
|
72
|
+
/** ECS-injected env var name carrying a user's plaintext password.
|
|
73
|
+
* Canonical source is `@fjall/util/migration § userPasswordEnvName` — re-exported
|
|
74
|
+
* here so the construct-side import chain stays unchanged while the runner
|
|
75
|
+
* (`webapp/scripts/migration-runner.mjs`) and any future consumer share a single
|
|
76
|
+
* declaration. Coupled values per `.claude/rules/code-quality.md § "Coupled values:
|
|
77
|
+
* shared source at 2 occurrences"`. */
|
|
78
|
+
export { userPasswordEnvName } from "@fjall/util/migration";
|
|
79
|
+
/** Env var name carrying the SHA-256 of a user's plaintext password.
|
|
80
|
+
* The container entrypoint wrapper derives this name from the
|
|
81
|
+
* `USER_<NAME>_PASSWORD` env var injected by ECS `secretsImport`, then
|
|
82
|
+
* resolves `<password_sha256_hex from_env="..."/>` directives in
|
|
83
|
+
* `users.d/fjall.xml` against it at server start. */
|
|
84
|
+
export declare function userSha256EnvName(userName: string): string;
|
|
85
|
+
/** Canonical bash pipeline that hashes a ClickHouse user password.
|
|
86
|
+
* Invoked from the container entrypoint wrapper
|
|
87
|
+
* (`buildClickHouseEntrypointWrapper`) to derive `USER_<NAME>_SHA256` from
|
|
88
|
+
* the ECS-injected plaintext at server start.
|
|
89
|
+
* @param plaintextShellRef A quoted bash reference to the plaintext
|
|
90
|
+
* password (e.g. `"\${!var}"` for the wrapper's indirect deref). */
|
|
91
|
+
export declare function clickHousePasswordSha256Snippet(plaintextShellRef: string): string;
|
|
92
|
+
/** Tag identifying ClickHouse server ASG instances.
|
|
93
|
+
* Three sites carry this pair and they MUST match: (a) the `tags:` map on
|
|
94
|
+
* the ClickHouse EC2 instance (threaded through to ASG instance tags),
|
|
95
|
+
* (b) the SSM `Targets: [{ Key: "tag:<key>", Values: ["<value>"] }]` filter
|
|
96
|
+
* on the live-reload `AwsCustomResource`, and (c) the synth snapshot tests
|
|
97
|
+
* pinning the relationship. Drift between (a) and (b) silently routes the
|
|
98
|
+
* reload command to zero instances — the `InvocationDoesNotExist` failure
|
|
99
|
+
* is already suppressed by `ignoreErrorCodesMatching`, so the misroute
|
|
100
|
+
* emits no signal. */
|
|
101
|
+
export declare const CLICKHOUSE_SERVER_ROLE_TAG: {
|
|
102
|
+
readonly key: "ClickHouseRole";
|
|
103
|
+
readonly value: "server";
|
|
104
|
+
};
|
|
105
|
+
/** Shared secret generation options (all ClickHouse users share the same policy). */
|
|
106
|
+
export declare const CLICKHOUSE_SECRET_OPTIONS: {
|
|
107
|
+
readonly excludePunctuation: true;
|
|
108
|
+
readonly passwordLength: 32;
|
|
109
|
+
};
|
|
110
|
+
/** Health check configuration. */
|
|
111
|
+
export declare const CLICKHOUSE_HEALTH_CHECK: {
|
|
112
|
+
readonly INTERVAL_SECONDS: 30;
|
|
113
|
+
readonly TIMEOUT_SECONDS: 5;
|
|
114
|
+
readonly RETRIES: 3;
|
|
115
|
+
readonly START_PERIOD_SECONDS: 120;
|
|
116
|
+
};
|
|
117
|
+
/** Container stop timeout in seconds. ECS waits this long after SIGTERM
|
|
118
|
+
* before sending SIGKILL. ClickHouse needs the longer-than-default window
|
|
119
|
+
* (CDK default: 30s) to flush in-progress merges, finish writing parts,
|
|
120
|
+
* and close the data directory cleanly — premature SIGKILL on a 1 vCPU
|
|
121
|
+
* m7g.medium under merge load risks `max_suspicious_broken_parts` on the
|
|
122
|
+
* next start. ECS upper bound is 120s. */
|
|
123
|
+
export declare const CLICKHOUSE_STOP_TIMEOUT_SECONDS = 90;
|
|
124
|
+
/** OPTIMIZE TABLE FINAL schedule.
|
|
125
|
+
* RMT tables carry min_age_to_force_merge_seconds=600 so the engine already merges
|
|
126
|
+
* old parts within 10 min; this task is a safety net for MVs (no engine-level setting)
|
|
127
|
+
* and for ReplacingMergeTree dedup under skewed write patterns. 6 hours is sufficient. */
|
|
128
|
+
export declare const OPTIMISE_FINAL_SCHEDULE = "rate(6 hours)";
|
|
129
|
+
/** Tables requiring periodic OPTIMIZE FINAL (ReplacingMergeTree only).
|
|
130
|
+
* Keep in sync with REPLACING_MERGE_TREE_TABLES in
|
|
131
|
+
* webapp/app/.server/lib/clickhouse/tenantQuery/tableConstants.ts (auto-FINAL). */
|
|
132
|
+
export declare const REPLACING_MERGE_TREE_TABLES: readonly ["application_metrics", "cost_records", "log_fingerprints", "insights", "asset_inventory"];
|
|
133
|
+
/** Subdirectory on the EBS volume for server config files (must match CDK volume mount). */
|
|
134
|
+
export declare const CLICKHOUSE_CONFIG_SUBDIR = "server-config.d";
|
|
135
|
+
/** Subdirectory on the EBS volume for users config files (must match CDK volume mount). */
|
|
136
|
+
export declare const CLICKHOUSE_USERS_SUBDIR = "server-users.d";
|
|
137
|
+
/** Cloud Map service name (resolves to `clickhouse.<appName>.local` via the per-app namespace registered by `App.getInstance().registerService(...)`). */
|
|
138
|
+
export declare const CLICKHOUSE_CLOUDMAP_SERVICE_NAME = "clickhouse";
|
|
139
|
+
/** ECS container name for the ClickHouse server task. Threaded through both
|
|
140
|
+
* the `addContainer` call site and the SSM reload script (which uses
|
|
141
|
+
* `docker ps --filter "label=com.amazonaws.ecs.container-name=<name>"` to
|
|
142
|
+
* locate the running container). Drift between the two sites turns the
|
|
143
|
+
* live-reload path into a silent no-op without any compile/test signal,
|
|
144
|
+
* so this lives as a single source of truth. */
|
|
145
|
+
export declare const CLICKHOUSE_SERVER_CONTAINER_NAME = "clickhouse";
|
|
146
|
+
/** Materialised views that benefit from periodic OPTIMIZE to reduce part count at read time.
|
|
147
|
+
* These are not ReplacingMergeTree (no dedup needed) but un-merged parts force
|
|
148
|
+
* read-time aggregation which degrades query performance. */
|
|
149
|
+
export declare const OPTIMISE_MV_TABLES: readonly ["metrics_hourly_mv", "metrics_daily_mv", "response_time_quantiles_hourly_mv", "deployment_duration_quantiles_daily_mv", "log_severity_hourly_mv", "compliance_score_daily_mv", "ai_usage_daily_mv", "finding_daily_aggregate", "insight_pattern_dismissals"];
|
|
150
|
+
/** Resource allocation for the lightweight optimise task. */
|
|
151
|
+
export declare const OPTIMISE_TASK_MEMORY_MIB = 256;
|
|
152
|
+
export declare const OPTIMISE_TASK_CPU_UNITS = 256;
|
|
153
|
+
/** Automated backup schedule (daily 03:00 UTC — low-traffic window). */
|
|
154
|
+
export declare const BACKUP_SCHEDULE = "cron(0 3 * * ? *)";
|
|
155
|
+
/** Resource allocation for the backup task (lightweight — clickhouse-client only). */
|
|
156
|
+
export declare const BACKUP_TASK_MEMORY_MIB = 256;
|
|
157
|
+
export declare const BACKUP_TASK_CPU_UNITS = 256;
|
|
158
|
+
/** Backup object expiration: 14 days (retains 14 daily snapshots). */
|
|
159
|
+
export declare const BACKUP_RETENTION_DAYS = 14;
|