@fjall/components-infrastructure 27.0.0 → 27.1.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.
@@ -12,7 +12,7 @@ import { Port } from "aws-cdk-lib/aws-ec2";
12
12
  import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
13
13
  import { MIGRATION_SNAPSHOT_NAME_PREFIX } from "./interfaces/database.js";
14
14
  import { pickLatestPrismaMigration } from "../../utils/migrationVersionResolvers.js";
15
- import { SCHEMA_GATE_DB_PASSWORD_ENV, SCHEMA_GATE_DB_URL_BASE_ENV, SCHEMA_GATE_DB_USER_ENV } from "@fjall/util/migration";
15
+ import { MIGRATION_SNAPSHOT_RETENTION_DAYS_ENV, SCHEMA_GATE_DB_PASSWORD_ENV, SCHEMA_GATE_DB_URL_BASE_ENV, SCHEMA_GATE_DB_USER_ENV } from "@fjall/util/migration";
16
16
  import { DatabaseClusterEngine, DatabaseInstanceEngine, AuroraPostgresEngineVersion, AuroraMysqlEngineVersion, PostgresEngineVersion, MysqlEngineVersion } from "aws-cdk-lib/aws-rds";
17
17
  import { Duration } from "aws-cdk-lib";
18
18
  export { isAwsManagedKey, isCMKRequested, AWS_MANAGED, USE_CMK } from "../../utils/databaseTypes.js";
@@ -296,11 +296,13 @@ const SNAPSHOT_ACTION_TABLE = {
296
296
  instance: {
297
297
  createAction: "rds:CreateDBSnapshot",
298
298
  describeAction: "rds:DescribeDBSnapshots",
299
+ deleteAction: "rds:DeleteDBSnapshot",
299
300
  resourceArnSegment: "snapshot"
300
301
  },
301
302
  cluster: {
302
303
  createAction: "rds:CreateDBClusterSnapshot",
303
304
  describeAction: "rds:DescribeDBClusterSnapshots",
305
+ deleteAction: "rds:DeleteDBClusterSnapshot",
304
306
  resourceArnSegment: "cluster-snapshot"
305
307
  }
306
308
  };
@@ -540,6 +542,10 @@ export class RelationalDatabase extends Construct {
540
542
  actions: [
541
543
  triple.createAction,
542
544
  triple.describeAction,
545
+ // Delete is confined to fjall-premigrate-* by IAM: a Delete request's
546
+ // resource is always a snapshot ARN, which only the prefix glob below
547
+ // can match — never the instance/cluster ARN.
548
+ triple.deleteAction,
543
549
  "rds:AddTagsToResource"
544
550
  ],
545
551
  resources: [
@@ -605,6 +611,7 @@ export class RelationalDatabase extends Construct {
605
611
  const target = this.getSnapshotTarget();
606
612
  environment.SNAPSHOT_TARGET_KIND = target.kind;
607
613
  environment.SNAPSHOT_TARGET_ARN = target.arn;
614
+ environment[MIGRATION_SNAPSHOT_RETENTION_DAYS_ENV] = String(this.database.getBackupRetentionDays());
608
615
  }
609
616
  const credentials = this.getCredentials();
610
617
  const secretsImport = {
@@ -127,9 +127,12 @@ export interface IRelationalDatabaseBase extends IDatabase, IConnectable, IMigra
127
127
  getSnapshotTarget(): SnapshotTarget;
128
128
  /**
129
129
  * Returns the IAM PolicyStatement granting the migration runner the rights
130
- * needed to take and tag a pre-migration snapshot of this database. Action
131
- * triple and resource ARN segment derive from `this.getSnapshotTarget().kind`.
132
- * Snapshot name glob is bound to `MIGRATION_SNAPSHOT_NAME_PREFIX`.
130
+ * needed to take, tag, describe, and prune pre-migration snapshots of this
131
+ * database. Actions and resource ARN segment derive from
132
+ * `this.getSnapshotTarget().kind`. Snapshot name glob is bound to
133
+ * `MIGRATION_SNAPSHOT_NAME_PREFIX`; the delete action can only ever match
134
+ * that glob (a Delete request's resource is a snapshot ARN, never the
135
+ * database ARN), confining pruning to premigrate snapshots.
133
136
  */
134
137
  getMigrationSnapshotPolicy(): PolicyStatement;
135
138
  /**
@@ -97,6 +97,7 @@ export declare class RdsAurora extends Construct implements IConnectable {
97
97
  private databaseCredentials;
98
98
  private cfnCluster?;
99
99
  private databaseNameValue;
100
+ private backupRetentionDaysValue;
100
101
  constructor(scope: Construct, id: string, props: RdsProps);
101
102
  private buildReaders;
102
103
  private addProxy;
@@ -108,6 +109,13 @@ export declare class RdsAurora extends Construct implements IConnectable {
108
109
  getSnapshotTarget(): Extract<SnapshotTarget, {
109
110
  kind: "cluster";
110
111
  }>;
112
+ /**
113
+ * Effective automated-backup retention in whole days (the applied
114
+ * `backupRetention`, defaulted). Feeds `MIGRATION_SNAPSHOT_RETENTION_DAYS`
115
+ * so the migration runner prunes pre-migration snapshots against the same
116
+ * window the cluster's PITR actually covers.
117
+ */
118
+ getBackupRetentionDays(): number;
111
119
  getDatabaseName(): string;
112
120
  getConnectionString(): string;
113
121
  getCfnCluster(): CfnDBCluster | undefined;
@@ -62,6 +62,7 @@ export class RdsAurora extends Construct {
62
62
  databaseCredentials;
63
63
  cfnCluster;
64
64
  databaseNameValue;
65
+ backupRetentionDaysValue;
65
66
  constructor(scope, id, props) {
66
67
  super(scope, id);
67
68
  validateServerlessV2DevKnobs(props);
@@ -163,6 +164,8 @@ export class RdsAurora extends Construct {
163
164
  const vpcSubnets = props.publiclyAccessible
164
165
  ? { subnetType: SubnetType.PUBLIC }
165
166
  : { subnetType: SubnetType.PRIVATE_WITH_EGRESS };
167
+ this.backupRetentionDaysValue =
168
+ props.backupRetention ?? RDS_DEFAULTS.BACKUP_RETENTION_DEFAULT_DAYS;
166
169
  // Common props shared across all cluster creation paths
167
170
  const baseClusterProps = {
168
171
  vpc: props.vpc,
@@ -171,7 +174,7 @@ export class RdsAurora extends Construct {
171
174
  engine,
172
175
  parameterGroup,
173
176
  backup: {
174
- retention: Duration.days(props.backupRetention ?? RDS_DEFAULTS.BACKUP_RETENTION_DEFAULT_DAYS)
177
+ retention: Duration.days(this.backupRetentionDaysValue)
175
178
  },
176
179
  storageEncrypted: true,
177
180
  ...(storageEncryptionKey && { storageEncryptionKey }),
@@ -361,6 +364,15 @@ export class RdsAurora extends Construct {
361
364
  getSnapshotTarget() {
362
365
  return { kind: "cluster", arn: this.databaseCluster.clusterArn };
363
366
  }
367
+ /**
368
+ * Effective automated-backup retention in whole days (the applied
369
+ * `backupRetention`, defaulted). Feeds `MIGRATION_SNAPSHOT_RETENTION_DAYS`
370
+ * so the migration runner prunes pre-migration snapshots against the same
371
+ * window the cluster's PITR actually covers.
372
+ */
373
+ getBackupRetentionDays() {
374
+ return this.backupRetentionDaysValue;
375
+ }
364
376
  getDatabaseName() {
365
377
  return this.databaseNameValue;
366
378
  }
@@ -81,6 +81,13 @@ export declare class RdsAuroraGlobal extends Construct implements IConnectable {
81
81
  kind: "cluster";
82
82
  }>;
83
83
  getDatabaseName(): string;
84
+ /**
85
+ * Effective automated-backup retention in whole days, delegated to the
86
+ * regional cluster that actually applies the default. Feeds
87
+ * `MIGRATION_SNAPSHOT_RETENTION_DAYS` so the migration runner prunes
88
+ * pre-migration snapshots against the same window PITR actually covers.
89
+ */
90
+ getBackupRetentionDays(): number;
84
91
  getConnectionString(): string;
85
92
  }
86
93
  export {};
@@ -182,6 +182,21 @@ export class RdsAuroraGlobal extends Construct {
182
182
  return this.regionalCluster.getDatabaseName();
183
183
  throw new Error("No Aurora cluster available to return database name from");
184
184
  }
185
+ /**
186
+ * Effective automated-backup retention in whole days, delegated to the
187
+ * regional cluster that actually applies the default. Feeds
188
+ * `MIGRATION_SNAPSHOT_RETENTION_DAYS` so the migration runner prunes
189
+ * pre-migration snapshots against the same window PITR actually covers.
190
+ */
191
+ getBackupRetentionDays() {
192
+ if (this.primaryCluster) {
193
+ return this.primaryCluster.getBackupRetentionDays();
194
+ }
195
+ if (this.regionalCluster) {
196
+ return this.regionalCluster.getBackupRetentionDays();
197
+ }
198
+ throw new Error("No Aurora cluster available to return backup retention from");
199
+ }
185
200
  getConnectionString() {
186
201
  if (this.primaryCluster)
187
202
  return this.primaryCluster.getConnectionString();
@@ -71,6 +71,7 @@ export declare class RdsInstance extends Construct implements IConnectable {
71
71
  private databaseProxySecurityGroup?;
72
72
  private readReplicaSecurityGroup?;
73
73
  private databaseNameValue;
74
+ private backupRetentionDaysValue;
74
75
  private readonly constructId;
75
76
  constructor(scope: Construct, id: string, props: RdsProps);
76
77
  private addDatabase;
@@ -84,6 +85,13 @@ export declare class RdsInstance extends Construct implements IConnectable {
84
85
  getSnapshotTarget(): Extract<SnapshotTarget, {
85
86
  kind: "instance";
86
87
  }>;
88
+ /**
89
+ * Effective automated-backup retention in whole days (the applied
90
+ * `backupRetention`, defaulted). Feeds `MIGRATION_SNAPSHOT_RETENTION_DAYS`
91
+ * so the migration runner prunes pre-migration snapshots against the same
92
+ * window the instance's PITR actually covers.
93
+ */
94
+ getBackupRetentionDays(): number;
87
95
  getDatabaseName(): string;
88
96
  getConnectionString(): string;
89
97
  /**
@@ -29,6 +29,7 @@ export class RdsInstance extends Construct {
29
29
  databaseProxySecurityGroup;
30
30
  readReplicaSecurityGroup;
31
31
  databaseNameValue;
32
+ backupRetentionDaysValue;
32
33
  constructId;
33
34
  constructor(scope, id, props) {
34
35
  super(scope, id);
@@ -121,6 +122,11 @@ export class RdsInstance extends Construct {
121
122
  const subnetType = props.publiclyAccessible
122
123
  ? SubnetType.PUBLIC
123
124
  : SubnetType.PRIVATE_WITH_EGRESS;
125
+ const backupRetention = props.backupRetention ??
126
+ Duration.days(RDS_DEFAULTS.BACKUP_RETENTION_DEFAULT_DAYS);
127
+ // toDays() throws on a fractional Duration — same whole-days contract the
128
+ // L2 DatabaseInstance enforces, surfaced at synth either way.
129
+ this.backupRetentionDaysValue = backupRetention.toDays();
124
130
  const commonInstanceProps = {
125
131
  vpc: this.vpc,
126
132
  vpcSubnets: {
@@ -130,8 +136,7 @@ export class RdsInstance extends Construct {
130
136
  engine,
131
137
  parameterGroup,
132
138
  allocatedStorage: props.allocatedStorage,
133
- backupRetention: props.backupRetention ??
134
- Duration.days(RDS_DEFAULTS.BACKUP_RETENTION_DEFAULT_DAYS),
139
+ backupRetention,
135
140
  preferredBackupWindow: props.preferredBackupWindow ?? "02:00-03:00",
136
141
  storageEncrypted: true,
137
142
  storageEncryptionKey,
@@ -357,6 +362,15 @@ exports.handler = async (event) => {
357
362
  getSnapshotTarget() {
358
363
  return { kind: "instance", arn: this.database.instanceArn };
359
364
  }
365
+ /**
366
+ * Effective automated-backup retention in whole days (the applied
367
+ * `backupRetention`, defaulted). Feeds `MIGRATION_SNAPSHOT_RETENTION_DAYS`
368
+ * so the migration runner prunes pre-migration snapshots against the same
369
+ * window the instance's PITR actually covers.
370
+ */
371
+ getBackupRetentionDays() {
372
+ return this.backupRetentionDaysValue;
373
+ }
360
374
  getDatabaseName() {
361
375
  return this.databaseNameValue;
362
376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/components-infrastructure",
3
- "version": "27.0.0",
3
+ "version": "27.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",
@@ -78,8 +78,8 @@
78
78
  },
79
79
  "dependencies": {
80
80
  "@aws-sdk/client-organizations": "^3.1098.0",
81
- "@fjall/generator": "^27.0.0",
82
- "@fjall/util": "^27.0.0",
81
+ "@fjall/generator": "^27.1.0",
82
+ "@fjall/util": "^27.1.0",
83
83
  "constructs": "^10.7.2",
84
84
  "zod": "^4.4.3"
85
85
  },