@fjall/components-infrastructure 2.33.0 → 2.34.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.
- package/dist/lib/app.js +15 -1
- package/dist/lib/patterns/aws/account.js +6 -1
- package/dist/lib/patterns/aws/computeEcs.js +9 -5
- package/dist/lib/patterns/aws/computeEcsTypes.d.ts +5 -1
- package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +4 -4
- package/dist/lib/resources/aws/logging/logGroup.d.ts +6 -0
- package/dist/lib/resources/aws/logging/logGroup.js +11 -1
- package/package.json +4 -4
package/dist/lib/app.js
CHANGED
|
@@ -20,6 +20,7 @@ import { FjallLogger } from "./utils/validationLogger.js";
|
|
|
20
20
|
import { getManifestCollector, writeManifest } from "./utils/manifestWriter.js";
|
|
21
21
|
import { toPascalCase, toKebab } from "./utils/capitaliseString.js";
|
|
22
22
|
import { COST_ALLOCATION_TAGS } from "./utils/costAllocationTags.js";
|
|
23
|
+
import { DEFAULT_ORG_ID, resolveOrgId } from "./utils/cdkContext.js";
|
|
23
24
|
/**
|
|
24
25
|
* The basic corner-stone of all Fjall-hosted applications.
|
|
25
26
|
* This class is a singleton and should be used to create and manage
|
|
@@ -702,6 +703,15 @@ export class App extends CdkApp {
|
|
|
702
703
|
}
|
|
703
704
|
applyTagsAspect() {
|
|
704
705
|
if (!this.aspectApplied && Object.keys(this.globalTags).length > 0) {
|
|
706
|
+
// Owner fallback resolves lazily HERE, not in initialiseStandardTags:
|
|
707
|
+
// at construction the orgId context may not be set yet, and an explicit
|
|
708
|
+
// addTags owner must win — by this point it is already in globalTags.
|
|
709
|
+
if (this.globalTags[COST_ALLOCATION_TAGS.OWNER] === undefined) {
|
|
710
|
+
const orgId = resolveOrgId(this.node);
|
|
711
|
+
if (orgId !== undefined && orgId !== DEFAULT_ORG_ID) {
|
|
712
|
+
this.globalTags[COST_ALLOCATION_TAGS.OWNER] = orgId;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
705
715
|
// Apply standard tags using Tags.of(this).add()
|
|
706
716
|
for (const [key, value] of Object.entries(this.globalTags)) {
|
|
707
717
|
Tags.of(this).add(key, value);
|
|
@@ -748,7 +758,11 @@ export class App extends CdkApp {
|
|
|
748
758
|
return this.manifestCollector;
|
|
749
759
|
}
|
|
750
760
|
synth(options) {
|
|
751
|
-
//
|
|
761
|
+
// Tagging is an invariant of synthesis, not of getStack(): governance
|
|
762
|
+
// stacks (Organisation/Account/Platform) and raw `extends Stack` classes
|
|
763
|
+
// attach directly to the App and never call getStack(), so without this
|
|
764
|
+
// they synthesise untagged. Idempotent via the aspectApplied guard.
|
|
765
|
+
this.applyTagsAspect();
|
|
752
766
|
const assembly = super.synth(options);
|
|
753
767
|
// After synthesis, write Fjall manifest to cdk.out
|
|
754
768
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CfnOutput, Stack } from "aws-cdk-lib";
|
|
1
|
+
import { CfnOutput, Stack, Tags } from "aws-cdk-lib";
|
|
2
2
|
import { EcrDefaultImage, SharedAlarmTopic } from "../../config/aws/index.js";
|
|
3
3
|
import { ManagementEventsTrail } from "../../config/aws/cloudTrail.js";
|
|
4
4
|
import { OidcConnector } from "../../config/aws/oidcConnector.js";
|
|
@@ -6,6 +6,7 @@ import { AccountMonitoringRole } from "../../config/aws/accountMonitoringRole.js
|
|
|
6
6
|
import { AccountAuditRole } from "../../config/aws/accountAuditRole.js";
|
|
7
7
|
import { CDK_CONTEXT_KEYS, resolveAccountTrailState } from "../../utils/cdkContext.js";
|
|
8
8
|
import { getConfig } from "../../utils/getConfig.js";
|
|
9
|
+
import { COST_ALLOCATION_TAGS } from "../../utils/costAllocationTags.js";
|
|
9
10
|
import { DisasterRecovery } from "../../config/aws/disasterRecovery.js";
|
|
10
11
|
import { S3BlockPublicAccess } from "../../config/aws/s3BlockPublicAccess.js";
|
|
11
12
|
import { EbsDefaultEncryption } from "../../config/aws/ebsDefaultEncryption.js";
|
|
@@ -28,6 +29,10 @@ export class Account extends Stack {
|
|
|
28
29
|
}
|
|
29
30
|
const env = props.env ?? { region, account: accountId };
|
|
30
31
|
super(scope, id, { ...props, env });
|
|
32
|
+
// Governance apps never name the App, so the App-level service tag would
|
|
33
|
+
// read "FjallApp"; the stack-scope tag wins over App scope and names the
|
|
34
|
+
// governance tier instead ("Organisation"/"Account"/"Platform").
|
|
35
|
+
Tags.of(this).add(COST_ALLOCATION_TAGS.SERVICE, id);
|
|
31
36
|
this.resolvedRegion = region ?? this.region;
|
|
32
37
|
const orgId = this.node.tryGetContext(CDK_CONTEXT_KEYS.ORG_ID);
|
|
33
38
|
if (orgId) {
|
|
@@ -13,6 +13,7 @@ import { resolveImportedSecret } from "../../resources/aws/secrets/index.js";
|
|
|
13
13
|
import App from "../../app.js";
|
|
14
14
|
import EcsCluster from "../../resources/aws/compute/ecs.js";
|
|
15
15
|
import { createScheduledTaskDefinition, createMigrationTaskDefinition } from "../../resources/aws/compute/ecsTaskDefinition.js";
|
|
16
|
+
import { DEFAULT_LOG_RETENTION } from "../../resources/aws/compute/ecsConstants.js";
|
|
16
17
|
import { EcsLifecycleHookMigration } from "../../resources/aws/compute/ecsLifecycleHookMigration.js";
|
|
17
18
|
import { Role } from "../../resources/aws/iam/role.js";
|
|
18
19
|
import { LogGroup } from "../../resources/aws/logging/logGroup.js";
|
|
@@ -924,13 +925,16 @@ export class EcsCompute extends Construct {
|
|
|
924
925
|
});
|
|
925
926
|
}
|
|
926
927
|
}
|
|
928
|
+
// `logRetention` must not reach the driver: AwsLogDriver.bind() creates
|
|
929
|
+
// a raw RETAIN LogGroup inside aws-cdk-lib, escaping the env-aware
|
|
930
|
+
// removal default (D17).
|
|
931
|
+
const logGroup = entry.logGroup ??
|
|
932
|
+
new LogGroup(this, `${id}${toPascalCase(entry.name)}LogGroup`, {
|
|
933
|
+
retention: entry.logRetention ?? DEFAULT_LOG_RETENTION
|
|
934
|
+
});
|
|
927
935
|
const logging = new AwsLogDriver({
|
|
928
936
|
streamPrefix: `/ecs-scheduled/${id}/${entry.name}`,
|
|
929
|
-
|
|
930
|
-
...(entry.logGroup === undefined &&
|
|
931
|
-
entry.logRetention !== undefined && {
|
|
932
|
-
logRetention: entry.logRetention
|
|
933
|
-
})
|
|
937
|
+
logGroup
|
|
934
938
|
});
|
|
935
939
|
const container = taskDef.addContainer(`${id}${toPascalCase(entry.name)}Container`, {
|
|
936
940
|
image: entry.image,
|
|
@@ -501,7 +501,11 @@ export interface EcsScheduledTaskConfig {
|
|
|
501
501
|
secrets?: Record<string, EcsSecret>;
|
|
502
502
|
/** Pre-existing CDK log group. Mutually exclusive with `logRetention`. */
|
|
503
503
|
logGroup?: ILogGroup;
|
|
504
|
-
/**
|
|
504
|
+
/**
|
|
505
|
+
* When `logGroup` is omitted, an explicit framework log group is created
|
|
506
|
+
* with this retention (default: `DEFAULT_LOG_RETENTION`) and the env-aware
|
|
507
|
+
* removal default (D17).
|
|
508
|
+
*/
|
|
505
509
|
logRetention?: RetentionDays;
|
|
506
510
|
securityGroups?: ISecurityGroup[];
|
|
507
511
|
subnetSelection?: SubnetSelection;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AwsLogDriver, ContainerDependencyCondition, FargateTaskDefinition, Ec2TaskDefinition, NetworkMode, CpuArchitecture, OperatingSystemFamily } from "aws-cdk-lib/aws-ecs";
|
|
2
|
-
import { Duration
|
|
2
|
+
import { Duration } from "aws-cdk-lib";
|
|
3
3
|
import { Secret as EcsSecret } from "aws-cdk-lib/aws-ecs";
|
|
4
4
|
import { StringParameter } from "aws-cdk-lib/aws-ssm";
|
|
5
5
|
import { buildParameterPath } from "@fjall/util";
|
|
@@ -143,10 +143,10 @@ export function addContainersToTask(ctx, serviceName, serviceProps, taskDefiniti
|
|
|
143
143
|
// Explicit so the cluster can attach metric filters (createLogPatternAlarms).
|
|
144
144
|
// No logGroupName: CDK auto-names from the logical ID so it cannot collide
|
|
145
145
|
// with the orphaned-retained old group during the implicit→explicit replace.
|
|
146
|
-
//
|
|
146
|
+
// Removal: env-aware wrapper default (D17) — production RETAIN keeps
|
|
147
|
+
// fail-closed rollback evidence; non-production DESTROY avoids orphans.
|
|
147
148
|
const logGroup = new LogGroup(ctx.scope, `${ctx.props.clusterName}${serviceName}LogGroup`, {
|
|
148
|
-
retention: DEFAULT_LOG_RETENTION
|
|
149
|
-
removalPolicy: RemovalPolicy.RETAIN
|
|
149
|
+
retention: DEFAULT_LOG_RETENTION
|
|
150
150
|
});
|
|
151
151
|
for (const containerConfig of serviceProps.containers) {
|
|
152
152
|
const image = getContainerImage(ctx, serviceName, containerConfig, serviceProps);
|
|
@@ -7,6 +7,12 @@ import { type Construct } from "constructs";
|
|
|
7
7
|
* would silently give sibling framework lambdas different retention.
|
|
8
8
|
*/
|
|
9
9
|
export declare const DEFAULT_FRAMEWORK_LOG_RETENTION = Logs.RetentionDays.ONE_WEEK;
|
|
10
|
+
/**
|
|
11
|
+
* Framework log group with an env-aware removal-policy default (D17).
|
|
12
|
+
* When no `removalPolicy` is passed, the default resolves via
|
|
13
|
+
* `envAwareRemovalPolicyDefault()` — production → RETAIN, other recognised
|
|
14
|
+
* stages → DESTROY, unrecognised values fail synth. An explicit prop wins.
|
|
15
|
+
*/
|
|
10
16
|
export declare class LogGroup extends Logs.LogGroup {
|
|
11
17
|
constructor(scope: Construct, id: string, props?: Logs.LogGroupProps);
|
|
12
18
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Logs from "aws-cdk-lib/aws-logs";
|
|
2
|
+
import { envAwareRemovalPolicyDefault, toRemovalPolicy } from "../../../utils/removalPolicy.js";
|
|
2
3
|
/**
|
|
3
4
|
* Default retention for framework-owned lambda log groups. Coupled across
|
|
4
5
|
* every framework lambda construct (LambdaFunction, SingletonFunction, the
|
|
@@ -6,8 +7,17 @@ import * as Logs from "aws-cdk-lib/aws-logs";
|
|
|
6
7
|
* would silently give sibling framework lambdas different retention.
|
|
7
8
|
*/
|
|
8
9
|
export const DEFAULT_FRAMEWORK_LOG_RETENTION = Logs.RetentionDays.ONE_WEEK;
|
|
10
|
+
/**
|
|
11
|
+
* Framework log group with an env-aware removal-policy default (D17).
|
|
12
|
+
* When no `removalPolicy` is passed, the default resolves via
|
|
13
|
+
* `envAwareRemovalPolicyDefault()` — production → RETAIN, other recognised
|
|
14
|
+
* stages → DESTROY, unrecognised values fail synth. An explicit prop wins.
|
|
15
|
+
*/
|
|
9
16
|
export class LogGroup extends Logs.LogGroup {
|
|
10
17
|
constructor(scope, id, props) {
|
|
11
|
-
super(scope, id,
|
|
18
|
+
super(scope, id, {
|
|
19
|
+
...props,
|
|
20
|
+
removalPolicy: props?.removalPolicy ?? toRemovalPolicy(envAwareRemovalPolicyDefault())
|
|
21
|
+
});
|
|
12
22
|
}
|
|
13
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/components-infrastructure",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.34.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@aws-sdk/client-organizations": "^3.1038.0",
|
|
70
|
-
"@fjall/generator": "^2.
|
|
71
|
-
"@fjall/util": "^2.
|
|
70
|
+
"@fjall/generator": "^2.34.0",
|
|
71
|
+
"@fjall/util": "^2.34.0",
|
|
72
72
|
"constructs": "^10.6.0"
|
|
73
73
|
},
|
|
74
74
|
"overrides": {
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"engines": {
|
|
83
83
|
"node": ">=18.0.0"
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "8c553fca8f9737d445061ea75cf6c6c7db0d24c9"
|
|
86
86
|
}
|