@friggframework/devtools 2.0.0--canary.463.62579dd.0 → 2.0.0--canary.461.ec909cf.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/frigg-cli/__tests__/unit/commands/db-setup.test.js +1 -1
- package/frigg-cli/db-setup-command/index.js +1 -1
- package/infrastructure/POSTGRES-CONFIGURATION.md +630 -0
- package/infrastructure/README.md +51 -0
- package/infrastructure/__tests__/postgres-config.test.js +914 -0
- package/infrastructure/aws-discovery.js +549 -21
- package/infrastructure/aws-discovery.test.js +447 -1
- package/infrastructure/domains/database/aurora-builder.js +307 -0
- package/infrastructure/domains/database/aurora-builder.test.js +482 -0
- package/infrastructure/domains/networking/vpc-builder.js +718 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +772 -0
- package/infrastructure/domains/networking/vpc-discovery.js +159 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +445 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +385 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.js +129 -0
- package/infrastructure/infrastructure-composer.test.js +1895 -0
- package/infrastructure/scripts/build-prisma-layer.js +534 -0
- package/infrastructure/serverless-template.js +790 -84
- package/infrastructure/serverless-template.test.js +94 -1
- package/package.json +8 -6
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
- package/frigg-cli/utils/prisma-runner.js +0 -280
|
@@ -21,11 +21,23 @@ const {
|
|
|
21
21
|
STSClient,
|
|
22
22
|
GetCallerIdentityCommand
|
|
23
23
|
} = require('@aws-sdk/client-sts');
|
|
24
|
+
const {
|
|
25
|
+
RDSClient,
|
|
26
|
+
DescribeDBClustersCommand,
|
|
27
|
+
DescribeDBSubnetGroupsCommand
|
|
28
|
+
} = require('@aws-sdk/client-rds');
|
|
29
|
+
const {
|
|
30
|
+
SecretsManagerClient,
|
|
31
|
+
ListSecretsCommand,
|
|
32
|
+
DescribeSecretCommand
|
|
33
|
+
} = require('@aws-sdk/client-secrets-manager');
|
|
24
34
|
|
|
25
35
|
// Create mock clients
|
|
26
36
|
const ec2Mock = mockClient(EC2Client);
|
|
27
37
|
const kmsMock = mockClient(KMSClient);
|
|
28
38
|
const stsMock = mockClient(STSClient);
|
|
39
|
+
const rdsMock = mockClient(RDSClient);
|
|
40
|
+
const secretsManagerMock = mockClient(SecretsManagerClient);
|
|
29
41
|
|
|
30
42
|
describe('AWSDiscovery', () => {
|
|
31
43
|
let discovery;
|
|
@@ -35,10 +47,71 @@ describe('AWSDiscovery', () => {
|
|
|
35
47
|
ec2Mock.reset();
|
|
36
48
|
kmsMock.reset();
|
|
37
49
|
stsMock.reset();
|
|
50
|
+
rdsMock.reset();
|
|
51
|
+
secretsManagerMock.reset();
|
|
52
|
+
|
|
53
|
+
// Set up default STS mock for credential validation
|
|
54
|
+
// This is needed because validateCredentials() is now called in discoverResources()
|
|
55
|
+
stsMock.on(GetCallerIdentityCommand).resolves({
|
|
56
|
+
Account: '123456789012',
|
|
57
|
+
Arn: 'arn:aws:iam::123456789012:user/test-user',
|
|
58
|
+
UserId: 'AIDAI1234567890EXAMPLE'
|
|
59
|
+
});
|
|
38
60
|
|
|
39
61
|
discovery = new AWSDiscovery('us-east-1');
|
|
40
62
|
});
|
|
41
63
|
|
|
64
|
+
describe('validateCredentials', () => {
|
|
65
|
+
it('should return valid credentials info when credentials are valid', async () => {
|
|
66
|
+
const mockResponse = {
|
|
67
|
+
Account: '123456789012',
|
|
68
|
+
Arn: 'arn:aws:iam::123456789012:user/test-user',
|
|
69
|
+
UserId: 'AIDAI1234567890EXAMPLE'
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
stsMock.reset();
|
|
73
|
+
stsMock.on(GetCallerIdentityCommand).resolves(mockResponse);
|
|
74
|
+
|
|
75
|
+
const result = await discovery.validateCredentials();
|
|
76
|
+
|
|
77
|
+
expect(result).toEqual({
|
|
78
|
+
valid: true,
|
|
79
|
+
accountId: '123456789012',
|
|
80
|
+
arn: 'arn:aws:iam::123456789012:user/test-user',
|
|
81
|
+
userId: 'AIDAI1234567890EXAMPLE'
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should throw descriptive error for expired credentials', async () => {
|
|
86
|
+
stsMock.reset();
|
|
87
|
+
const expiredError = new Error('Request has expired.');
|
|
88
|
+
expiredError.Code = 'RequestExpired';
|
|
89
|
+
stsMock.on(GetCallerIdentityCommand).rejects(expiredError);
|
|
90
|
+
|
|
91
|
+
await expect(discovery.validateCredentials())
|
|
92
|
+
.rejects.toThrow('AWS credential validation failed: Request has expired.');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should throw descriptive error for invalid credentials', async () => {
|
|
96
|
+
stsMock.reset();
|
|
97
|
+
const invalidError = new Error('The security token included in the request is invalid');
|
|
98
|
+
invalidError.Code = 'InvalidClientTokenId';
|
|
99
|
+
stsMock.on(GetCallerIdentityCommand).rejects(invalidError);
|
|
100
|
+
|
|
101
|
+
await expect(discovery.validateCredentials())
|
|
102
|
+
.rejects.toThrow('AWS credential validation failed');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should throw descriptive error when credentials cannot be loaded', async () => {
|
|
106
|
+
stsMock.reset();
|
|
107
|
+
const noCredsError = new Error('Could not load credentials from any providers');
|
|
108
|
+
stsMock.on(GetCallerIdentityCommand).rejects(noCredsError);
|
|
109
|
+
|
|
110
|
+
await expect(discovery.validateCredentials())
|
|
111
|
+
.rejects.toThrow('AWS credential validation failed');
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
42
115
|
describe('getAccountId', () => {
|
|
43
116
|
it('should return AWS account ID', async () => {
|
|
44
117
|
const mockAccountId = '123456789012';
|
|
@@ -1100,7 +1173,7 @@ describe('AWSDiscovery', () => {
|
|
|
1100
1173
|
jest.spyOn(discovery, 'isSubnetPrivate')
|
|
1101
1174
|
.mockResolvedValue(false); // All subnets are public
|
|
1102
1175
|
|
|
1103
|
-
const result = await discovery.discoverResources({ selfHeal: true });
|
|
1176
|
+
const result = await discovery.discoverResources({ vpc: { selfHeal: true } });
|
|
1104
1177
|
|
|
1105
1178
|
// Verify that findPrivateSubnets was called with autoConvert=true
|
|
1106
1179
|
expect(discovery.findPrivateSubnets).toHaveBeenCalledWith('vpc-12345678', true);
|
|
@@ -1217,4 +1290,377 @@ describe('AWSDiscovery', () => {
|
|
|
1217
1290
|
expect(customDiscovery.region).toBe('us-west-2');
|
|
1218
1291
|
});
|
|
1219
1292
|
});
|
|
1293
|
+
|
|
1294
|
+
describe('Aurora PostgreSQL Discovery', () => {
|
|
1295
|
+
describe('findAuroraCluster', () => {
|
|
1296
|
+
it('should return cluster matching specific clusterIdentifier', async () => {
|
|
1297
|
+
const mockClusters = {
|
|
1298
|
+
DBClusters: [
|
|
1299
|
+
{
|
|
1300
|
+
DBClusterIdentifier: 'target-cluster',
|
|
1301
|
+
Engine: 'aurora-postgresql',
|
|
1302
|
+
Status: 'available',
|
|
1303
|
+
Endpoint: 'target.cluster.us-east-1.rds.amazonaws.com',
|
|
1304
|
+
Port: 5432,
|
|
1305
|
+
EngineVersion: '15.3',
|
|
1306
|
+
MasterUsername: 'admin',
|
|
1307
|
+
DatabaseName: 'mydb',
|
|
1308
|
+
TagList: []
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
DBClusterIdentifier: 'other-cluster',
|
|
1312
|
+
Engine: 'aurora-postgresql',
|
|
1313
|
+
Status: 'available',
|
|
1314
|
+
Endpoint: 'other.cluster.us-east-1.rds.amazonaws.com',
|
|
1315
|
+
Port: 5432,
|
|
1316
|
+
TagList: []
|
|
1317
|
+
}
|
|
1318
|
+
]
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
rdsMock.on(DescribeDBClustersCommand).resolves(mockClusters);
|
|
1322
|
+
|
|
1323
|
+
const result = await discovery.findAuroraCluster('target-cluster');
|
|
1324
|
+
|
|
1325
|
+
expect(result).toBeDefined();
|
|
1326
|
+
expect(result.identifier).toBe('target-cluster');
|
|
1327
|
+
expect(result.endpoint).toBe('target.cluster.us-east-1.rds.amazonaws.com');
|
|
1328
|
+
expect(result.port).toBe(5432);
|
|
1329
|
+
});
|
|
1330
|
+
|
|
1331
|
+
it('should prioritize Frigg-managed cluster with matching service+stage tags', async () => {
|
|
1332
|
+
const mockClusters = {
|
|
1333
|
+
DBClusters: [
|
|
1334
|
+
{
|
|
1335
|
+
DBClusterIdentifier: 'generic-frigg-cluster',
|
|
1336
|
+
Engine: 'aurora-postgresql',
|
|
1337
|
+
Status: 'available',
|
|
1338
|
+
TagList: [
|
|
1339
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1340
|
+
]
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
DBClusterIdentifier: 'matching-cluster',
|
|
1344
|
+
Engine: 'aurora-postgresql',
|
|
1345
|
+
Status: 'available',
|
|
1346
|
+
Endpoint: 'matching.cluster.us-east-1.rds.amazonaws.com',
|
|
1347
|
+
Port: 5432,
|
|
1348
|
+
TagList: [
|
|
1349
|
+
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1350
|
+
{ Key: 'Service', Value: 'test-service' },
|
|
1351
|
+
{ Key: 'Stage', Value: 'dev' }
|
|
1352
|
+
]
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
DBClusterIdentifier: 'non-frigg-cluster',
|
|
1356
|
+
Engine: 'aurora-postgresql',
|
|
1357
|
+
Status: 'available',
|
|
1358
|
+
TagList: []
|
|
1359
|
+
}
|
|
1360
|
+
]
|
|
1361
|
+
};
|
|
1362
|
+
|
|
1363
|
+
rdsMock.on(DescribeDBClustersCommand).resolves(mockClusters);
|
|
1364
|
+
|
|
1365
|
+
const result = await discovery.findAuroraCluster(null, 'test-service', 'dev');
|
|
1366
|
+
|
|
1367
|
+
expect(result).toBeDefined();
|
|
1368
|
+
expect(result.identifier).toBe('matching-cluster');
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
it('should fall back to any Frigg-managed cluster', async () => {
|
|
1372
|
+
const mockClusters = {
|
|
1373
|
+
DBClusters: [
|
|
1374
|
+
{
|
|
1375
|
+
DBClusterIdentifier: 'non-frigg-cluster',
|
|
1376
|
+
Engine: 'aurora-postgresql',
|
|
1377
|
+
Status: 'available',
|
|
1378
|
+
TagList: []
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
DBClusterIdentifier: 'frigg-cluster',
|
|
1382
|
+
Engine: 'aurora-postgresql',
|
|
1383
|
+
Status: 'available',
|
|
1384
|
+
Endpoint: 'frigg.cluster.us-east-1.rds.amazonaws.com',
|
|
1385
|
+
Port: 5432,
|
|
1386
|
+
TagList: [
|
|
1387
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1388
|
+
]
|
|
1389
|
+
}
|
|
1390
|
+
]
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
rdsMock.on(DescribeDBClustersCommand).resolves(mockClusters);
|
|
1394
|
+
|
|
1395
|
+
const result = await discovery.findAuroraCluster(null, 'different-service', 'prod');
|
|
1396
|
+
|
|
1397
|
+
expect(result).toBeDefined();
|
|
1398
|
+
expect(result.identifier).toBe('frigg-cluster');
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
it('should return first available cluster as last fallback', async () => {
|
|
1402
|
+
const mockClusters = {
|
|
1403
|
+
DBClusters: [
|
|
1404
|
+
{
|
|
1405
|
+
DBClusterIdentifier: 'first-cluster',
|
|
1406
|
+
Engine: 'aurora-postgresql',
|
|
1407
|
+
Status: 'available',
|
|
1408
|
+
Endpoint: 'first.cluster.us-east-1.rds.amazonaws.com',
|
|
1409
|
+
Port: 5432,
|
|
1410
|
+
TagList: []
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
DBClusterIdentifier: 'second-cluster',
|
|
1414
|
+
Engine: 'aurora-postgresql',
|
|
1415
|
+
Status: 'available',
|
|
1416
|
+
TagList: []
|
|
1417
|
+
}
|
|
1418
|
+
]
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1421
|
+
rdsMock.on(DescribeDBClustersCommand).resolves(mockClusters);
|
|
1422
|
+
|
|
1423
|
+
const result = await discovery.findAuroraCluster();
|
|
1424
|
+
|
|
1425
|
+
expect(result).toBeDefined();
|
|
1426
|
+
expect(result.identifier).toBe('first-cluster');
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
it('should return null when no clusters found', async () => {
|
|
1430
|
+
rdsMock.on(DescribeDBClustersCommand).resolves({ DBClusters: [] });
|
|
1431
|
+
|
|
1432
|
+
const result = await discovery.findAuroraCluster();
|
|
1433
|
+
|
|
1434
|
+
expect(result).toBeNull();
|
|
1435
|
+
});
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
describe('findDBSubnetGroup', () => {
|
|
1439
|
+
it('should return Frigg-managed subnet group in VPC', async () => {
|
|
1440
|
+
const mockSubnetGroups = {
|
|
1441
|
+
DBSubnetGroups: [
|
|
1442
|
+
{
|
|
1443
|
+
DBSubnetGroupName: 'other-subnet-group',
|
|
1444
|
+
VpcId: 'vpc-12345',
|
|
1445
|
+
Subnets: [{ SubnetIdentifier: 'subnet-1' }],
|
|
1446
|
+
Tags: []
|
|
1447
|
+
},
|
|
1448
|
+
{
|
|
1449
|
+
DBSubnetGroupName: 'frigg-subnet-group',
|
|
1450
|
+
VpcId: 'vpc-12345',
|
|
1451
|
+
Subnets: [
|
|
1452
|
+
{ SubnetIdentifier: 'subnet-1' },
|
|
1453
|
+
{ SubnetIdentifier: 'subnet-2' }
|
|
1454
|
+
],
|
|
1455
|
+
DBSubnetGroupDescription: 'Frigg managed',
|
|
1456
|
+
Tags: [
|
|
1457
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1458
|
+
]
|
|
1459
|
+
}
|
|
1460
|
+
]
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
rdsMock.on(DescribeDBSubnetGroupsCommand).resolves(mockSubnetGroups);
|
|
1464
|
+
|
|
1465
|
+
const result = await discovery.findDBSubnetGroup('vpc-12345');
|
|
1466
|
+
|
|
1467
|
+
expect(result).toBeDefined();
|
|
1468
|
+
expect(result.name).toBe('frigg-subnet-group');
|
|
1469
|
+
expect(result.vpcId).toBe('vpc-12345');
|
|
1470
|
+
expect(result.subnets).toEqual(['subnet-1', 'subnet-2']);
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
it('should return first available subnet group as fallback', async () => {
|
|
1474
|
+
const mockSubnetGroups = {
|
|
1475
|
+
DBSubnetGroups: [
|
|
1476
|
+
{
|
|
1477
|
+
DBSubnetGroupName: 'first-subnet-group',
|
|
1478
|
+
VpcId: 'vpc-12345',
|
|
1479
|
+
Subnets: [{ SubnetIdentifier: 'subnet-a' }],
|
|
1480
|
+
DBSubnetGroupDescription: 'First group',
|
|
1481
|
+
Tags: []
|
|
1482
|
+
}
|
|
1483
|
+
]
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
rdsMock.on(DescribeDBSubnetGroupsCommand).resolves(mockSubnetGroups);
|
|
1487
|
+
|
|
1488
|
+
const result = await discovery.findDBSubnetGroup('vpc-12345');
|
|
1489
|
+
|
|
1490
|
+
expect(result).toBeDefined();
|
|
1491
|
+
expect(result.name).toBe('first-subnet-group');
|
|
1492
|
+
});
|
|
1493
|
+
|
|
1494
|
+
it('should return null when no subnet groups found in VPC', async () => {
|
|
1495
|
+
const mockSubnetGroups = {
|
|
1496
|
+
DBSubnetGroups: [
|
|
1497
|
+
{
|
|
1498
|
+
DBSubnetGroupName: 'wrong-vpc-group',
|
|
1499
|
+
VpcId: 'vpc-wrong',
|
|
1500
|
+
Subnets: [{ SubnetIdentifier: 'subnet-x' }],
|
|
1501
|
+
Tags: []
|
|
1502
|
+
}
|
|
1503
|
+
]
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
rdsMock.on(DescribeDBSubnetGroupsCommand).resolves(mockSubnetGroups);
|
|
1507
|
+
|
|
1508
|
+
const result = await discovery.findDBSubnetGroup('vpc-12345');
|
|
1509
|
+
|
|
1510
|
+
expect(result).toBeNull();
|
|
1511
|
+
});
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
describe('findDatabaseSecret', () => {
|
|
1515
|
+
it('should return secret with matching service+stage tags', async () => {
|
|
1516
|
+
const mockSecrets = {
|
|
1517
|
+
SecretList: [
|
|
1518
|
+
{
|
|
1519
|
+
ARN: 'arn:aws:secretsmanager:us-east-1:123:secret:generic-secret',
|
|
1520
|
+
Name: 'generic-aurora-secret',
|
|
1521
|
+
Tags: [
|
|
1522
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1523
|
+
]
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
ARN: 'arn:aws:secretsmanager:us-east-1:123:secret:matching-secret',
|
|
1527
|
+
Name: 'test-service-dev-aurora-credentials',
|
|
1528
|
+
Tags: [
|
|
1529
|
+
{ Key: 'ManagedBy', Value: 'Frigg' },
|
|
1530
|
+
{ Key: 'Service', Value: 'test-service' },
|
|
1531
|
+
{ Key: 'Stage', Value: 'dev' }
|
|
1532
|
+
]
|
|
1533
|
+
}
|
|
1534
|
+
]
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
secretsManagerMock.on(ListSecretsCommand).resolves(mockSecrets);
|
|
1538
|
+
|
|
1539
|
+
const result = await discovery.findDatabaseSecret('test-service', 'dev');
|
|
1540
|
+
|
|
1541
|
+
expect(result).toBeDefined();
|
|
1542
|
+
expect(result.name).toBe('test-service-dev-aurora-credentials');
|
|
1543
|
+
expect(result.arn).toBe('arn:aws:secretsmanager:us-east-1:123:secret:matching-secret');
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
it('should return any Frigg-managed database secret as fallback', async () => {
|
|
1547
|
+
const mockSecrets = {
|
|
1548
|
+
SecretList: [
|
|
1549
|
+
{
|
|
1550
|
+
ARN: 'arn:aws:secretsmanager:us-east-1:123:secret:frigg-secret',
|
|
1551
|
+
Name: 'frigg-database-credentials',
|
|
1552
|
+
Tags: [
|
|
1553
|
+
{ Key: 'ManagedBy', Value: 'Frigg' }
|
|
1554
|
+
]
|
|
1555
|
+
}
|
|
1556
|
+
]
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1559
|
+
secretsManagerMock.on(ListSecretsCommand).resolves(mockSecrets);
|
|
1560
|
+
|
|
1561
|
+
const result = await discovery.findDatabaseSecret('different-service', 'staging');
|
|
1562
|
+
|
|
1563
|
+
expect(result).toBeDefined();
|
|
1564
|
+
expect(result.name).toBe('frigg-database-credentials');
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
it('should return null when no Frigg database secrets found', async () => {
|
|
1568
|
+
const mockSecrets = {
|
|
1569
|
+
SecretList: [
|
|
1570
|
+
{
|
|
1571
|
+
ARN: 'arn:aws:secretsmanager:us-east-1:123:secret:other',
|
|
1572
|
+
Name: 'other-secret',
|
|
1573
|
+
Tags: []
|
|
1574
|
+
}
|
|
1575
|
+
]
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
secretsManagerMock.on(ListSecretsCommand).resolves(mockSecrets);
|
|
1579
|
+
|
|
1580
|
+
const result = await discovery.findDatabaseSecret('test-service', 'dev');
|
|
1581
|
+
|
|
1582
|
+
expect(result).toBeNull();
|
|
1583
|
+
});
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
describe('discoverAuroraResources', () => {
|
|
1587
|
+
it('should return needsCreation=true in create-new mode', async () => {
|
|
1588
|
+
const result = await discovery.discoverAuroraResources({
|
|
1589
|
+
vpcId: 'vpc-123',
|
|
1590
|
+
management: 'create-new'
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
expect(result.needsCreation).toBe(true);
|
|
1594
|
+
expect(result.clusterIdentifier).toBeNull();
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
it('should discover all resources in discover mode', async () => {
|
|
1598
|
+
const mockClusters = {
|
|
1599
|
+
DBClusters: [{
|
|
1600
|
+
DBClusterIdentifier: 'discovered-cluster',
|
|
1601
|
+
Engine: 'aurora-postgresql',
|
|
1602
|
+
Status: 'available',
|
|
1603
|
+
Endpoint: 'discovered.cluster.us-east-1.rds.amazonaws.com',
|
|
1604
|
+
Port: 5432,
|
|
1605
|
+
EngineVersion: '15.3',
|
|
1606
|
+
TagList: []
|
|
1607
|
+
}]
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
const mockSubnetGroups = {
|
|
1611
|
+
DBSubnetGroups: [{
|
|
1612
|
+
DBSubnetGroupName: 'discovered-subnet-group',
|
|
1613
|
+
VpcId: 'vpc-123',
|
|
1614
|
+
Subnets: [{ SubnetIdentifier: 'subnet-1' }, { SubnetIdentifier: 'subnet-2' }],
|
|
1615
|
+
Tags: []
|
|
1616
|
+
}]
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
const mockSecrets = {
|
|
1620
|
+
SecretList: [{
|
|
1621
|
+
ARN: 'arn:aws:secretsmanager:us-east-1:123:secret:discovered-secret',
|
|
1622
|
+
Name: 'discovered-aurora-credentials',
|
|
1623
|
+
Tags: [{ Key: 'ManagedBy', Value: 'Frigg' }]
|
|
1624
|
+
}]
|
|
1625
|
+
};
|
|
1626
|
+
|
|
1627
|
+
rdsMock.on(DescribeDBClustersCommand).resolves(mockClusters);
|
|
1628
|
+
rdsMock.on(DescribeDBSubnetGroupsCommand).resolves(mockSubnetGroups);
|
|
1629
|
+
secretsManagerMock.on(ListSecretsCommand).resolves(mockSecrets);
|
|
1630
|
+
|
|
1631
|
+
const result = await discovery.discoverAuroraResources({
|
|
1632
|
+
vpcId: 'vpc-123',
|
|
1633
|
+
management: 'discover'
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
expect(result.needsCreation).toBe(false);
|
|
1637
|
+
expect(result.clusterIdentifier).toBe('discovered-cluster');
|
|
1638
|
+
expect(result.endpoint).toBe('discovered.cluster.us-east-1.rds.amazonaws.com');
|
|
1639
|
+
expect(result.port).toBe(5432);
|
|
1640
|
+
expect(result.dbSubnetGroupName).toBe('discovered-subnet-group');
|
|
1641
|
+
expect(result.secretArn).toBe('arn:aws:secretsmanager:us-east-1:123:secret:discovered-secret');
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
it('should require clusterIdentifier in use-existing mode and throw error if missing', async () => {
|
|
1645
|
+
await expect(
|
|
1646
|
+
discovery.discoverAuroraResources({
|
|
1647
|
+
vpcId: 'vpc-123',
|
|
1648
|
+
management: 'use-existing'
|
|
1649
|
+
})
|
|
1650
|
+
).rejects.toThrow('clusterIdentifier is required');
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
it('should return needsCreation=true when no cluster found in discover mode', async () => {
|
|
1654
|
+
rdsMock.on(DescribeDBClustersCommand).resolves({ DBClusters: [] });
|
|
1655
|
+
|
|
1656
|
+
const result = await discovery.discoverAuroraResources({
|
|
1657
|
+
vpcId: 'vpc-123',
|
|
1658
|
+
management: 'discover'
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
expect(result.needsCreation).toBe(true);
|
|
1662
|
+
expect(result.clusterIdentifier).toBeNull();
|
|
1663
|
+
});
|
|
1664
|
+
});
|
|
1665
|
+
});
|
|
1220
1666
|
});
|