@fjall/components-infrastructure 6.0.0 → 7.0.0

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.
Files changed (29) hide show
  1. package/dist/lib/patterns/aws/clickhouseDatabase.js +4 -0
  2. package/dist/lib/resources/aws/compute/ec2.d.ts +31 -14
  3. package/dist/lib/resources/aws/compute/ec2.js +34 -10
  4. package/dist/lib/resources/aws/compute/ecsCapacityConfig.d.ts +56 -16
  5. package/dist/lib/resources/aws/compute/ecsCapacityConfig.js +226 -64
  6. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +15 -0
  7. package/dist/lib/resources/aws/compute/ecsConstants.js +15 -0
  8. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +16 -6
  9. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +68 -19
  10. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +33 -4
  11. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +15 -5
  12. package/dist/lib/resources/aws/compute/persistentDataVolume.js +9 -6
  13. package/dist/lib/utils/capacityIdentityContext.d.ts +20 -0
  14. package/dist/lib/utils/capacityIdentityContext.js +43 -0
  15. package/dist/lib/utils/manifestWriter.d.ts +19 -2
  16. package/dist/lib/utils/manifestWriter.js +35 -0
  17. package/package.json +4 -5
  18. package/dist/lib/config/aws/identityCenterMembership.d.ts +0 -11
  19. package/dist/lib/config/aws/identityCenterMembership.js +0 -61
  20. package/dist/lib/layers/layers/secrets-resolver/bin/resolve-secrets +0 -30
  21. package/dist/lib/layers/layers/secrets-resolver/bin/resolve-secrets.mjs +0 -212
  22. package/dist/lib/patterns/aws/buildkite/alarms.d.ts +0 -25
  23. package/dist/lib/patterns/aws/buildkite/alarms.js +0 -78
  24. package/dist/lib/patterns/aws/domainDelegation.d.ts +0 -8
  25. package/dist/lib/patterns/aws/domainDelegation.js +0 -45
  26. package/dist/lib/patterns/aws/domainFactory.d.ts +0 -23
  27. package/dist/lib/patterns/aws/domainFactory.js +0 -61
  28. package/dist/lib/utils/addSuffixToEmail.d.ts +0 -1
  29. package/dist/lib/utils/addSuffixToEmail.js +0 -3
@@ -1,25 +0,0 @@
1
- import type { Construct } from "constructs";
2
- export interface BuildkiteAlarmParams {
3
- readonly buildkiteOrgSlug: string;
4
- readonly buildkiteQueue: string;
5
- readonly autoScalingGroupName: string;
6
- /**
7
- * Alarm-action destination, in either `resolveAlertsTopic` string shape:
8
- * a literal `arn:...` or `"import:<ExportName>"` (e.g.
9
- * `"import:SharedAlarmTopicArn"`). Omitted → alarms exist but page nobody.
10
- */
11
- readonly alarmSnsTopicArn?: string;
12
- }
13
- /**
14
- * The fleet's two Phase-1 alarms (design § D14) — the "~zero babysitting"
15
- * posture is honest only with these:
16
- *
17
- * 1. Scaler heartbeat — `ScheduledJobsCount` goes MISSING for 15 minutes.
18
- * The scaler publishes every poll, so metric absence means the scaler
19
- * Lambda is dead or failing; `treatMissingData: BREACHING` is the alarm's
20
- * entire mechanism.
21
- * 2. Queued with zero capacity — jobs scheduled while the ASG has no
22
- * in-service instances for 15 minutes: the fleet cannot boot (AMI gone,
23
- * quota, subnet failure) while work is waiting.
24
- */
25
- export declare function addBuildkiteAlarms(scope: Construct, params: BuildkiteAlarmParams): void;
@@ -1,78 +0,0 @@
1
- import { Duration } from "aws-cdk-lib";
2
- import { Alarm, ComparisonOperator, MathExpression, Metric, TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
3
- import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
4
- import { resolveAlertsTopic } from "../../../utils/resolveAlertsTopic.js";
5
- /**
6
- * Metrics namespace the buildkite-agent-scaler publishes to, dimensioned by
7
- * {Org, Queue} — BOTH dimensions are required; querying Queue alone reads no
8
- * data (CloudWatch dimension matching is exact-set), leaving the heartbeat
9
- * permanently ALARM and the queued alarm permanently inert. Phase-1b rollout
10
- * gate: verify both metrics carry data after the first build before trusting
11
- * the alarms (design § D13/D14) — a namespace/dimension mismatch here is
12
- * invisible to the synth tests.
13
- */
14
- const SCALER_METRICS_NAMESPACE = "Buildkite";
15
- /**
16
- * The fleet's two Phase-1 alarms (design § D14) — the "~zero babysitting"
17
- * posture is honest only with these:
18
- *
19
- * 1. Scaler heartbeat — `ScheduledJobsCount` goes MISSING for 15 minutes.
20
- * The scaler publishes every poll, so metric absence means the scaler
21
- * Lambda is dead or failing; `treatMissingData: BREACHING` is the alarm's
22
- * entire mechanism.
23
- * 2. Queued with zero capacity — jobs scheduled while the ASG has no
24
- * in-service instances for 15 minutes: the fleet cannot boot (AMI gone,
25
- * quota, subnet failure) while work is waiting.
26
- */
27
- export function addBuildkiteAlarms(scope, params) {
28
- const scheduledJobs = new Metric({
29
- namespace: SCALER_METRICS_NAMESPACE,
30
- metricName: "ScheduledJobsCount",
31
- dimensionsMap: {
32
- Org: params.buildkiteOrgSlug,
33
- Queue: params.buildkiteQueue
34
- },
35
- statistic: "Maximum",
36
- period: Duration.minutes(5)
37
- });
38
- const heartbeatAlarm = new Alarm(scope, "ScalerHeartbeatAlarm", {
39
- alarmDescription: `Buildkite scaler for queue '${params.buildkiteQueue}' has stopped ` +
40
- "publishing metrics — scaler Lambda dead or erroring. Jobs will queue " +
41
- "with no scale-out.",
42
- metric: scheduledJobs,
43
- comparisonOperator: ComparisonOperator.LESS_THAN_THRESHOLD,
44
- threshold: 0,
45
- evaluationPeriods: 3,
46
- treatMissingData: TreatMissingData.BREACHING
47
- });
48
- const inServiceInstances = new Metric({
49
- namespace: "AWS/AutoScaling",
50
- metricName: "GroupInServiceInstances",
51
- dimensionsMap: { AutoScalingGroupName: params.autoScalingGroupName },
52
- statistic: "Maximum",
53
- period: Duration.minutes(5)
54
- });
55
- const queuedWithZeroCapacity = new MathExpression({
56
- expression: "IF(scheduled > 0 AND inService == 0, 1, 0)",
57
- usingMetrics: {
58
- scheduled: scheduledJobs,
59
- inService: inServiceInstances
60
- },
61
- period: Duration.minutes(5)
62
- });
63
- const queuedAlarm = new Alarm(scope, "QueuedWithZeroCapacityAlarm", {
64
- alarmDescription: `Buildkite queue '${params.buildkiteQueue}' has scheduled jobs but ` +
65
- "zero in-service agents for 15 minutes — the fleet cannot boot " +
66
- "(AMI, quota, or subnet failure) while work waits.",
67
- metric: queuedWithZeroCapacity,
68
- comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
69
- threshold: 1,
70
- evaluationPeriods: 3,
71
- treatMissingData: TreatMissingData.NOT_BREACHING
72
- });
73
- const topic = resolveAlertsTopic(scope, "BuildkiteAlarmTopic", params.alarmSnsTopicArn);
74
- if (topic !== undefined) {
75
- heartbeatAlarm.addAlarmAction(new SnsAction(topic));
76
- queuedAlarm.addAlarmAction(new SnsAction(topic));
77
- }
78
- }
@@ -1,8 +0,0 @@
1
- import { Construct } from "constructs";
2
- import type { IHostedZone } from "aws-cdk-lib/aws-route53";
3
- import type { DomainDelegatedProps } from "./interfaces/domain.js";
4
- export declare class DomainDelegation extends Construct {
5
- readonly hostedZoneId: string;
6
- readonly hostedZone: IHostedZone;
7
- constructor(scope: Construct, id: string, props: DomainDelegatedProps);
8
- }
@@ -1,45 +0,0 @@
1
- import { Construct } from "constructs";
2
- import { Fn } from "aws-cdk-lib";
3
- import { getDomainExportNames } from "@fjall/util";
4
- import { Role } from "../../resources/aws/iam/index.js";
5
- import { toPascalCase, getSafeZoneName } from "../../utils/capitaliseString.js";
6
- import { HostedZone } from "../../resources/aws/networking/hostedZone.js";
7
- import { CrossAccountDelegationRecord } from "../../resources/aws/networking/crossAccountDelegationRecord.js";
8
- import { DomainCertificate } from "../../resources/aws/networking/domainCertificate.js";
9
- import { composeDnsRecords } from "./dnsRecordComposer.js";
10
- export class DomainDelegation extends Construct {
11
- hostedZoneId;
12
- hostedZone;
13
- constructor(scope, id, props) {
14
- super(scope, id);
15
- const delegationRoleArn = Fn.importValue(getDomainExportNames(props.parentZoneName).delegationRoleArn);
16
- const hostedZoneDelegationRole = Role.fromRoleArn(this, "HostedZoneDelegationRole", delegationRoleArn);
17
- const safeZone = getSafeZoneName(props.zoneName);
18
- const zone = new HostedZone(this, `${safeZone}HostedZone`, {
19
- zoneName: props.zoneName,
20
- createDelegationRole: false
21
- });
22
- this.hostedZone = zone.hostedZone;
23
- this.hostedZoneId = zone.hostedZoneId;
24
- new CrossAccountDelegationRecord(this, `${safeZone}DelegationRecord`, {
25
- delegationRole: hostedZoneDelegationRole,
26
- delegatedZone: zone.hostedZone,
27
- delegatedZoneName: props.zoneName,
28
- parentHostedZoneName: props.parentZoneName
29
- });
30
- const safeKey = toPascalCase(safeZone);
31
- if (props.certificates) {
32
- props.certificates.forEach((cert, index) => {
33
- const safeCertName = toPascalCase(cert.domainName.split(".").join(""));
34
- new DomainCertificate(this, `${safeKey}${safeCertName}Cert${index}`, {
35
- domainName: cert.domainName,
36
- subjectAlternativeNames: cert.subjectAlternativeNames,
37
- hostedZone: zone.hostedZone
38
- });
39
- });
40
- }
41
- if (props.records) {
42
- composeDnsRecords(this, zone.hostedZone, props.zoneName, props.records);
43
- }
44
- }
45
- }
@@ -1,23 +0,0 @@
1
- import { Construct } from "constructs";
2
- import type { IHostedZone } from "aws-cdk-lib/aws-route53";
3
- import { DomainDelegation } from "./domainDelegation.js";
4
- import type { DomainApexProps, DomainDelegatedProps } from "./interfaces/domain.js";
5
- /**
6
- * @internal
7
- * Apex domain composition — combines HostedZone, per-type DNS records, and
8
- * certificates. Defined inline here rather than in its own file because Phase 1
9
- * replaces this class wholesale with the user-facing `Domain` construct. Extracting
10
- * now would be over-investment in a doomed abstraction.
11
- *
12
- * Exported only so pattern-layer tests can assert `instanceof ApexDomain`.
13
- */
14
- export declare class ApexDomain extends Construct {
15
- readonly hostedZone: IHostedZone;
16
- readonly hostedZoneId: string;
17
- constructor(scope: Construct, id: string, props: DomainApexProps);
18
- private addCertificates;
19
- }
20
- export declare class DomainFactory {
21
- static build(id: string, props: DomainApexProps): (scope: Construct) => ApexDomain;
22
- static build(id: string, props: DomainDelegatedProps): (scope: Construct) => DomainDelegation;
23
- }
@@ -1,61 +0,0 @@
1
- import { Construct } from "constructs";
2
- import { HostedZone } from "../../resources/aws/networking/hostedZone.js";
3
- import { DomainCertificate } from "../../resources/aws/networking/domainCertificate.js";
4
- import { toPascalCase } from "../../utils/capitaliseString.js";
5
- import { DomainDelegation } from "./domainDelegation.js";
6
- import { composeDnsRecords } from "./dnsRecordComposer.js";
7
- /**
8
- * @internal
9
- * Apex domain composition — combines HostedZone, per-type DNS records, and
10
- * certificates. Defined inline here rather than in its own file because Phase 1
11
- * replaces this class wholesale with the user-facing `Domain` construct. Extracting
12
- * now would be over-investment in a doomed abstraction.
13
- *
14
- * Exported only so pattern-layer tests can assert `instanceof ApexDomain`.
15
- */
16
- export class ApexDomain extends Construct {
17
- hostedZone;
18
- hostedZoneId;
19
- constructor(scope, id, props) {
20
- super(scope, id);
21
- const zone = new HostedZone(this, "Zone", {
22
- zoneName: props.zoneName,
23
- hostedZoneId: props.hostedZoneId
24
- });
25
- this.hostedZone = zone.hostedZone;
26
- this.hostedZoneId = zone.hostedZoneId;
27
- if (props.records) {
28
- composeDnsRecords(this, zone.hostedZone, props.zoneName, props.records);
29
- }
30
- if (props.certificates) {
31
- this.addCertificates(props.certificates, props.zoneName);
32
- }
33
- }
34
- addCertificates(certificates, zoneName) {
35
- const safeZone = toPascalCase(zoneName.split(".").join(""));
36
- certificates.forEach((cert, index) => {
37
- const safeCertName = toPascalCase(cert.domainName.split(".").join(""));
38
- new DomainCertificate(this, `${safeZone}${safeCertName}Cert${index}`, {
39
- domainName: cert.domainName,
40
- subjectAlternativeNames: cert.subjectAlternativeNames,
41
- hostedZone: this.hostedZone
42
- });
43
- });
44
- }
45
- }
46
- export class DomainFactory {
47
- static build(id, props) {
48
- return (scope) => {
49
- switch (props.type) {
50
- case "domain":
51
- return new ApexDomain(scope, id, props);
52
- case "delegated":
53
- return new DomainDelegation(scope, id, props);
54
- default: {
55
- const _exhaustive = props;
56
- throw new Error(`Unsupported domain type: ${String(_exhaustive.type)}`);
57
- }
58
- }
59
- };
60
- }
61
- }
@@ -1 +0,0 @@
1
- export default function addSuffixToEmail(email: string, suffix: string): string;
@@ -1,3 +0,0 @@
1
- export default function addSuffixToEmail(email, suffix) {
2
- return email.replace("@", `+${suffix}@`);
3
- }