@friggframework/devtools 2.0.0--canary.640.b31eb4a.0 → 2.0.0--canary.640.5140601.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/infrastructure/__tests__/aurora-scale-to-zero-connectivity.test.js +104 -3
- package/infrastructure/domains/database/aurora-builder.js +106 -10
- package/infrastructure/domains/database/aurora-builder.test.js +1 -1
- package/infrastructure/domains/networking/vpc-builder.js +32 -2
- package/package.json +7 -7
|
@@ -57,9 +57,15 @@ describe('ADR-033: Aurora scale-to-zero + connectivity', () => {
|
|
|
57
57
|
describe('validator (AuroraBuilder.validate)', () => {
|
|
58
58
|
const build = new AuroraBuilder();
|
|
59
59
|
// ValidationResult exposes hasErrors(); "valid" means no errors.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
60
|
+
// vpc.enable defaults to true so connectivity:'public' cases don't trip the
|
|
61
|
+
// "public requires vpc.enable" rule; pass appOverrides to change it.
|
|
62
|
+
const validateResult = (postgres, appOverrides = {}) =>
|
|
63
|
+
build.validate({
|
|
64
|
+
vpc: { enable: true },
|
|
65
|
+
database: { postgres: { enable: true, ...postgres } },
|
|
66
|
+
...appOverrides,
|
|
67
|
+
});
|
|
68
|
+
const isValid = (postgres, appOverrides) => !validateResult(postgres, appOverrides).hasErrors();
|
|
63
69
|
|
|
64
70
|
test('accepts minCapacity: 0 (scale-to-zero)', () => {
|
|
65
71
|
expect(isValid({ minCapacity: 0 })).toBe(true);
|
|
@@ -102,6 +108,57 @@ describe('ADR-033: Aurora scale-to-zero + connectivity', () => {
|
|
|
102
108
|
expect(isValid({ allowedCidrs: ['10.0.0.0/8', '203.0.113.5/32'] })).toBe(true);
|
|
103
109
|
});
|
|
104
110
|
|
|
111
|
+
test('rejects out-of-range CIDR octets/prefix (numeric, not just shape)', () => {
|
|
112
|
+
expect(isValid({ allowedCidrs: ['999.999.999.999/99'] })).toBe(false);
|
|
113
|
+
expect(isValid({ allowedCidrs: ['10.0.0.0/33'] })).toBe(false);
|
|
114
|
+
expect(isValid({ allowedCidrs: ['256.1.1.1/24'] })).toBe(false);
|
|
115
|
+
expect(isValid({ allowedCidrs: ['0.0.0.0/0'] })).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('rejects empty allowedCidrs in public mode (no silent full-internet fallback)', () => {
|
|
119
|
+
const r = validateResult({ connectivity: 'public', allowedCidrs: [] });
|
|
120
|
+
expect(r.hasErrors()).toBe(true);
|
|
121
|
+
expect(r.errors.join(' ')).toMatch(/allowedCidrs is empty with connectivity="public"/);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('rejects minCapacity > maxCapacity', () => {
|
|
125
|
+
const r = validateResult({ minCapacity: 8, maxCapacity: 4 });
|
|
126
|
+
expect(r.hasErrors()).toBe(true);
|
|
127
|
+
expect(r.errors.join(' ')).toMatch(/minCapacity \(8\) must be <= maxCapacity \(4\)/);
|
|
128
|
+
// equal is fine
|
|
129
|
+
expect(isValid({ minCapacity: 4, maxCapacity: 4 })).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("connectivity:'public' requires vpc.enable=true", () => {
|
|
133
|
+
const r = validateResult({ connectivity: 'public' }, { vpc: { enable: false } });
|
|
134
|
+
expect(r.hasErrors()).toBe(true);
|
|
135
|
+
expect(r.errors.join(' ')).toMatch(/connectivity="public" requires vpc\.enable=true/);
|
|
136
|
+
// With vpc.enable true (default helper), it's valid
|
|
137
|
+
expect(isValid({ connectivity: 'public' })).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('warns when secondsUntilAutoPause set with minCapacity !== 0', () => {
|
|
141
|
+
const r = validateResult({ minCapacity: 0.5, secondsUntilAutoPause: 3600 });
|
|
142
|
+
expect(r.hasErrors()).toBe(false);
|
|
143
|
+
expect(r.warnings.join(' ')).toMatch(/secondsUntilAutoPause is ignored unless minCapacity is 0/);
|
|
144
|
+
// No such warning when minCapacity is 0
|
|
145
|
+
const r0 = validateResult({ minCapacity: 0, secondsUntilAutoPause: 3600 });
|
|
146
|
+
expect(r0.warnings.join(' ')).not.toMatch(/ignored unless minCapacity is 0/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("warns (not errors) for connectivity:'public' with discover/use-existing management", () => {
|
|
150
|
+
const rDiscover = validateResult({ connectivity: 'public', management: 'discover' });
|
|
151
|
+
expect(rDiscover.hasErrors()).toBe(false);
|
|
152
|
+
expect(rDiscover.warnings.join(' ')).toMatch(/assumes the EXISTING Aurora cluster is already publicly accessible/);
|
|
153
|
+
|
|
154
|
+
const rUseExisting = validateResult({
|
|
155
|
+
connectivity: 'public',
|
|
156
|
+
management: 'use-existing',
|
|
157
|
+
endpoint: 'db.example.com',
|
|
158
|
+
});
|
|
159
|
+
expect(rUseExisting.warnings.join(' ')).toMatch(/only affects TLS/);
|
|
160
|
+
});
|
|
161
|
+
|
|
105
162
|
test('warns (does not fail) when minCapacity:0 with an older pinned engine version', () => {
|
|
106
163
|
const r = validateResult({ minCapacity: 0, engineVersion: '15.4' });
|
|
107
164
|
expect(r.hasErrors()).toBe(false); // warning, not error
|
|
@@ -188,6 +245,45 @@ describe('ADR-033: Aurora scale-to-zero + connectivity', () => {
|
|
|
188
245
|
expect(nats).toHaveLength(0);
|
|
189
246
|
});
|
|
190
247
|
|
|
248
|
+
test('public subnets get an IGW default route + route table + both associations (create-new VPC)', async () => {
|
|
249
|
+
const t = await composeServerlessDefinition(makeApp({ connectivity: 'public' }));
|
|
250
|
+
const R = t.resources.Resources;
|
|
251
|
+
|
|
252
|
+
// Public route table
|
|
253
|
+
expect(R.FriggPublicRouteTable).toBeDefined();
|
|
254
|
+
expect(R.FriggPublicRouteTable.Type).toBe('AWS::EC2::RouteTable');
|
|
255
|
+
|
|
256
|
+
// 0.0.0.0/0 -> Internet Gateway default route
|
|
257
|
+
const igwRoutes = findResources(
|
|
258
|
+
t,
|
|
259
|
+
(r) =>
|
|
260
|
+
r.Type === 'AWS::EC2::Route' &&
|
|
261
|
+
r.Properties.DestinationCidrBlock === '0.0.0.0/0' &&
|
|
262
|
+
r.Properties.GatewayId &&
|
|
263
|
+
r.Properties.GatewayId.Ref === 'FriggInternetGateway'
|
|
264
|
+
);
|
|
265
|
+
expect(igwRoutes).toHaveLength(1);
|
|
266
|
+
|
|
267
|
+
// Both public subnet associations
|
|
268
|
+
expect(R.FriggPublicSubnet1RouteTableAssociation).toBeDefined();
|
|
269
|
+
expect(R.FriggPublicSubnet2RouteTableAssociation).toBeDefined();
|
|
270
|
+
expect(R.FriggPublicSubnet1RouteTableAssociation.Properties.RouteTableId).toEqual({
|
|
271
|
+
Ref: 'FriggPublicRouteTable',
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// And there must be NO NAT route (that would imply a NAT default route)
|
|
275
|
+
const natRoutes = findResources(
|
|
276
|
+
t,
|
|
277
|
+
(r) => r.Type === 'AWS::EC2::Route' && r.Properties.NatGatewayId
|
|
278
|
+
);
|
|
279
|
+
expect(natRoutes).toHaveLength(0);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('VPC_ENABLED is false in public mode (Lambda not VPC-attached)', async () => {
|
|
283
|
+
const t = await composeServerlessDefinition(makeApp({ connectivity: 'public' }));
|
|
284
|
+
expect(t.provider.environment.VPC_ENABLED).toBe('false');
|
|
285
|
+
});
|
|
286
|
+
|
|
191
287
|
test('Lambda is NOT attached to the VPC (provider.vpc unset)', async () => {
|
|
192
288
|
const t = await composeServerlessDefinition(makeApp({ connectivity: 'public' }));
|
|
193
289
|
expect(t.provider.vpc).toBeUndefined();
|
|
@@ -251,5 +347,10 @@ describe('ADR-033: Aurora scale-to-zero + connectivity', () => {
|
|
|
251
347
|
const url = t.provider.environment.DATABASE_URL;
|
|
252
348
|
expect(url['Fn::Sub'][0]).not.toContain('sslmode=require');
|
|
253
349
|
});
|
|
350
|
+
|
|
351
|
+
test('VPC_ENABLED stays true in vpc mode', async () => {
|
|
352
|
+
const t = await composeServerlessDefinition(makeApp());
|
|
353
|
+
expect(t.provider.environment.VPC_ENABLED).toBe('true');
|
|
354
|
+
});
|
|
254
355
|
});
|
|
255
356
|
});
|
|
@@ -107,6 +107,17 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
107
107
|
if (dbConfig.maxCapacity !== undefined && (dbConfig.maxCapacity < 0.5 || dbConfig.maxCapacity > 128)) {
|
|
108
108
|
result.addError('database.postgres.maxCapacity must be between 0.5 and 128');
|
|
109
109
|
}
|
|
110
|
+
// Cross-check: minCapacity must not exceed maxCapacity, or CloudFormation
|
|
111
|
+
// rejects the ServerlessV2ScalingConfiguration at deploy time.
|
|
112
|
+
if (
|
|
113
|
+
dbConfig.minCapacity !== undefined &&
|
|
114
|
+
dbConfig.maxCapacity !== undefined &&
|
|
115
|
+
dbConfig.minCapacity > dbConfig.maxCapacity
|
|
116
|
+
) {
|
|
117
|
+
result.addError(
|
|
118
|
+
`database.postgres.minCapacity (${dbConfig.minCapacity}) must be <= maxCapacity (${dbConfig.maxCapacity})`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
110
121
|
|
|
111
122
|
// Validate scale-to-zero auto-pause window (seconds). AWS-valid range is
|
|
112
123
|
// 300–86400 (5 minutes to 24 hours) and it must be an integer.
|
|
@@ -115,6 +126,12 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
115
126
|
if (!Number.isInteger(s) || s < 300 || s > 86400) {
|
|
116
127
|
result.addError('database.postgres.secondsUntilAutoPause must be an integer between 300 and 86400');
|
|
117
128
|
}
|
|
129
|
+
// secondsUntilAutoPause only takes effect for scale-to-zero clusters.
|
|
130
|
+
if (dbConfig.minCapacity !== 0) {
|
|
131
|
+
result.addWarning(
|
|
132
|
+
'database.postgres.secondsUntilAutoPause is ignored unless minCapacity is 0 (scale-to-zero).'
|
|
133
|
+
);
|
|
134
|
+
}
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
// Validate connectivity mode. 'vpc' (default) keeps today's behavior;
|
|
@@ -129,20 +146,37 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
129
146
|
}
|
|
130
147
|
}
|
|
131
148
|
|
|
132
|
-
//
|
|
149
|
+
// Public connectivity requires an enabled VPC (Aurora must live in a VPC —
|
|
150
|
+
// an AWS constraint — and the builder creates the public subnets there).
|
|
151
|
+
// Validate it here so the failure is a clear message rather than a downstream
|
|
152
|
+
// "Aurora requires 2 public subnets" throw.
|
|
153
|
+
if (dbConfig.connectivity === 'public' && appDefinition.vpc?.enable !== true) {
|
|
154
|
+
result.addError(
|
|
155
|
+
'database.postgres.connectivity="public" requires vpc.enable=true (Aurora needs public subnets in a VPC).'
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Validate allowedCidrs is an array of syntactically valid IPv4 CIDRs when
|
|
160
|
+
// present. NOTE: IPv4 only — IPv6 (CidrIpv6) is not supported here.
|
|
133
161
|
if (dbConfig.allowedCidrs !== undefined) {
|
|
134
|
-
const cidrPattern = /^([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}$/;
|
|
135
162
|
if (!Array.isArray(dbConfig.allowedCidrs)) {
|
|
136
163
|
result.addError('database.postgres.allowedCidrs must be an array of CIDR strings');
|
|
137
164
|
} else {
|
|
138
|
-
const bad = dbConfig.allowedCidrs.filter(
|
|
139
|
-
(c) => typeof c !== 'string' || !cidrPattern.test(c)
|
|
140
|
-
);
|
|
165
|
+
const bad = dbConfig.allowedCidrs.filter((c) => !this.isValidIpv4Cidr(c));
|
|
141
166
|
if (bad.length > 0) {
|
|
142
167
|
result.addError(
|
|
143
168
|
`database.postgres.allowedCidrs contains invalid CIDR value(s): ${bad.join(', ')}`
|
|
144
169
|
);
|
|
145
170
|
}
|
|
171
|
+
// An empty allowlist in public mode is almost always a mistake that
|
|
172
|
+
// would silently fall back to 0.0.0.0/0 (full internet exposure) —
|
|
173
|
+
// reject it so "allow nothing" cannot mean "allow everything".
|
|
174
|
+
if (dbConfig.allowedCidrs.length === 0 && dbConfig.connectivity === 'public') {
|
|
175
|
+
result.addError(
|
|
176
|
+
'database.postgres.allowedCidrs is empty with connectivity="public". List at least one CIDR ' +
|
|
177
|
+
"(use ['0.0.0.0/0'] to intentionally allow the whole internet)."
|
|
178
|
+
);
|
|
179
|
+
}
|
|
146
180
|
}
|
|
147
181
|
}
|
|
148
182
|
|
|
@@ -171,18 +205,67 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
171
205
|
'database.postgres.connectivity="public" exposes the Aurora endpoint to the internet. ' +
|
|
172
206
|
'TLS is enforced (sslmode=require) and access is restricted to allowedCidrs, but prefer "vpc" for production.'
|
|
173
207
|
);
|
|
174
|
-
|
|
208
|
+
// Only the default fallback (empty array is now an error above) or an
|
|
209
|
+
// explicit 0.0.0.0/0 reaches here as "whole internet".
|
|
210
|
+
const cidrs =
|
|
211
|
+
Array.isArray(dbConfig.allowedCidrs) && dbConfig.allowedCidrs.length > 0
|
|
212
|
+
? dbConfig.allowedCidrs
|
|
213
|
+
: ['0.0.0.0/0'];
|
|
175
214
|
if (cidrs.includes('0.0.0.0/0')) {
|
|
176
215
|
result.addWarning(
|
|
177
216
|
'database.postgres.allowedCidrs allows 0.0.0.0/0 (the whole internet). ' +
|
|
178
217
|
'Narrow this to known egress ranges wherever the deployment can.'
|
|
179
218
|
);
|
|
180
219
|
}
|
|
220
|
+
|
|
221
|
+
// Public connectivity only makes Frigg place the cluster in public
|
|
222
|
+
// subnets when Frigg CREATES the cluster (management='managed'). In
|
|
223
|
+
// discover/use-existing mode Frigg cannot flip an existing cluster to
|
|
224
|
+
// public subnets / PubliclyAccessible, yet the Lambda is still detached
|
|
225
|
+
// from the VPC — so if the existing cluster is private it becomes
|
|
226
|
+
// unreachable. Warn loudly. (management defaults to 'discover'.)
|
|
227
|
+
const mgmt = dbConfig.management || 'discover';
|
|
228
|
+
if (mgmt === 'discover' || mgmt === 'use-existing') {
|
|
229
|
+
result.addWarning(
|
|
230
|
+
`database.postgres.connectivity="public" with management="${mgmt}" assumes the EXISTING Aurora cluster ` +
|
|
231
|
+
'is already publicly accessible. Frigg detaches the Lambda from the VPC in public mode, so a private ' +
|
|
232
|
+
'existing cluster will be unreachable. Ensure the cluster has a public endpoint and open security group.'
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (mgmt === 'use-existing') {
|
|
236
|
+
result.addWarning(
|
|
237
|
+
'database.postgres.connectivity="public" with management="use-existing" only affects TLS (sslmode=require) on the ' +
|
|
238
|
+
'connection string — Frigg does not manage the existing cluster\'s subnets or ingress.'
|
|
239
|
+
);
|
|
240
|
+
}
|
|
181
241
|
}
|
|
182
242
|
|
|
183
243
|
return result;
|
|
184
244
|
}
|
|
185
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Validate an IPv4 CIDR string with real numeric range checks (each octet
|
|
248
|
+
* 0–255, prefix 0–32) — not just shape — so values like 999.999.999.999/99
|
|
249
|
+
* are rejected. IPv4 only; IPv6 is out of scope for allowedCidrs.
|
|
250
|
+
* @param {string} cidr
|
|
251
|
+
* @returns {boolean}
|
|
252
|
+
*/
|
|
253
|
+
isValidIpv4Cidr(cidr) {
|
|
254
|
+
if (typeof cidr !== 'string') {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
const match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/.exec(cidr);
|
|
258
|
+
if (!match) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
const octets = [match[1], match[2], match[3], match[4]].map(Number);
|
|
262
|
+
if (octets.some((o) => o > 255)) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
const prefix = Number(match[5]);
|
|
266
|
+
return prefix >= 0 && prefix <= 32;
|
|
267
|
+
}
|
|
268
|
+
|
|
186
269
|
/**
|
|
187
270
|
* Determine whether an Aurora PostgreSQL engine version supports
|
|
188
271
|
* Serverless v2 scale-to-zero (minCapacity: 0). Minimum capable versions:
|
|
@@ -429,9 +512,14 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
429
512
|
}
|
|
430
513
|
}
|
|
431
514
|
|
|
432
|
-
//
|
|
433
|
-
// NOTE:
|
|
434
|
-
//
|
|
515
|
+
// Mirror config into translated.database.postgres.config for completeness.
|
|
516
|
+
// NOTE: this is NOT the load-bearing path — createNewAurora/discoverAurora
|
|
517
|
+
// read the TOP-LEVEL database.postgres fields (preserved by the deep clone
|
|
518
|
+
// above), and 0-propagation for scale-to-zero comes from that clone plus the
|
|
519
|
+
// `?? 0.5` in the scaling config, not from here. These `.config.*` copies are
|
|
520
|
+
// kept only so any future consumer that reads `config` sees the same values;
|
|
521
|
+
// `!== undefined` (not truthiness) is used so a minCapacity of 0 is mirrored
|
|
522
|
+
// rather than dropped.
|
|
435
523
|
if (appDefinition.database?.postgres?.minCapacity !== undefined) {
|
|
436
524
|
translated.database.postgres.config.minCapacity = appDefinition.database.postgres.minCapacity;
|
|
437
525
|
}
|
|
@@ -679,6 +767,12 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
679
767
|
throw new Error('database.postgres.endpoint is required when management="use-existing"');
|
|
680
768
|
}
|
|
681
769
|
|
|
770
|
+
// ADR-033: for use-existing, `connectivity` only affects TLS on the
|
|
771
|
+
// connection params — Frigg does not own the cluster, so it manages neither
|
|
772
|
+
// its subnets nor its ingress (the validator warns about this). Public mode
|
|
773
|
+
// still requires TLS, so append sslmode=require.
|
|
774
|
+
const publicConnectivity = dbConfig.connectivity === 'public';
|
|
775
|
+
|
|
682
776
|
// Set environment variables for existing cluster
|
|
683
777
|
result.environment.DATABASE_HOST = dbConfig.endpoint;
|
|
684
778
|
result.environment.DATABASE_PORT = String(dbConfig.port || 5432);
|
|
@@ -687,7 +781,9 @@ class AuroraBuilder extends InfrastructureBuilder {
|
|
|
687
781
|
// Consumers that build DATABASE_URL from components at runtime MUST
|
|
688
782
|
// append `?${DATABASE_URL_PARAMS}` to get the same hang-prevention
|
|
689
783
|
// timeouts as the managed path.
|
|
690
|
-
result.environment.DATABASE_URL_PARAMS =
|
|
784
|
+
result.environment.DATABASE_URL_PARAMS = publicConnectivity
|
|
785
|
+
? `${LAMBDA_DATABASE_URL_QUERY_PARAMS}&sslmode=require`
|
|
786
|
+
: LAMBDA_DATABASE_URL_QUERY_PARAMS;
|
|
691
787
|
|
|
692
788
|
console.log(` ✅ Using existing cluster: ${dbConfig.endpoint}`);
|
|
693
789
|
}
|
|
@@ -194,7 +194,7 @@ describe('AuroraBuilder', () => {
|
|
|
194
194
|
const result = auroraBuilder.validate(appDefinition);
|
|
195
195
|
|
|
196
196
|
expect(result.valid).toBe(false);
|
|
197
|
-
expect(result.errors.some(e => e.includes('minCapacity must be between 0.5 and 128'))).toBe(true);
|
|
197
|
+
expect(result.errors.some(e => e.includes('minCapacity must be 0 (scale-to-zero) or between 0.5 and 128'))).toBe(true);
|
|
198
198
|
});
|
|
199
199
|
|
|
200
200
|
it('should error when maxCapacity is out of range', () => {
|
|
@@ -603,6 +603,34 @@ class VpcBuilder extends InfrastructureBuilder {
|
|
|
603
603
|
console.log(
|
|
604
604
|
' ⊝ NAT Gateway skipped (database.postgres.connectivity=public — Lambda is not VPC-attached, so no NAT is needed)'
|
|
605
605
|
);
|
|
606
|
+
|
|
607
|
+
// ADR-033: the public subnets Aurora sits in still need an Internet
|
|
608
|
+
// Gateway default route + subnet→route-table associations to be
|
|
609
|
+
// internet-routable. Those are normally emitted only as a SIDE EFFECT
|
|
610
|
+
// of the NAT build (createPublicRouting is called from inside the NAT
|
|
611
|
+
// methods), so skipping NAT would otherwise leave the public subnets on
|
|
612
|
+
// the VPC main route table (local-only) and the public Aurora endpoint
|
|
613
|
+
// unreachable — a green deploy with a dead DB. Decouple the public-subnet
|
|
614
|
+
// routing from NAT here.
|
|
615
|
+
//
|
|
616
|
+
// GUARD: only do this for a Frigg-created (stack) VPC — signalled by the
|
|
617
|
+
// presence of FriggInternetGateway in the template (emitted by
|
|
618
|
+
// buildVpcFromDecision only for STACK ownership). createPublicRouting
|
|
619
|
+
// references { Ref: 'FriggInternetGateway' } and DependsOn
|
|
620
|
+
// 'FriggVPCGatewayAttachment', and associates the stack-created
|
|
621
|
+
// FriggPublicSubnet* — all of which exist only in that case. For a
|
|
622
|
+
// discovered/existing VPC, its public subnets already route to an IGW, so
|
|
623
|
+
// creating our own public route table would be redundant/conflicting.
|
|
624
|
+
if (result.resources.FriggInternetGateway) {
|
|
625
|
+
console.log(
|
|
626
|
+
' → Public-subnet routing (IGW default route + associations) for stack-created VPC'
|
|
627
|
+
);
|
|
628
|
+
this.createPublicRouting(appDefinition, discoveredResources, result);
|
|
629
|
+
} else {
|
|
630
|
+
console.log(
|
|
631
|
+
' ℹ Public connectivity on a discovered/existing VPC — assuming its public subnets already route to an Internet Gateway; not creating conflicting routing'
|
|
632
|
+
);
|
|
633
|
+
}
|
|
606
634
|
} else {
|
|
607
635
|
// Build NAT Gateway based on ownership decision
|
|
608
636
|
this.buildNatGatewayFromDecision(decisions.natGateway, appDefinition, discoveredResources, result);
|
|
@@ -620,8 +648,10 @@ class VpcBuilder extends InfrastructureBuilder {
|
|
|
620
648
|
this.buildVpcEndpointsFromDecisions(decisions.vpcEndpoints, decisions.securityGroup, appDefinition, discoveredResources, result);
|
|
621
649
|
}
|
|
622
650
|
|
|
623
|
-
// Set VPC_ENABLED environment variable
|
|
624
|
-
|
|
651
|
+
// Set VPC_ENABLED environment variable.
|
|
652
|
+
// ADR-033: in public connectivity the Lambda is NOT attached to the VPC, so
|
|
653
|
+
// report VPC_ENABLED=false — otherwise /health would misreport isInVpc:true.
|
|
654
|
+
result.environment.VPC_ENABLED = publicDbConnectivity ? 'false' : 'true';
|
|
625
655
|
|
|
626
656
|
console.log(`\n[${this.name}] ✅ VPC infrastructure built successfully`);
|
|
627
657
|
console.log(` - VPC ID: ${result.vpcId || 'from discovery'}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.640.
|
|
4
|
+
"version": "2.0.0--canary.640.5140601.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"@babel/eslint-parser": "^7.18.9",
|
|
27
27
|
"@babel/parser": "^7.25.3",
|
|
28
28
|
"@babel/traverse": "^7.25.3",
|
|
29
|
-
"@friggframework/core": "2.0.0--canary.640.
|
|
30
|
-
"@friggframework/schemas": "2.0.0--canary.640.
|
|
31
|
-
"@friggframework/test": "2.0.0--canary.640.
|
|
29
|
+
"@friggframework/core": "2.0.0--canary.640.5140601.0",
|
|
30
|
+
"@friggframework/schemas": "2.0.0--canary.640.5140601.0",
|
|
31
|
+
"@friggframework/test": "2.0.0--canary.640.5140601.0",
|
|
32
32
|
"@hapi/boom": "^10.0.1",
|
|
33
33
|
"@inquirer/prompts": "^5.3.8",
|
|
34
34
|
"axios": "^1.18.0",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"validate-npm-package-name": "^5.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@friggframework/eslint-config": "2.0.0--canary.640.
|
|
60
|
-
"@friggframework/prettier-config": "2.0.0--canary.640.
|
|
59
|
+
"@friggframework/eslint-config": "2.0.0--canary.640.5140601.0",
|
|
60
|
+
"@friggframework/prettier-config": "2.0.0--canary.640.5140601.0",
|
|
61
61
|
"aws-sdk-client-mock": "^4.1.0",
|
|
62
62
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
63
63
|
"jest": "^30.1.3",
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"publishConfig": {
|
|
90
90
|
"access": "public"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "51406015cf670753915e87f84a29f2d6067cd5be"
|
|
93
93
|
}
|