@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,139 @@
|
|
|
1
|
+
import { Duration } from "aws-cdk-lib";
|
|
2
|
+
import { Alarm, ComparisonOperator, TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
|
|
3
|
+
import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
|
|
4
|
+
import { Metric } from "aws-cdk-lib/aws-cloudwatch";
|
|
5
|
+
import { FilterPattern, MetricFilter } from "aws-cdk-lib/aws-logs";
|
|
6
|
+
import { ALARM_DEFAULTS, registerAlarm, buildAlarmDescription } from "./alarmDefaults.js";
|
|
7
|
+
const CLICKHOUSE_METRIC_NAMESPACE = "Fjall/ClickHouse";
|
|
8
|
+
/**
|
|
9
|
+
* Single-node ClickHouse posture alarms. Covers host-level CPU + (optional)
|
|
10
|
+
* memory and disk via the CloudWatch Agent metric namespace `CWAgent`, plus
|
|
11
|
+
* two log-driven alarms:
|
|
12
|
+
*
|
|
13
|
+
* - **Stuck merges** — `client.ts` polls `system.merges` every 5 min and logs
|
|
14
|
+
* `serverLogger.warn("ClickHouse", "Stuck merge detected")` when elapsed
|
|
15
|
+
* exceeds 30 min. The metric filter on the webapp log group emits a count
|
|
16
|
+
* metric per match; the alarm fires on Sum >= 1 over 5 min × 2 evaluations.
|
|
17
|
+
* - **Backup failures** — `AccessDenied` or `S3Exception` from the backup
|
|
18
|
+
* task's BACKUP DATABASE TO S3 statement. Closes the silent-failure mode
|
|
19
|
+
* that masked the original IAM-grant misconfiguration (see
|
|
20
|
+
* `designs/2026-04-27-clickhouse-backup-iam-role.md`).
|
|
21
|
+
*/
|
|
22
|
+
export function createClickHouseAlarms(props) {
|
|
23
|
+
const { scope, instanceRole, asgName, alarmTopic, webappLogGroup, backupTaskLogGroup, config = {} } = props;
|
|
24
|
+
const alarms = [];
|
|
25
|
+
const snsAction = new SnsAction(alarmTopic);
|
|
26
|
+
const cpuAlarm = new Alarm(scope, "ClickHouseCpuAlarm", {
|
|
27
|
+
alarmDescription: buildAlarmDescription("ClickHouse host CPU utilisation exceeds threshold", undefined),
|
|
28
|
+
metric: new Metric({
|
|
29
|
+
namespace: "AWS/EC2",
|
|
30
|
+
metricName: "CPUUtilization",
|
|
31
|
+
dimensionsMap: { AutoScalingGroupName: asgName },
|
|
32
|
+
period: ALARM_DEFAULTS.EVALUATION_PERIOD,
|
|
33
|
+
statistic: "Average"
|
|
34
|
+
}),
|
|
35
|
+
threshold: config.cpuThreshold ?? 90,
|
|
36
|
+
evaluationPeriods: 3,
|
|
37
|
+
datapointsToAlarm: 2,
|
|
38
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
39
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
40
|
+
});
|
|
41
|
+
registerAlarm(cpuAlarm, snsAction, alarms);
|
|
42
|
+
const memoryAlarm = new Alarm(scope, "ClickHouseMemoryAlarm", {
|
|
43
|
+
alarmDescription: buildAlarmDescription("ClickHouse host memory utilisation exceeds threshold (CWAgent)", undefined),
|
|
44
|
+
metric: new Metric({
|
|
45
|
+
namespace: "CWAgent",
|
|
46
|
+
metricName: "mem_used_percent",
|
|
47
|
+
dimensionsMap: { AutoScalingGroupName: asgName },
|
|
48
|
+
period: ALARM_DEFAULTS.EVALUATION_PERIOD,
|
|
49
|
+
statistic: "Average"
|
|
50
|
+
}),
|
|
51
|
+
threshold: config.memoryThreshold ?? 80,
|
|
52
|
+
evaluationPeriods: 3,
|
|
53
|
+
datapointsToAlarm: 2,
|
|
54
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
55
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
56
|
+
});
|
|
57
|
+
registerAlarm(memoryAlarm, snsAction, alarms);
|
|
58
|
+
const diskWarnAlarm = new Alarm(scope, "ClickHouseDiskWarnAlarm", {
|
|
59
|
+
alarmDescription: buildAlarmDescription("ClickHouse data volume above 70% used — plan growth response", undefined),
|
|
60
|
+
metric: new Metric({
|
|
61
|
+
namespace: "CWAgent",
|
|
62
|
+
metricName: "disk_used_percent",
|
|
63
|
+
dimensionsMap: { AutoScalingGroupName: asgName },
|
|
64
|
+
period: Duration.minutes(15),
|
|
65
|
+
statistic: "Average"
|
|
66
|
+
}),
|
|
67
|
+
threshold: config.diskWarnThreshold ?? 70,
|
|
68
|
+
evaluationPeriods: 2,
|
|
69
|
+
datapointsToAlarm: 2,
|
|
70
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
71
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
72
|
+
});
|
|
73
|
+
registerAlarm(diskWarnAlarm, snsAction, alarms);
|
|
74
|
+
const diskCriticalAlarm = new Alarm(scope, "ClickHouseDiskCriticalAlarm", {
|
|
75
|
+
alarmDescription: buildAlarmDescription("ClickHouse data volume above 85% used — imminent insert failures", undefined),
|
|
76
|
+
metric: new Metric({
|
|
77
|
+
namespace: "CWAgent",
|
|
78
|
+
metricName: "disk_used_percent",
|
|
79
|
+
dimensionsMap: { AutoScalingGroupName: asgName },
|
|
80
|
+
period: Duration.minutes(5),
|
|
81
|
+
statistic: "Average"
|
|
82
|
+
}),
|
|
83
|
+
threshold: config.diskCriticalThreshold ?? 85,
|
|
84
|
+
evaluationPeriods: 2,
|
|
85
|
+
datapointsToAlarm: 2,
|
|
86
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
87
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
88
|
+
});
|
|
89
|
+
registerAlarm(diskCriticalAlarm, snsAction, alarms);
|
|
90
|
+
const stuckMergeMetricName = "ClickHouseStuckMergeCount";
|
|
91
|
+
new MetricFilter(scope, "ClickHouseStuckMergeMetricFilter", {
|
|
92
|
+
logGroup: webappLogGroup,
|
|
93
|
+
metricNamespace: CLICKHOUSE_METRIC_NAMESPACE,
|
|
94
|
+
metricName: stuckMergeMetricName,
|
|
95
|
+
filterPattern: FilterPattern.literal('"Stuck merge detected"'),
|
|
96
|
+
metricValue: "1",
|
|
97
|
+
defaultValue: 0
|
|
98
|
+
});
|
|
99
|
+
const stuckMergeAlarm = new Alarm(scope, "ClickHouseStuckMergeAlarm", {
|
|
100
|
+
alarmDescription: buildAlarmDescription("ClickHouse merge stuck > 30 min — investigate parts pressure or replica health", undefined),
|
|
101
|
+
metric: new Metric({
|
|
102
|
+
namespace: CLICKHOUSE_METRIC_NAMESPACE,
|
|
103
|
+
metricName: stuckMergeMetricName,
|
|
104
|
+
period: Duration.minutes(5),
|
|
105
|
+
statistic: "Sum"
|
|
106
|
+
}),
|
|
107
|
+
threshold: 1,
|
|
108
|
+
evaluationPeriods: 2,
|
|
109
|
+
datapointsToAlarm: 2,
|
|
110
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
|
|
111
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
112
|
+
});
|
|
113
|
+
registerAlarm(stuckMergeAlarm, snsAction, alarms);
|
|
114
|
+
const backupFailureMetricName = "ClickHouseBackupFailureCount";
|
|
115
|
+
new MetricFilter(scope, "ClickHouseBackupFailureMetricFilter", {
|
|
116
|
+
logGroup: backupTaskLogGroup,
|
|
117
|
+
metricNamespace: CLICKHOUSE_METRIC_NAMESPACE,
|
|
118
|
+
metricName: backupFailureMetricName,
|
|
119
|
+
filterPattern: FilterPattern.anyTerm("AccessDenied", "S3Exception"),
|
|
120
|
+
metricValue: "1",
|
|
121
|
+
defaultValue: 0
|
|
122
|
+
});
|
|
123
|
+
const backupFailureAlarm = new Alarm(scope, "ClickHouseBackupFailureAlarm", {
|
|
124
|
+
alarmDescription: buildAlarmDescription(`ClickHouse BACKUP TO S3 emitted AccessDenied/S3Exception — verify instance role '${instanceRole.roleName}' grant on backup bucket`, undefined),
|
|
125
|
+
metric: new Metric({
|
|
126
|
+
namespace: CLICKHOUSE_METRIC_NAMESPACE,
|
|
127
|
+
metricName: backupFailureMetricName,
|
|
128
|
+
period: Duration.hours(1),
|
|
129
|
+
statistic: "Sum"
|
|
130
|
+
}),
|
|
131
|
+
threshold: 1,
|
|
132
|
+
evaluationPeriods: 1,
|
|
133
|
+
datapointsToAlarm: 1,
|
|
134
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
|
|
135
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
136
|
+
});
|
|
137
|
+
registerAlarm(backupFailureAlarm, snsAction, alarms);
|
|
138
|
+
return alarms;
|
|
139
|
+
}
|
|
@@ -2,3 +2,5 @@ export { ALARM_DEFAULTS, APPLICATION_ID_TAG_KEY, registerAlarm, tagAlarmsWithApp
|
|
|
2
2
|
export { createEcsServiceAlarms, type EcsServiceAlarmThresholds, type EcsServiceAlarmsProps } from "./ecsAlarms.js";
|
|
3
3
|
export { createRdsAlarms, type RdsAlarmThresholds, type RdsAlarmsProps } from "./rdsAlarms.js";
|
|
4
4
|
export { createLambdaAlarms, type LambdaAlarmThresholds, type LambdaAlarmsProps } from "./lambdaAlarms.js";
|
|
5
|
+
export { createScheduleAlarms, type ScheduleAlarmThresholds, type CreateScheduleAlarmsProps } from "./scheduleAlarms.js";
|
|
6
|
+
export { createClickHouseAlarms, type ClickHouseAlarmThresholds, type ClickHouseAlarmsProps } from "./clickhouseAlarms.js";
|
|
@@ -2,3 +2,5 @@ export { ALARM_DEFAULTS, APPLICATION_ID_TAG_KEY, registerAlarm, tagAlarmsWithApp
|
|
|
2
2
|
export { createEcsServiceAlarms } from "./ecsAlarms.js";
|
|
3
3
|
export { createRdsAlarms } from "./rdsAlarms.js";
|
|
4
4
|
export { createLambdaAlarms } from "./lambdaAlarms.js";
|
|
5
|
+
export { createScheduleAlarms } from "./scheduleAlarms.js";
|
|
6
|
+
export { createClickHouseAlarms } from "./clickhouseAlarms.js";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Alarm } from "aws-cdk-lib/aws-cloudwatch";
|
|
2
|
+
import type { IRule } from "aws-cdk-lib/aws-events";
|
|
3
|
+
import type { ITopic } from "aws-cdk-lib/aws-sns";
|
|
4
|
+
import type { Construct } from "constructs";
|
|
5
|
+
/**
|
|
6
|
+
* Tunable thresholds for the EventBridge schedule alarms. Lives here (NOT in
|
|
7
|
+
* `messaging/schedule.ts`) so the consumer module — `eventBridgeRule.ts` /
|
|
8
|
+
* `Schedule` — can re-export it without forming a circular import.
|
|
9
|
+
*/
|
|
10
|
+
export interface ScheduleAlarmThresholds {
|
|
11
|
+
failedInvocationsThreshold?: number;
|
|
12
|
+
throttledRulesThreshold?: number;
|
|
13
|
+
dlqFailureThreshold?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Multiplier applied to `expectedTicksPerHour` to derive the missed-tick
|
|
16
|
+
* floor. Default 0.8 — fires when observed matches drop below 80% of the
|
|
17
|
+
* expected cadence (silent disablement detector).
|
|
18
|
+
*/
|
|
19
|
+
matchedEventsLowFloorRatio?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface CreateScheduleAlarmsProps {
|
|
22
|
+
scope: Construct;
|
|
23
|
+
ruleName: string;
|
|
24
|
+
rule: IRule;
|
|
25
|
+
kind: "schedule" | "subscription";
|
|
26
|
+
/** Populated by `parseRateExpression(...)` — undefined for cron schedules and subscriptions. */
|
|
27
|
+
expectedTicksPerHour?: number;
|
|
28
|
+
config?: ScheduleAlarmThresholds;
|
|
29
|
+
alarmTopic: ITopic;
|
|
30
|
+
applicationId?: string;
|
|
31
|
+
dlqEnabled: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Provisions D13 alarms for an EventBridge rule. Returns the created alarms
|
|
35
|
+
* (each wired to the SNS topic for both ALARM and OK transitions and tagged
|
|
36
|
+
* with the application id for webhook routing).
|
|
37
|
+
*
|
|
38
|
+
* Alarm coverage:
|
|
39
|
+
* - `FailedInvocations` / `ThrottledRules` always fire.
|
|
40
|
+
* - `InvocationsFailedToBeSentToDlq` fires only when `dlqEnabled === true`.
|
|
41
|
+
* - `MatchedEvents` (missed-tick detector) fires only when
|
|
42
|
+
* `kind === "schedule"` AND `expectedTicksPerHour !== undefined` —
|
|
43
|
+
* i.e. rate expressions, not cron, not subscriptions. Uses
|
|
44
|
+
* `LessThanThreshold` + `treatMissingData: BREACHING` so a fully silent
|
|
45
|
+
* rule trips the alarm.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createScheduleAlarms(props: CreateScheduleAlarmsProps): Alarm[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Duration } from "aws-cdk-lib";
|
|
2
|
+
import { Alarm, ComparisonOperator, TreatMissingData, Metric } from "aws-cdk-lib/aws-cloudwatch";
|
|
3
|
+
import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
|
|
4
|
+
import { ALARM_DEFAULTS, registerAlarm, tagAlarmsWithApplicationId, buildAlarmDescription } from "./alarmDefaults.js";
|
|
5
|
+
const DEFAULT_THRESHOLDS = {
|
|
6
|
+
FAILED_INVOCATIONS: 1,
|
|
7
|
+
THROTTLED_RULES: 1,
|
|
8
|
+
DLQ_FAILURE: 1,
|
|
9
|
+
MATCHED_EVENTS_LOW_FLOOR_RATIO: 0.8
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Provisions D13 alarms for an EventBridge rule. Returns the created alarms
|
|
13
|
+
* (each wired to the SNS topic for both ALARM and OK transitions and tagged
|
|
14
|
+
* with the application id for webhook routing).
|
|
15
|
+
*
|
|
16
|
+
* Alarm coverage:
|
|
17
|
+
* - `FailedInvocations` / `ThrottledRules` always fire.
|
|
18
|
+
* - `InvocationsFailedToBeSentToDlq` fires only when `dlqEnabled === true`.
|
|
19
|
+
* - `MatchedEvents` (missed-tick detector) fires only when
|
|
20
|
+
* `kind === "schedule"` AND `expectedTicksPerHour !== undefined` —
|
|
21
|
+
* i.e. rate expressions, not cron, not subscriptions. Uses
|
|
22
|
+
* `LessThanThreshold` + `treatMissingData: BREACHING` so a fully silent
|
|
23
|
+
* rule trips the alarm.
|
|
24
|
+
*/
|
|
25
|
+
export function createScheduleAlarms(props) {
|
|
26
|
+
const { scope, ruleName, rule, kind, expectedTicksPerHour, config = {}, alarmTopic, applicationId, dlqEnabled } = props;
|
|
27
|
+
const alarms = [];
|
|
28
|
+
const snsAction = new SnsAction(alarmTopic);
|
|
29
|
+
const failedInvocationsThreshold = config.failedInvocationsThreshold ?? DEFAULT_THRESHOLDS.FAILED_INVOCATIONS;
|
|
30
|
+
const throttledRulesThreshold = config.throttledRulesThreshold ?? DEFAULT_THRESHOLDS.THROTTLED_RULES;
|
|
31
|
+
const dlqFailureThreshold = config.dlqFailureThreshold ?? DEFAULT_THRESHOLDS.DLQ_FAILURE;
|
|
32
|
+
const matchedEventsLowFloorRatio = config.matchedEventsLowFloorRatio ??
|
|
33
|
+
DEFAULT_THRESHOLDS.MATCHED_EVENTS_LOW_FLOOR_RATIO;
|
|
34
|
+
const ruleDimensions = { RuleName: rule.ruleName };
|
|
35
|
+
const failedInvocationsAlarm = new Alarm(scope, `${ruleName}FailedInvocationsAlarm`, {
|
|
36
|
+
alarmDescription: buildAlarmDescription(`EventBridge rule ${ruleName} FailedInvocations exceeds ${failedInvocationsThreshold}`, applicationId),
|
|
37
|
+
metric: new Metric({
|
|
38
|
+
namespace: "AWS/Events",
|
|
39
|
+
metricName: "FailedInvocations",
|
|
40
|
+
dimensionsMap: ruleDimensions,
|
|
41
|
+
period: ALARM_DEFAULTS.EVALUATION_PERIOD,
|
|
42
|
+
statistic: "Sum"
|
|
43
|
+
}),
|
|
44
|
+
threshold: failedInvocationsThreshold,
|
|
45
|
+
evaluationPeriods: 2,
|
|
46
|
+
datapointsToAlarm: 2,
|
|
47
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
48
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
49
|
+
});
|
|
50
|
+
registerAlarm(failedInvocationsAlarm, snsAction, alarms);
|
|
51
|
+
const throttledRulesAlarm = new Alarm(scope, `${ruleName}ThrottledRulesAlarm`, {
|
|
52
|
+
alarmDescription: buildAlarmDescription(`EventBridge rule ${ruleName} ThrottledRules exceeds ${throttledRulesThreshold}`, applicationId),
|
|
53
|
+
metric: new Metric({
|
|
54
|
+
namespace: "AWS/Events",
|
|
55
|
+
metricName: "ThrottledRules",
|
|
56
|
+
dimensionsMap: ruleDimensions,
|
|
57
|
+
period: ALARM_DEFAULTS.EVALUATION_PERIOD,
|
|
58
|
+
statistic: "Sum"
|
|
59
|
+
}),
|
|
60
|
+
threshold: throttledRulesThreshold,
|
|
61
|
+
evaluationPeriods: 2,
|
|
62
|
+
datapointsToAlarm: 2,
|
|
63
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
64
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
65
|
+
});
|
|
66
|
+
registerAlarm(throttledRulesAlarm, snsAction, alarms);
|
|
67
|
+
if (dlqEnabled) {
|
|
68
|
+
const dlqFailureAlarm = new Alarm(scope, `${ruleName}DlqFailureAlarm`, {
|
|
69
|
+
alarmDescription: buildAlarmDescription(`EventBridge rule ${ruleName} InvocationsFailedToBeSentToDlq exceeds ${dlqFailureThreshold}`, applicationId),
|
|
70
|
+
metric: new Metric({
|
|
71
|
+
namespace: "AWS/Events",
|
|
72
|
+
metricName: "InvocationsFailedToBeSentToDlq",
|
|
73
|
+
dimensionsMap: ruleDimensions,
|
|
74
|
+
period: Duration.minutes(5),
|
|
75
|
+
statistic: "Sum"
|
|
76
|
+
}),
|
|
77
|
+
threshold: dlqFailureThreshold,
|
|
78
|
+
evaluationPeriods: 1,
|
|
79
|
+
datapointsToAlarm: 1,
|
|
80
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
|
81
|
+
treatMissingData: TreatMissingData.NOT_BREACHING
|
|
82
|
+
});
|
|
83
|
+
registerAlarm(dlqFailureAlarm, snsAction, alarms);
|
|
84
|
+
}
|
|
85
|
+
if (kind === "schedule" && expectedTicksPerHour !== undefined) {
|
|
86
|
+
const floor = Math.max(1, Math.floor(expectedTicksPerHour * matchedEventsLowFloorRatio));
|
|
87
|
+
const matchedEventsAlarm = new Alarm(scope, `${ruleName}MatchedEventsAlarm`, {
|
|
88
|
+
alarmDescription: buildAlarmDescription(`EventBridge rule ${ruleName} expected >=${floor} ticks/hour, observed <=${Math.round(matchedEventsLowFloorRatio * 100)}% threshold = silent disablement detector`, applicationId),
|
|
89
|
+
metric: new Metric({
|
|
90
|
+
namespace: "AWS/Events",
|
|
91
|
+
metricName: "MatchedEvents",
|
|
92
|
+
dimensionsMap: ruleDimensions,
|
|
93
|
+
period: Duration.hours(1),
|
|
94
|
+
statistic: "Sum"
|
|
95
|
+
}),
|
|
96
|
+
threshold: floor,
|
|
97
|
+
evaluationPeriods: 1,
|
|
98
|
+
datapointsToAlarm: 1,
|
|
99
|
+
comparisonOperator: ComparisonOperator.LESS_THAN_THRESHOLD,
|
|
100
|
+
treatMissingData: TreatMissingData.BREACHING
|
|
101
|
+
});
|
|
102
|
+
registerAlarm(matchedEventsAlarm, snsAction, alarms);
|
|
103
|
+
}
|
|
104
|
+
tagAlarmsWithApplicationId(alarms, applicationId);
|
|
105
|
+
return alarms;
|
|
106
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import { Tags } from "aws-cdk-lib";
|
|
3
3
|
import { CrossAccountZoneDelegationRecord as CdkCrossAccountZoneDelegationRecord } from "aws-cdk-lib/aws-route53";
|
|
4
|
-
import {
|
|
4
|
+
import { applyCostAllocationTags } from "../../../utils/costAllocationTags.js";
|
|
5
5
|
// CDK's CrossAccountZoneDelegationRecord is a custom-resource Lambda: user-level tags
|
|
6
6
|
// on the record itself may not reach AWS. We tag the wrapping Fjall Construct for
|
|
7
7
|
// assertion purposes; create-path tags still propagate to child resources (Lambda,
|
|
@@ -20,8 +20,10 @@ export class CrossAccountDelegationRecord extends Construct {
|
|
|
20
20
|
parentHostedZoneName: props.parentHostedZoneName
|
|
21
21
|
});
|
|
22
22
|
Tags.of(this).add("fjall:description", this.description);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
applyCostAllocationTags(this, {
|
|
24
|
+
service: "crossAccountDelegation",
|
|
25
|
+
domain: props.costAllocationDomain ?? props.delegatedZoneName,
|
|
26
|
+
environment: props.costAllocationEnvironment
|
|
27
|
+
});
|
|
26
28
|
}
|
|
27
29
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
|
-
import { Duration } from "aws-cdk-lib";
|
|
2
|
+
import { Duration, Fn } from "aws-cdk-lib";
|
|
3
3
|
import { Runtime } from "aws-cdk-lib/aws-lambda";
|
|
4
4
|
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
5
5
|
import { CustomResource } from "../utilities/customResource.js";
|
|
@@ -35,7 +35,7 @@ export class CrossAccountReturnRoutes extends Construct {
|
|
|
35
35
|
properties: {
|
|
36
36
|
PeerRoleArn: props.peerRoleArn,
|
|
37
37
|
PeerRegion: props.peerRegion,
|
|
38
|
-
RouteTableIds:
|
|
38
|
+
RouteTableIds: Fn.join(",", props.routeTableIds),
|
|
39
39
|
LocalVpcCidr: props.localVpcCidr,
|
|
40
40
|
PeeringConnectionId: props.peeringConnectionId,
|
|
41
41
|
EnableDns: props.enableDns === false ? "false" : "true"
|
|
@@ -107,13 +107,18 @@ exports.handler = async (event) => {
|
|
|
107
107
|
if (event.RequestType === "Update") {
|
|
108
108
|
const oldProps = event.OldResourceProperties || {};
|
|
109
109
|
const oldRouteTableIds = parseRouteTableIds(oldProps.RouteTableIds);
|
|
110
|
+
const oldCidr = oldProps.LocalVpcCidr || props.LocalVpcCidr;
|
|
111
|
+
const cidrChanged = oldCidr !== props.LocalVpcCidr;
|
|
110
112
|
const currentSet = new Set(routeTableIds);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
// CIDR change: stale routes persist on every kept table unless deleted by old CIDR first.
|
|
114
|
+
const idsToDeleteOldCidr = cidrChanged
|
|
115
|
+
? oldRouteTableIds
|
|
116
|
+
: oldRouteTableIds.filter((id) => !currentSet.has(id));
|
|
117
|
+
for (const routeTableId of idsToDeleteOldCidr) {
|
|
113
118
|
try {
|
|
114
119
|
await ec2.send(new DeleteRouteCommand({
|
|
115
120
|
RouteTableId: routeTableId,
|
|
116
|
-
DestinationCidrBlock:
|
|
121
|
+
DestinationCidrBlock: oldCidr
|
|
117
122
|
}));
|
|
118
123
|
} catch (err) {
|
|
119
124
|
const name = err && err.name;
|
|
@@ -140,14 +145,13 @@ exports.handler = async (event) => {
|
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
148
|
+
// Always sent so a flip from true to false actually disables DNS — guarding on === "true" leaves it enabled forever.
|
|
149
|
+
await ec2.send(new ModifyVpcPeeringConnectionOptionsCommand({
|
|
150
|
+
VpcPeeringConnectionId: props.PeeringConnectionId,
|
|
151
|
+
AccepterPeeringConnectionOptions: {
|
|
152
|
+
AllowDnsResolutionFromRemoteVpc: props.EnableDns === "true"
|
|
153
|
+
}
|
|
154
|
+
}));
|
|
151
155
|
|
|
152
156
|
return { PhysicalResourceId: physicalResourceId };
|
|
153
157
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Duration
|
|
1
|
+
import { Duration } from "aws-cdk-lib";
|
|
2
2
|
import { DNS_APEX } from "@fjall/util";
|
|
3
|
-
import {
|
|
3
|
+
import { applyCostAllocationTags } from "../../../../utils/costAllocationTags.js";
|
|
4
4
|
export const DEFAULT_DNS_TTL_SECONDS = 300;
|
|
5
5
|
export function resolveFqdn(zoneName, recordName) {
|
|
6
6
|
return recordName === DNS_APEX ? zoneName : `${recordName}.${zoneName}`;
|
|
@@ -12,7 +12,9 @@ export function defaultDnsComment(recordType, fqdn) {
|
|
|
12
12
|
return `Fjall-managed ${recordType} record for ${fqdn}`;
|
|
13
13
|
}
|
|
14
14
|
export function applyDnsRecordTags(construct, props) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
applyCostAllocationTags(construct, {
|
|
16
|
+
service: "dnsRecord",
|
|
17
|
+
domain: props.costAllocationDomain ?? props.zoneName,
|
|
18
|
+
environment: props.costAllocationEnvironment
|
|
19
|
+
});
|
|
18
20
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
|
-
import {
|
|
2
|
+
import { type ICertificate } from "aws-cdk-lib/aws-certificatemanager";
|
|
3
3
|
import { type IHostedZone } from "aws-cdk-lib/aws-route53";
|
|
4
4
|
export interface DomainCertificateProps {
|
|
5
5
|
readonly domainName: string;
|
|
@@ -11,7 +11,7 @@ export interface DomainCertificateProps {
|
|
|
11
11
|
readonly costAllocationDomain?: string;
|
|
12
12
|
}
|
|
13
13
|
export declare class DomainCertificate extends Construct {
|
|
14
|
-
readonly certificate:
|
|
14
|
+
readonly certificate: ICertificate;
|
|
15
15
|
readonly certificateArn: string;
|
|
16
16
|
readonly description: string;
|
|
17
17
|
constructor(scope: Construct, id: string, props: DomainCertificateProps);
|
|
@@ -3,7 +3,7 @@ import { CfnOutput, Tags } from "aws-cdk-lib";
|
|
|
3
3
|
import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager";
|
|
4
4
|
import { getDomainExportNames } from "@fjall/util";
|
|
5
5
|
import { toPascalCase } from "../../../utils/capitaliseString.js";
|
|
6
|
-
import {
|
|
6
|
+
import { applyCostAllocationTags } from "../../../utils/costAllocationTags.js";
|
|
7
7
|
export class DomainCertificate extends Construct {
|
|
8
8
|
certificate;
|
|
9
9
|
certificateArn;
|
|
@@ -21,9 +21,11 @@ export class DomainCertificate extends Construct {
|
|
|
21
21
|
});
|
|
22
22
|
this.certificateArn = this.certificate.certificateArn;
|
|
23
23
|
Tags.of(this).add("fjall:description", this.description);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
applyCostAllocationTags(this, {
|
|
25
|
+
service: "certificate",
|
|
26
|
+
domain: props.costAllocationDomain ?? props.domainName,
|
|
27
|
+
environment: props.costAllocationEnvironment
|
|
28
|
+
});
|
|
27
29
|
const safeKey = toPascalCase(props.domainName.split(".").join(""));
|
|
28
30
|
const exports = getDomainExportNames(props.domainName);
|
|
29
31
|
new CfnOutput(this, `${safeKey}CertificateArn`, {
|
|
@@ -4,7 +4,7 @@ import { HostedZone as AWSHostedZone } from "aws-cdk-lib/aws-route53";
|
|
|
4
4
|
import { getDomainExportNames } from "@fjall/util";
|
|
5
5
|
import { toPascalCase, getSafeZoneName } from "../../../utils/capitaliseString.js";
|
|
6
6
|
import { DelegationRole } from "../iam/delegationRole.js";
|
|
7
|
-
import {
|
|
7
|
+
import { applyCostAllocationTags } from "../../../utils/costAllocationTags.js";
|
|
8
8
|
export class HostedZoneFactory {
|
|
9
9
|
static import(stack, hostedZoneId, zoneName, opts) {
|
|
10
10
|
const safeZone = toPascalCase(getSafeZoneName(zoneName));
|
|
@@ -71,10 +71,11 @@ export class HostedZone extends Construct {
|
|
|
71
71
|
this.isImported = true;
|
|
72
72
|
Tags.of(this).add("fjall:description", this.description);
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
applyCostAllocationTags(this, {
|
|
75
|
+
service: "hostedZone",
|
|
76
|
+
domain: props.costAllocationDomain ?? props.zoneName,
|
|
77
|
+
environment: props.costAllocationEnvironment
|
|
78
|
+
});
|
|
78
79
|
new CfnOutput(this, `${safeZone}HostedZoneId`, {
|
|
79
80
|
key: `${safeZone}HostedZoneId`,
|
|
80
81
|
value: this.hostedZoneId,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Construct } from "constructs";
|
|
2
|
+
import { Duration } from "aws-cdk-lib";
|
|
3
|
+
import { type IVpc } from "aws-cdk-lib/aws-ec2";
|
|
4
|
+
import { type IPrivateDnsNamespace, type IService } from "aws-cdk-lib/aws-servicediscovery";
|
|
5
|
+
/**
|
|
6
|
+
* Plumbing-only base resource wrapping `aws-cdk-lib/aws-servicediscovery`'s
|
|
7
|
+
* `PrivateDnsNamespace`. Per D7 + D29 of the ClickHouse Database Factory
|
|
8
|
+
* promotion design, the only consumer entry point is `App.getNamespace()` —
|
|
9
|
+
* this class is NOT re-exported from `lib/index.ts`.
|
|
10
|
+
*
|
|
11
|
+
* No `static fromXxx(...)` factory is exposed: cross-stack consumption is
|
|
12
|
+
* single-stack-only for v1 per D29. If/when cross-stack arises, a `fromXxx`
|
|
13
|
+
* factory lands then, not now.
|
|
14
|
+
*/
|
|
15
|
+
export interface ServiceDiscoveryNamespaceProps {
|
|
16
|
+
vpc: IVpc;
|
|
17
|
+
name: string;
|
|
18
|
+
/** Override the default description (`"ServiceDiscovery <name> — Fjall private DNS namespace"`). */
|
|
19
|
+
description?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Removal policy. Default resolves via the `env()` helper (production →
|
|
22
|
+
* RETAIN; non-prod → DESTROY) — matches the convention codified in D17 of
|
|
23
|
+
* the EventBridge promotion design. NODE_ENV is intentionally NOT consulted
|
|
24
|
+
* because it is unset during CDK synth in Fjall's deployment paths.
|
|
25
|
+
*/
|
|
26
|
+
removalPolicy?: "DESTROY" | "RETAIN";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Properties for registering a Cloud Map service inside the namespace via
|
|
30
|
+
* `registerService(...)`. Single source of truth for the second-tier wrapper
|
|
31
|
+
* around `AWS::ServiceDiscovery::Service`. Per D7(d), this method is
|
|
32
|
+
* internal-only — never consumed directly from user infrastructure code.
|
|
33
|
+
*/
|
|
34
|
+
export interface ServiceRegistrationProps {
|
|
35
|
+
/** Service name within the namespace (forms the leftmost label of the FQDN). */
|
|
36
|
+
name: string;
|
|
37
|
+
/**
|
|
38
|
+
* Description override. Default: `"Fjall internal service: <name>"`.
|
|
39
|
+
* Operators see this in the Cloud Map console; explicit overrides are
|
|
40
|
+
* preferred when the service name alone is ambiguous (e.g. `"clickhouse"`
|
|
41
|
+
* is fine; `"web"` benefits from a description naming the role).
|
|
42
|
+
*/
|
|
43
|
+
description?: string;
|
|
44
|
+
/**
|
|
45
|
+
* DNS TTL applied to the A records returned for this service. Default:
|
|
46
|
+
* 60 seconds — matches CDK's implicit ECS default. Raise for stable
|
|
47
|
+
* services (cuts query volume); lower for rapidly-changing instance sets.
|
|
48
|
+
*/
|
|
49
|
+
dnsTtl?: Duration;
|
|
50
|
+
/**
|
|
51
|
+
* DNS record type. Default `"A"` — appropriate for `awsvpc` ECS services
|
|
52
|
+
* where each task has its own ENI IP. Set to `"SRV"` when the consumer is
|
|
53
|
+
* an ECS service running under `host` or `bridge` network mode: SRV records
|
|
54
|
+
* carry both the host IP and the published port, which `A` records cannot.
|
|
55
|
+
* CDK's `Ec2Service.associateCloudMapService(...)` requires SRV (and a
|
|
56
|
+
* `containerName` + `containerPort`) under those modes.
|
|
57
|
+
*/
|
|
58
|
+
dnsRecordType?: "A" | "SRV";
|
|
59
|
+
}
|
|
60
|
+
export declare class ServiceDiscoveryNamespace extends Construct {
|
|
61
|
+
#private;
|
|
62
|
+
readonly id: string;
|
|
63
|
+
constructor(scope: Construct, id: string, props: ServiceDiscoveryNamespaceProps);
|
|
64
|
+
/**
|
|
65
|
+
* Get the underlying CDK private DNS namespace as the `IPrivateDnsNamespace`
|
|
66
|
+
* interface. Per D18(a), the public surface is the interface, never the
|
|
67
|
+
* concrete `PrivateDnsNamespace` class.
|
|
68
|
+
*/
|
|
69
|
+
getNamespace(): IPrivateDnsNamespace;
|
|
70
|
+
getNamespaceArn(): string;
|
|
71
|
+
getNamespaceName(): string;
|
|
72
|
+
/**
|
|
73
|
+
* Register a Cloud Map service inside this namespace. Creates an
|
|
74
|
+
* `AWS::ServiceDiscovery::Service` resource via the underlying CDK
|
|
75
|
+
* `PrivateDnsNamespace.createService(...)` with Fjall description discipline
|
|
76
|
+
* applied. Internal-only per D7(d) — Fjall resources call this from inside
|
|
77
|
+
* their construct bodies; user code never invokes it directly.
|
|
78
|
+
*
|
|
79
|
+
* Throws on duplicate registration: a single namespace cannot host two
|
|
80
|
+
* services with the same DNS name. Caught at synth time, not deploy time.
|
|
81
|
+
*/
|
|
82
|
+
registerService(props: ServiceRegistrationProps): IService;
|
|
83
|
+
/**
|
|
84
|
+
* True if a service with this name has been registered in the namespace.
|
|
85
|
+
* Cheap registry lookup; does NOT consult AWS — only counts services
|
|
86
|
+
* registered via `registerService(...)` on this construct.
|
|
87
|
+
*/
|
|
88
|
+
hasService(name: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Build the FQDN for a previously-registered service:
|
|
91
|
+
* `<serviceName>.<namespaceName>` (e.g. `clickhouse.acme.local`). Throws if
|
|
92
|
+
* the service has not been registered — prevents typos that would silently
|
|
93
|
+
* resolve to NXDOMAIN at runtime.
|
|
94
|
+
*/
|
|
95
|
+
getEndpoint(serviceName: string): string;
|
|
96
|
+
}
|