@gugananuvem/aws-local-simulator 1.0.35 → 1.0.39
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/README.md +662 -658
- package/package.json +10 -3
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +12 -1
- package/src/services/lambda/simulator.js +4 -0
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- package/aws-config +0 -154
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route53 Simulator
|
|
3
|
+
* DNS and domain management with weighted routing, health checks, and failover
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const HostedZone = require('./models/hostedZone');
|
|
8
|
+
const ResourceRecord = require('./models/resourceRecord');
|
|
9
|
+
const HealthCheck = require('./models/healthCheck');
|
|
10
|
+
const LocalStore = require('../../utils/local-store');
|
|
11
|
+
const logger = require('../../utils/logger');
|
|
12
|
+
|
|
13
|
+
class Route53Simulator {
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.hostedZones = new Map();
|
|
17
|
+
this.resourceRecords = new Map(); // zoneId -> records[]
|
|
18
|
+
this.healthChecks = new Map();
|
|
19
|
+
this.dnsResolution = new Map(); // domain -> resolved records
|
|
20
|
+
|
|
21
|
+
// Inicializar persistência
|
|
22
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
|
|
23
|
+
this.dataDir = path.join(dataDir, 'route53');
|
|
24
|
+
this.store = new LocalStore(this.dataDir);
|
|
25
|
+
|
|
26
|
+
// Health check interval
|
|
27
|
+
this.healthCheckInterval = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Aceita tanto "abc123" quanto "/hostedzone/abc123"
|
|
31
|
+
normalizeZoneId(zoneId) {
|
|
32
|
+
if (!zoneId) return zoneId;
|
|
33
|
+
return zoneId.startsWith('/hostedzone/') ? zoneId : `/hostedzone/${zoneId}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async initialize() {
|
|
37
|
+
// 1. Carregar dados persistidos
|
|
38
|
+
await this.loadFromPersistence();
|
|
39
|
+
|
|
40
|
+
// 2. Criar zonas padrão se configurado
|
|
41
|
+
if (this.hostedZones.size === 0 && this.config.hostedZones && Array.isArray(this.config.hostedZones)) {
|
|
42
|
+
logger.info('📚 Carregando Zonas Hospedadas Route53 da configuração inicial...');
|
|
43
|
+
for (const zoneConfig of this.config.hostedZones) {
|
|
44
|
+
this.createHostedZone(zoneConfig.Name, zoneConfig);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 3. Iniciar health checks periódicos
|
|
49
|
+
this.startHealthCheckInterval();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Carrega dados persistidos do storage
|
|
54
|
+
*/
|
|
55
|
+
async loadFromPersistence() {
|
|
56
|
+
try {
|
|
57
|
+
// Carregar Hosted Zones
|
|
58
|
+
const zonesData = this.store.read('hosted-zones');
|
|
59
|
+
if (Array.isArray(zonesData)) {
|
|
60
|
+
logger.info(`📂 Carregando ${zonesData.length} Hosted Zones do storage...`);
|
|
61
|
+
for (const zoneData of zonesData) {
|
|
62
|
+
const zone = new HostedZone(zoneData.Name, zoneData.Config);
|
|
63
|
+
zone.Id = zoneData.Id;
|
|
64
|
+
zone.CallerReference = zoneData.CallerReference;
|
|
65
|
+
zone.CreatedDate = zoneData.CreatedDate;
|
|
66
|
+
zone.ResourceRecordSetCount = zoneData.ResourceRecordSetCount;
|
|
67
|
+
this.hostedZones.set(zone.Id, zone);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Carregar Resource Records
|
|
72
|
+
const recordsData = this.store.read('resource-records');
|
|
73
|
+
if (Array.isArray(recordsData)) {
|
|
74
|
+
logger.info(`📂 Carregando ${recordsData.length} Resource Records do storage...`);
|
|
75
|
+
for (const recordGroup of recordsData) {
|
|
76
|
+
const records = [];
|
|
77
|
+
for (const recordData of recordGroup.records) {
|
|
78
|
+
const record = new ResourceRecord(recordData.Name, recordData.Type, recordData.ResourceRecords.map(r => r.Value), recordData);
|
|
79
|
+
record.CreatedDate = recordData.CreatedDate;
|
|
80
|
+
record.LastModified = recordData.LastModified;
|
|
81
|
+
records.push(record);
|
|
82
|
+
}
|
|
83
|
+
this.resourceRecords.set(recordGroup.zoneId, records);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Carregar Health Checks
|
|
88
|
+
const healthChecksData = this.store.read('health-checks');
|
|
89
|
+
if (Array.isArray(healthChecksData)) {
|
|
90
|
+
logger.info(`📂 Carregando ${healthChecksData.length} Health Checks do storage...`);
|
|
91
|
+
for (const hcData of healthChecksData) {
|
|
92
|
+
const hc = new HealthCheck(hcData.HealthCheckConfig);
|
|
93
|
+
hc.Id = hcData.Id;
|
|
94
|
+
hc.Status = hcData.Status;
|
|
95
|
+
hc.CreatedDate = hcData.CreatedDate;
|
|
96
|
+
this.healthChecks.set(hc.Id, hc);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
logger.error(`Erro ao carregar persistência Route53: ${error.message}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Persiste dados para storage
|
|
106
|
+
*/
|
|
107
|
+
persistData() {
|
|
108
|
+
try {
|
|
109
|
+
// Persistir Hosted Zones
|
|
110
|
+
const zonesData = Array.from(this.hostedZones.values()).map(zone => zone.toJSON());
|
|
111
|
+
this.store.write('hosted-zones', zonesData);
|
|
112
|
+
|
|
113
|
+
// Persistir Resource Records
|
|
114
|
+
const recordsData = Array.from(this.resourceRecords.entries()).map(([zoneId, records]) => ({
|
|
115
|
+
zoneId,
|
|
116
|
+
records: records.map(r => r.toJSON()),
|
|
117
|
+
}));
|
|
118
|
+
this.store.write('resource-records', recordsData);
|
|
119
|
+
|
|
120
|
+
// Persistir Health Checks
|
|
121
|
+
const healthChecksData = Array.from(this.healthChecks.values()).map(hc => hc.toJSON());
|
|
122
|
+
this.store.write('health-checks', healthChecksData);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.error(`Erro ao persistir Route53: ${error.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Cria uma zona hospedada
|
|
130
|
+
*/
|
|
131
|
+
createHostedZone(name, config = {}) {
|
|
132
|
+
const zone = new HostedZone(name, config);
|
|
133
|
+
this.hostedZones.set(zone.Id, zone);
|
|
134
|
+
this.resourceRecords.set(zone.Id, []);
|
|
135
|
+
this.persistData();
|
|
136
|
+
logger.info(`✅ Hosted Zone criada: ${name} (${zone.Id})`);
|
|
137
|
+
return zone;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Lista zonas hospedadas
|
|
142
|
+
*/
|
|
143
|
+
listHostedZones() {
|
|
144
|
+
return Array.from(this.hostedZones.values());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Obtém zona hospedada
|
|
149
|
+
*/
|
|
150
|
+
getHostedZone(zoneId) {
|
|
151
|
+
return this.hostedZones.get(this.normalizeZoneId(zoneId));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Deleta zona hospedada
|
|
156
|
+
*/
|
|
157
|
+
deleteHostedZone(zoneId) {
|
|
158
|
+
zoneId = this.normalizeZoneId(zoneId);
|
|
159
|
+
const zone = this.hostedZones.get(zoneId);
|
|
160
|
+
if (!zone) throw new Error(`Hosted Zone não encontrada: ${zoneId}`);
|
|
161
|
+
|
|
162
|
+
// Verificar se tem registros
|
|
163
|
+
const records = this.resourceRecords.get(zoneId) || [];
|
|
164
|
+
if (records.length > 2) { // NS e SOA são criados automaticamente
|
|
165
|
+
throw new Error(`Não pode deletar zona com registros: ${records.length} encontrados`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.hostedZones.delete(zoneId);
|
|
169
|
+
this.resourceRecords.delete(zoneId);
|
|
170
|
+
this.persistData();
|
|
171
|
+
logger.info(`✅ Hosted Zone deletada: ${zoneId}`);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Cria um resource record
|
|
177
|
+
*/
|
|
178
|
+
createResourceRecord(zoneId, name, type, values, config = {}) {
|
|
179
|
+
zoneId = this.normalizeZoneId(zoneId);
|
|
180
|
+
if (!this.hostedZones.has(zoneId)) {
|
|
181
|
+
throw new Error(`Hosted Zone não encontrada: ${zoneId}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!this.resourceRecords.has(zoneId)) {
|
|
185
|
+
this.resourceRecords.set(zoneId, []);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const record = new ResourceRecord(name, type, values, config);
|
|
189
|
+
const records = this.resourceRecords.get(zoneId);
|
|
190
|
+
records.push(record);
|
|
191
|
+
|
|
192
|
+
const zone = this.hostedZones.get(zoneId);
|
|
193
|
+
zone.ResourceRecordSetCount = records.length;
|
|
194
|
+
|
|
195
|
+
this.persistData();
|
|
196
|
+
logger.info(`✅ Resource Record criado: ${name} (${type})`);
|
|
197
|
+
return record;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Lista resource records
|
|
202
|
+
*/
|
|
203
|
+
listResourceRecords(zoneId) {
|
|
204
|
+
zoneId = this.normalizeZoneId(zoneId);
|
|
205
|
+
if (!this.hostedZones.has(zoneId)) {
|
|
206
|
+
throw new Error(`Hosted Zone não encontrada: ${zoneId}`);
|
|
207
|
+
}
|
|
208
|
+
return this.resourceRecords.get(zoneId) || [];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Deleta resource record
|
|
213
|
+
*/
|
|
214
|
+
deleteResourceRecord(zoneId, name, type) {
|
|
215
|
+
zoneId = this.normalizeZoneId(zoneId);
|
|
216
|
+
if (!this.resourceRecords.has(zoneId)) {
|
|
217
|
+
throw new Error(`Hosted Zone não encontrada: ${zoneId}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const records = this.resourceRecords.get(zoneId);
|
|
221
|
+
const index = records.findIndex(r => r.Name === name && r.Type === type);
|
|
222
|
+
|
|
223
|
+
if (index === -1) {
|
|
224
|
+
throw new Error(`Resource Record não encontrado: ${name} (${type})`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
records.splice(index, 1);
|
|
228
|
+
|
|
229
|
+
const zone = this.hostedZones.get(zoneId);
|
|
230
|
+
zone.ResourceRecordSetCount = records.length;
|
|
231
|
+
|
|
232
|
+
this.persistData();
|
|
233
|
+
logger.info(`✅ Resource Record deletado: ${name} (${type})`);
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Cria um health check
|
|
239
|
+
*/
|
|
240
|
+
createHealthCheck(config = {}) {
|
|
241
|
+
const healthCheck = new HealthCheck(config);
|
|
242
|
+
this.healthChecks.set(healthCheck.Id, healthCheck);
|
|
243
|
+
this.persistData();
|
|
244
|
+
logger.info(`✅ Health Check criado: ${healthCheck.Id}`);
|
|
245
|
+
return healthCheck;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Lista health checks
|
|
250
|
+
*/
|
|
251
|
+
listHealthChecks() {
|
|
252
|
+
return Array.from(this.healthChecks.values());
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Obtém health check
|
|
257
|
+
*/
|
|
258
|
+
getHealthCheck(id) {
|
|
259
|
+
return this.healthChecks.get(id);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Deleta health check
|
|
264
|
+
*/
|
|
265
|
+
deleteHealthCheck(id) {
|
|
266
|
+
if (!this.healthChecks.has(id)) {
|
|
267
|
+
throw new Error(`Health Check não encontrado: ${id}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.healthChecks.delete(id);
|
|
271
|
+
this.persistData();
|
|
272
|
+
logger.info(`✅ Health Check deletado: ${id}`);
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Resolve DNS query (simular resolução)
|
|
278
|
+
*/
|
|
279
|
+
resolveDNS(domain, type = 'A', queryType = 'weighted') {
|
|
280
|
+
// Procurar a zona apropriada
|
|
281
|
+
let zone = null;
|
|
282
|
+
let zoneDomain = null;
|
|
283
|
+
|
|
284
|
+
for (const z of this.hostedZones.values()) {
|
|
285
|
+
if (domain.endsWith(z.Name.replace(/\.$/, ''))) {
|
|
286
|
+
zone = z;
|
|
287
|
+
zoneDomain = z.Name;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!zone) {
|
|
293
|
+
throw new Error(`Zona não encontrada para domínio: ${domain}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const records = this.resourceRecords.get(zone.Id) || [];
|
|
297
|
+
let matchedRecords = records.filter(r => r.Name === domain && r.Type === type);
|
|
298
|
+
|
|
299
|
+
if (matchedRecords.length === 0) {
|
|
300
|
+
throw new Error(`Nenhum registro encontrado para: ${domain} (${type})`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Aplicar estratégia de resolução
|
|
304
|
+
let selected = matchedRecords;
|
|
305
|
+
if (queryType === 'weighted' && matchedRecords.some(r => r.Weight !== null)) {
|
|
306
|
+
selected = ResourceRecord.selectWeightedRecords(matchedRecords);
|
|
307
|
+
} else if (queryType === 'geolocation') {
|
|
308
|
+
selected = ResourceRecord.selectGeoLocationRecords(matchedRecords);
|
|
309
|
+
} else if (queryType === 'failover' && matchedRecords.some(r => r.Failover)) {
|
|
310
|
+
const healthyIds = Array.from(this.healthChecks.values())
|
|
311
|
+
.filter(hc => hc.Status === 'Healthy')
|
|
312
|
+
.map(hc => hc.Id);
|
|
313
|
+
selected = ResourceRecord.selectFailoverRecords(matchedRecords, healthyIds);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
domain,
|
|
318
|
+
type,
|
|
319
|
+
records: selected,
|
|
320
|
+
resolvedAt: new Date().toISOString(),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Inicia health check periódico
|
|
326
|
+
*/
|
|
327
|
+
startHealthCheckInterval() {
|
|
328
|
+
if (this.healthCheckInterval) clearInterval(this.healthCheckInterval);
|
|
329
|
+
|
|
330
|
+
this.healthCheckInterval = setInterval(() => {
|
|
331
|
+
for (const hc of this.healthChecks.values()) {
|
|
332
|
+
hc.check();
|
|
333
|
+
}
|
|
334
|
+
this.persistData();
|
|
335
|
+
}, 30000); // 30 segundos
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Para health check periódico
|
|
340
|
+
*/
|
|
341
|
+
stopHealthCheckInterval() {
|
|
342
|
+
if (this.healthCheckInterval) {
|
|
343
|
+
clearInterval(this.healthCheckInterval);
|
|
344
|
+
this.healthCheckInterval = null;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
module.exports = Route53Simulator;
|
|
350
|
+
|
package/src/services/s3/index.js
CHANGED
|
@@ -34,6 +34,68 @@ class S3Service {
|
|
|
34
34
|
injectDependencies(server) {
|
|
35
35
|
const ct = server.getService('cloudtrail');
|
|
36
36
|
if (ct?.simulator) this.simulator.audit.setTrail(ct.simulator);
|
|
37
|
+
|
|
38
|
+
const lambda = server.getService('lambda');
|
|
39
|
+
if (lambda) this.simulator.setLambdaService(lambda);
|
|
40
|
+
|
|
41
|
+
const sns = server.getService('sns');
|
|
42
|
+
if (sns) this.simulator.setSnsService(sns);
|
|
43
|
+
|
|
44
|
+
const sqs = server.getService('sqs');
|
|
45
|
+
if (sqs) this.simulator.setSqsService(sqs);
|
|
46
|
+
|
|
47
|
+
const eventbridge = server.getService('eventbridge');
|
|
48
|
+
if (eventbridge) this.simulator.setEventbridgeService(eventbridge);
|
|
49
|
+
|
|
50
|
+
// IAM enforcement middleware (only when IAM service is active)
|
|
51
|
+
const iamService = server.getService('iam');
|
|
52
|
+
if (iamService && iamService.getSimulator()) {
|
|
53
|
+
const { createIAMEnforcementMiddleware } = require('../iam/middleware');
|
|
54
|
+
const iamSimulator = iamService.getSimulator();
|
|
55
|
+
|
|
56
|
+
const middleware = createIAMEnforcementMiddleware({
|
|
57
|
+
iamSimulator,
|
|
58
|
+
servicePrefix: 's3',
|
|
59
|
+
extractAction: (req) => {
|
|
60
|
+
const pathParts = req.path.split('/').filter(Boolean);
|
|
61
|
+
const hasKey = pathParts.length >= 2;
|
|
62
|
+
|
|
63
|
+
switch (req.method) {
|
|
64
|
+
case 'GET':
|
|
65
|
+
return hasKey ? 'GetObject' : 'ListBucket';
|
|
66
|
+
case 'PUT':
|
|
67
|
+
return hasKey ? 'PutObject' : 'CreateBucket';
|
|
68
|
+
case 'DELETE':
|
|
69
|
+
return hasKey ? 'DeleteObject' : 'DeleteBucket';
|
|
70
|
+
case 'HEAD':
|
|
71
|
+
return hasKey ? 'GetObject' : 'ListBucket';
|
|
72
|
+
case 'POST':
|
|
73
|
+
return 'PutObject';
|
|
74
|
+
default:
|
|
75
|
+
return 'Unknown';
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
extractResource: (req) => {
|
|
79
|
+
const pathParts = req.path.split('/').filter(Boolean);
|
|
80
|
+
const bucket = pathParts[0] || 'unknown';
|
|
81
|
+
const key = pathParts.slice(1).join('/');
|
|
82
|
+
if (key) return `arn:aws:s3:::${bucket}/${key}`;
|
|
83
|
+
return `arn:aws:s3:::${bucket}`;
|
|
84
|
+
},
|
|
85
|
+
extractConditionContext: (req) => ({
|
|
86
|
+
's3:prefix': req.query?.prefix || '',
|
|
87
|
+
's3:delimiter': req.query?.delimiter || ''
|
|
88
|
+
}),
|
|
89
|
+
formatError: (res, code, message) => {
|
|
90
|
+
res.status(403).set('Content-Type', 'application/xml').send(
|
|
91
|
+
`<?xml version="1.0" encoding="UTF-8"?><Error><Code>${code}</Code><Message>${message}</Message></Error>`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Set middleware on the S3 server so it's applied before routes
|
|
97
|
+
this.server.iamMiddleware = middleware;
|
|
98
|
+
}
|
|
37
99
|
}
|
|
38
100
|
|
|
39
101
|
async start() {
|