@gugananuvem/aws-local-simulator 1.0.34 → 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.
Files changed (150) hide show
  1. package/README.md +662 -658
  2. package/package.json +11 -4
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/handler-loader.js +50 -5
  100. package/src/services/lambda/index.js +26 -0
  101. package/src/services/lambda/server.js +18 -12
  102. package/src/services/lambda/simulator.js +73 -17
  103. package/src/services/opensearch/index.js +105 -0
  104. package/src/services/opensearch/models/document.js +115 -0
  105. package/src/services/opensearch/models/index.js +73 -0
  106. package/src/services/opensearch/server.js +310 -0
  107. package/src/services/opensearch/simulator.js +605 -0
  108. package/src/services/opensearch/store.js +162 -0
  109. package/src/services/organizations/control-tower-simulator.js +321 -0
  110. package/src/services/organizations/control-tower-store.js +182 -0
  111. package/src/services/organizations/iam-integration.js +413 -0
  112. package/src/services/organizations/index.js +172 -0
  113. package/src/services/organizations/models/index.js +13 -0
  114. package/src/services/organizations/models/invitation.js +48 -0
  115. package/src/services/organizations/models/organization.js +566 -0
  116. package/src/services/organizations/models/role.js +244 -0
  117. package/src/services/organizations/models/scp.js +131 -0
  118. package/src/services/organizations/server.js +736 -0
  119. package/src/services/organizations/simulator.js +593 -0
  120. package/src/services/organizations/store.js +235 -0
  121. package/src/services/organizations/vpc-integration.js +453 -0
  122. package/src/services/route53/index.js +67 -0
  123. package/src/services/route53/models/healthCheck.js +101 -0
  124. package/src/services/route53/models/hostedZone.js +37 -0
  125. package/src/services/route53/models/resourceRecord.js +106 -0
  126. package/src/services/route53/server.js +841 -0
  127. package/src/services/route53/simulator.js +350 -0
  128. package/src/services/s3/index.js +62 -0
  129. package/src/services/s3/presigned-url-generator.js +337 -0
  130. package/src/services/s3/server.js +231 -0
  131. package/src/services/s3/simulator.js +551 -4
  132. package/src/services/sns/index.js +31 -0
  133. package/src/services/sns/server.js +9 -0
  134. package/src/services/sqs/index.js +38 -0
  135. package/src/services/sqs/server.js +57 -4
  136. package/src/services/sqs/simulator.js +423 -33
  137. package/src/services/sts/simulator.js +209 -17
  138. package/src/services/vpc/README.md +459 -0
  139. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  140. package/src/services/vpc/index.js +90 -0
  141. package/src/services/vpc/models/internetGateway.js +71 -0
  142. package/src/services/vpc/models/routeTable.js +160 -0
  143. package/src/services/vpc/models/subnet.js +48 -0
  144. package/src/services/vpc/models/vpc.js +37 -0
  145. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  146. package/src/services/vpc/models/vpcGateway.js +134 -0
  147. package/src/services/vpc/server.js +836 -0
  148. package/src/services/vpc/simulator.js +613 -0
  149. package/src/utils/policy-cache.js +215 -0
  150. package/aws-config +0 -154
@@ -0,0 +1,613 @@
1
+ /**
2
+ * VPC Simulator
3
+ * Gerencia o estado de VPCs, subnets, rotas e gateways com persistência
4
+ */
5
+
6
+ const path = require('path');
7
+ const { v4: uuidv4 } = require('uuid');
8
+ const VPC = require('./models/vpc');
9
+ const Subnet = require('./models/subnet');
10
+ const { RouteTable } = require('./models/routeTable');
11
+ const { InternetGateway } = require('./models/internetGateway');
12
+ const VPCEndpoint = require('./models/vpcEndpoint');
13
+ const {
14
+ NATGateway,
15
+ CustomerGateway,
16
+ VpnGateway,
17
+ VpnConnection
18
+ } = require('./models/vpcGateway');
19
+ const LocalStore = require('../../utils/local-store');
20
+ const logger = require('../../utils/logger');
21
+
22
+ class VPCSimulator {
23
+ constructor(config = {}) {
24
+ this.config = config;
25
+ this.vpcs = new Map();
26
+ this.subnets = new Map();
27
+ this.routeTables = new Map();
28
+ this.internetGateways = new Map();
29
+ this.vpcEndpoints = new Map();
30
+ this.natGateways = new Map();
31
+ this.customerGateways = new Map();
32
+ this.vpnGateways = new Map();
33
+ this.vpnConnections = new Map();
34
+
35
+ // Inicializar persistência
36
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
37
+ this.dataDir = path.join(dataDir, 'vpc');
38
+ this.store = new LocalStore(this.dataDir);
39
+ this.isPersistent = true;
40
+ }
41
+
42
+ async initialize() {
43
+ // 1. Carregar dados persistidos se existirem
44
+ await this.loadFromPersistence();
45
+
46
+ // 2. Se não houver dados persistidos, carregar da configuração
47
+ if (this.vpcs.size === 0 && this.config.vpcs && Array.isArray(this.config.vpcs)) {
48
+ logger.info('📚 Carregando VPCs da configuração inicial...');
49
+ for (const vpcConfig of this.config.vpcs) {
50
+ this.createVPC(vpcConfig.CidrBlock, vpcConfig.Tags || []);
51
+ }
52
+ }
53
+
54
+ if (this.subnets.size === 0 && this.config.subnets && Array.isArray(this.config.subnets)) {
55
+ for (const subnetConfig of this.config.subnets) {
56
+ this.createSubnet(
57
+ subnetConfig.VpcId,
58
+ subnetConfig.CidrBlock,
59
+ subnetConfig.AvailabilityZone,
60
+ subnetConfig.Tags
61
+ );
62
+ }
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Carrega dados persistidos do storage
68
+ */
69
+ async loadFromPersistence() {
70
+ try {
71
+ // Carregar VPCs
72
+ const vpcsData = this.store.read('vpcs');
73
+ if (Array.isArray(vpcsData)) {
74
+ logger.info(`📂 Carregando ${vpcsData.length} VPCs do storage...`);
75
+ for (const vpcData of vpcsData) {
76
+ const vpc = new VPC(vpcData.CidrBlock, vpcData.Tags, {
77
+ EnableDnsHostnames: vpcData.EnableDnsHostnames,
78
+ EnableDnsSupport: vpcData.EnableDnsSupport
79
+ });
80
+ vpc.VpcId = vpcData.VpcId;
81
+ vpc.State = vpcData.State;
82
+ this.vpcs.set(vpc.VpcId, vpc);
83
+ }
84
+ }
85
+
86
+ // Carregar Subnets
87
+ const subnetsData = this.store.read('subnets');
88
+ if (Array.isArray(subnetsData)) {
89
+ logger.info(`📂 Carregando ${subnetsData.length} Subnets do storage...`);
90
+ for (const subnetData of subnetsData) {
91
+ const subnet = new Subnet(
92
+ subnetData.VpcId,
93
+ subnetData.CidrBlock,
94
+ subnetData.AvailabilityZone,
95
+ subnetData.Tags,
96
+ { MapPublicIpOnLaunch: subnetData.MapPublicIpOnLaunch }
97
+ );
98
+ subnet.SubnetId = subnetData.SubnetId;
99
+ subnet.State = subnetData.State;
100
+ this.subnets.set(subnet.SubnetId, subnet);
101
+ }
102
+ }
103
+
104
+ // Carregar Internet Gateways
105
+ const igwsData = this.store.read('internet-gateways');
106
+ if (Array.isArray(igwsData)) {
107
+ logger.info(`📂 Carregando ${igwsData.length} Internet Gateways do storage...`);
108
+ for (const igwData of igwsData) {
109
+ const igw = new InternetGateway(igwData.Tags);
110
+ igw.InternetGatewayId = igwData.InternetGatewayId;
111
+ igw.Attachments = igwData.Attachments || [];
112
+ this.internetGateways.set(igw.InternetGatewayId, igw);
113
+ }
114
+ }
115
+
116
+ // Carregar Route Tables
117
+ const rtbsData = this.store.read('route-tables');
118
+ if (Array.isArray(rtbsData)) {
119
+ logger.info(`📂 Carregando ${rtbsData.length} Route Tables do storage...`);
120
+ for (const rtbData of rtbsData) {
121
+ const rtb = new RouteTable(rtbData.VpcId, rtbData.Tags);
122
+ rtb.RouteTableId = rtbData.RouteTableId;
123
+ rtb.State = rtbData.State || 'active';
124
+ rtb.CreatedAt = rtbData.CreatedAt || new Date().toISOString();
125
+
126
+ // Desserializar Routes corretamente
127
+ const { Route } = require('./models/routeTable');
128
+ rtb.Routes = (rtbData.Routes || []).map(routeData => {
129
+ const route = new Route(
130
+ routeData.DestinationCidrBlock,
131
+ routeData.GatewayId || routeData.NatGatewayId || routeData.VpcEndpointId || routeData.InstanceId || routeData.NetworkInterfaceId || 'local',
132
+ routeData.GatewayId ? 'igw' : routeData.NatGatewayId ? 'nat' : routeData.VpcEndpointId ? 'vpce' : routeData.InstanceId ? 'instance' : routeData.NetworkInterfaceId ? 'eni' : 'local'
133
+ );
134
+ // Restaurar propriedades originais
135
+ route.State = routeData.State || 'active';
136
+ route.Origin = routeData.Origin || 'CreateRouteTable';
137
+ route.CreatedAt = routeData.CreatedAt || new Date().toISOString();
138
+ return route;
139
+ });
140
+
141
+ // Desserializar Associations corretamente
142
+ const { RouteTableAssociation } = require('./models/routeTable');
143
+ rtb.Associations = (rtbData.Associations || []).map(assocData => {
144
+ const association = new RouteTableAssociation(assocData.SubnetId, assocData.RouteTableId, assocData.Main);
145
+ association.RouteTableAssociationId = assocData.RouteTableAssociationId || `rtbassoc-${uuidv4().substring(0, 8)}`;
146
+ association.CreatedAt = assocData.CreatedAt || new Date().toISOString();
147
+ return association;
148
+ });
149
+
150
+ rtb.IsMain = rtbData.IsMain || false;
151
+ this.routeTables.set(rtb.RouteTableId, rtb);
152
+ }
153
+ }
154
+ } catch (error) {
155
+ logger.warn(`⚠️ Erro ao carregar dados persistidos: ${error.message}`);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Persiste estado atual em storage
161
+ */
162
+ persistData() {
163
+ try {
164
+ this.store.write('vpcs', Array.from(this.vpcs.values()).map(v => v.toJSON()));
165
+ this.store.write('subnets', Array.from(this.subnets.values()).map(s => s.toJSON()));
166
+ this.store.write('internet-gateways', Array.from(this.internetGateways.values()).map(igw => igw.toJSON()));
167
+ this.store.write('route-tables', Array.from(this.routeTables.values()).map(rt => rt.toJSON()));
168
+ } catch (error) {
169
+ logger.error(`Erro ao persistir dados VPC: ${error.message}`);
170
+ }
171
+ }
172
+
173
+ // ========== VPC Operations ==========
174
+
175
+ createVPC(cidrBlock, tags = [], config = {}) {
176
+ const vpc = new VPC(cidrBlock, tags, config);
177
+ this.vpcs.set(vpc.VpcId, vpc);
178
+
179
+ // Criar tabela de rotas padrão para VPC
180
+ const mainRouteTable = this.createRouteTable(vpc.VpcId, [], true);
181
+
182
+ // ✅ Persistir mudanças
183
+ this.persistData();
184
+
185
+ return vpc;
186
+ }
187
+
188
+ describeVPCs(filters = {}) {
189
+ let vpcs = Array.from(this.vpcs.values());
190
+
191
+ if (filters.vpcIds && Array.isArray(filters.vpcIds)) {
192
+ vpcs = vpcs.filter(v => filters.vpcIds.includes(v.VpcId));
193
+ }
194
+
195
+ if (filters.cidrBlock) {
196
+ vpcs = vpcs.filter(v => v.CidrBlock === filters.cidrBlock);
197
+ }
198
+
199
+ return vpcs;
200
+ }
201
+
202
+ getVPC(vpcId) {
203
+ const vpc = this.vpcs.get(vpcId);
204
+ if (!vpc) {
205
+ throw new Error(`VPC ${vpcId} not found`);
206
+ }
207
+ return vpc;
208
+ }
209
+
210
+ deleteVPC(vpcId) {
211
+ // Verificar dependências
212
+ const subnets = Array.from(this.subnets.values()).filter(s => s.VpcId === vpcId);
213
+ if (subnets.length > 0) {
214
+ throw new Error(`Cannot delete VPC ${vpcId}: VPC has subnets`);
215
+ }
216
+
217
+ const igws = Array.from(this.internetGateways.values()).filter(igw =>
218
+ igw.isAttachedTo(vpcId)
219
+ );
220
+ if (igws.length > 0) {
221
+ throw new Error(`Cannot delete VPC ${vpcId}: IGW still attached`);
222
+ }
223
+
224
+ // Deletar tabelas de rotas associadas
225
+ const rtbs = Array.from(this.routeTables.values()).filter(rt => rt.VpcId === vpcId);
226
+ for (const rtb of rtbs) {
227
+ this.routeTables.delete(rtb.RouteTableId);
228
+ }
229
+
230
+ this.vpcs.delete(vpcId);
231
+
232
+ // ✅ Persistir mudanças
233
+ this.persistData();
234
+
235
+ return true;
236
+ }
237
+
238
+ modifyVPCAttribute(vpcId, attribute, value) {
239
+ const vpc = this.getVPC(vpcId);
240
+
241
+ switch (attribute) {
242
+ case 'enableDnsHostnames':
243
+ vpc.EnableDnsHostnames = value;
244
+ break;
245
+ case 'enableDnsSupport':
246
+ vpc.EnableDnsSupport = value;
247
+ break;
248
+ default:
249
+ throw new Error(`Unknown VPC attribute: ${attribute}`);
250
+ }
251
+
252
+ // ✅ Persistir mudanças
253
+ this.persistData();
254
+
255
+ return vpc;
256
+ }
257
+
258
+ // ========== Subnet Operations ==========
259
+
260
+ createSubnet(vpcId, cidrBlock, availabilityZone, tags = [], config = {}) {
261
+ // Verificar se VPC existe
262
+ this.getVPC(vpcId);
263
+
264
+ // Verificar sobreposição de CIDR
265
+ const existingSubnets = Array.from(this.subnets.values()).filter(
266
+ s => s.VpcId === vpcId
267
+ );
268
+ if (this.hasCIDROverlap(cidrBlock, existingSubnets.map(s => s.CidrBlock))) {
269
+ throw new Error(`CIDR block ${cidrBlock} overlaps with existing subnets`);
270
+ }
271
+
272
+ const subnet = new Subnet(vpcId, cidrBlock, availabilityZone, tags, config);
273
+ this.subnets.set(subnet.SubnetId, subnet);
274
+
275
+ // ✅ Persistir mudanças
276
+ this.persistData();
277
+
278
+ return subnet;
279
+ }
280
+
281
+ describeSubnets(filters = {}) {
282
+ let subnets = Array.from(this.subnets.values());
283
+
284
+ if (filters.vpcIds && Array.isArray(filters.vpcIds)) {
285
+ subnets = subnets.filter(s => filters.vpcIds.includes(s.VpcId));
286
+ }
287
+
288
+ if (filters.subnetIds && Array.isArray(filters.subnetIds)) {
289
+ subnets = subnets.filter(s => filters.subnetIds.includes(s.SubnetId));
290
+ }
291
+
292
+ if (filters.cidrBlock) {
293
+ subnets = subnets.filter(s => s.CidrBlock === filters.cidrBlock);
294
+ }
295
+
296
+ return subnets;
297
+ }
298
+
299
+ getSubnet(subnetId) {
300
+ const subnet = this.subnets.get(subnetId);
301
+ if (!subnet) {
302
+ throw new Error(`Subnet ${subnetId} not found`);
303
+ }
304
+ return subnet;
305
+ }
306
+
307
+ deleteSubnet(subnetId) {
308
+ // Verificar dependências
309
+ const routeTableAssocs = Array.from(this.routeTables.values())
310
+ .flatMap(rt => rt.Associations)
311
+ .filter(a => a.SubnetId === subnetId);
312
+
313
+ if (routeTableAssocs.length > 0) {
314
+ throw new Error(`Subnet ${subnetId} has associated route tables`);
315
+ }
316
+
317
+ this.subnets.delete(subnetId);
318
+
319
+ // ✅ Persistir mudanças
320
+ this.persistData();
321
+
322
+ return true;
323
+ }
324
+
325
+ // ========== Route Table Operations ==========
326
+
327
+ createRouteTable(vpcId, tags = [], isMain = false) {
328
+ // Verificar se VPC existe
329
+ this.getVPC(vpcId);
330
+
331
+ const routeTable = new RouteTable(vpcId, tags, isMain);
332
+ this.routeTables.set(routeTable.RouteTableId, routeTable);
333
+
334
+ // ✅ Persistir mudanças (apenas se não for main table)
335
+ if (!isMain) {
336
+ this.persistData();
337
+ }
338
+
339
+ return routeTable;
340
+ }
341
+
342
+ describeRouteTables(filters = {}) {
343
+ let routeTables = Array.from(this.routeTables.values());
344
+
345
+ if (filters.vpcIds && Array.isArray(filters.vpcIds)) {
346
+ routeTables = routeTables.filter(rt => filters.vpcIds.includes(rt.VpcId));
347
+ }
348
+
349
+ if (filters.routeTableIds && Array.isArray(filters.routeTableIds)) {
350
+ routeTables = routeTables.filter(rt => filters.routeTableIds.includes(rt.RouteTableId));
351
+ }
352
+
353
+ return routeTables;
354
+ }
355
+
356
+ getRouteTable(routeTableId) {
357
+ const rt = this.routeTables.get(routeTableId);
358
+ if (!rt) {
359
+ throw new Error(`Route Table ${routeTableId} not found`);
360
+ }
361
+ return rt;
362
+ }
363
+
364
+ deleteRouteTable(routeTableId) {
365
+ const rt = this.getRouteTable(routeTableId);
366
+
367
+ if (rt.IsMain) {
368
+ throw new Error(`Cannot delete main route table ${routeTableId}`);
369
+ }
370
+
371
+ if (rt.Associations.length > 0) {
372
+ throw new Error(`Cannot delete route table with associations`);
373
+ }
374
+
375
+ this.routeTables.delete(routeTableId);
376
+
377
+ // ✅ Persistir mudanças
378
+ this.persistData();
379
+
380
+ return true;
381
+ }
382
+
383
+ addRoute(routeTableId, destinationCidrBlock, target, targetType = 'igw') {
384
+ const rt = this.getRouteTable(routeTableId);
385
+ const route = rt.addRoute(destinationCidrBlock, target, targetType);
386
+
387
+ // ✅ Persistir mudanças
388
+ this.persistData();
389
+
390
+ return route;
391
+ }
392
+
393
+ removeRoute(routeTableId, destinationCidrBlock) {
394
+ const rt = this.getRouteTable(routeTableId);
395
+ const route = rt.removeRoute(destinationCidrBlock);
396
+ return route;
397
+ }
398
+
399
+ associateRouteTable(routeTableId, subnetId) {
400
+ const rt = this.getRouteTable(routeTableId);
401
+ this.getSubnet(subnetId);
402
+
403
+ const association = rt.addAssociation(subnetId);
404
+
405
+ // ✅ Persistir mudanças
406
+ this.persistData();
407
+
408
+ return association;
409
+ }
410
+
411
+ disassociateRouteTable(routeTableId, subnetId) {
412
+ const rt = this.getRouteTable(routeTableId);
413
+ const association = rt.removeAssociation(subnetId);
414
+
415
+ // ✅ Persistir mudanças
416
+ this.persistData();
417
+
418
+ return association;
419
+ }
420
+
421
+ // ========== Internet Gateway Operations ==========
422
+
423
+ createInternetGateway(tags = []) {
424
+ const igw = new InternetGateway(tags);
425
+ this.internetGateways.set(igw.InternetGatewayId, igw);
426
+
427
+ // ✅ Persistir mudanças
428
+ this.persistData();
429
+
430
+ return igw;
431
+ }
432
+
433
+ describeInternetGateways(filters = {}) {
434
+ let igws = Array.from(this.internetGateways.values());
435
+
436
+ if (filters.internetGatewayIds && Array.isArray(filters.internetGatewayIds)) {
437
+ igws = igws.filter(igw => filters.internetGatewayIds.includes(igw.InternetGatewayId));
438
+ }
439
+
440
+ return igws;
441
+ }
442
+
443
+ getInternetGateway(internetGatewayId) {
444
+ const igw = this.internetGateways.get(internetGatewayId);
445
+ if (!igw) {
446
+ throw new Error(`Internet Gateway ${internetGatewayId} not found`);
447
+ }
448
+ return igw;
449
+ }
450
+
451
+ attachInternetGateway(internetGatewayId, vpcId) {
452
+ const igw = this.getInternetGateway(internetGatewayId);
453
+ this.getVPC(vpcId);
454
+
455
+ const attachment = igw.attach(vpcId);
456
+
457
+ // ✅ Persistir mudanças
458
+ this.persistData();
459
+
460
+ return attachment;
461
+ }
462
+
463
+ detachInternetGateway(internetGatewayId, vpcId) {
464
+ const igw = this.getInternetGateway(internetGatewayId);
465
+ const attachment = igw.detach(vpcId);
466
+
467
+ // ✅ Persistir mudanças
468
+ this.persistData();
469
+
470
+ return attachment;
471
+ }
472
+
473
+ deleteInternetGateway(internetGatewayId) {
474
+ const igw = this.getInternetGateway(internetGatewayId);
475
+
476
+ if (igw.Attachments.length > 0) {
477
+ throw new Error(`Cannot delete Internet Gateway with attachments`);
478
+ }
479
+
480
+ this.internetGateways.delete(internetGatewayId);
481
+
482
+ // ✅ Persistir mudanças
483
+ this.persistData();
484
+
485
+ return true;
486
+ }
487
+
488
+ // ========== VPC Endpoint Operations ==========
489
+
490
+ createVPCEndpoint(vpcId, serviceName, config = {}) {
491
+ this.getVPC(vpcId);
492
+
493
+ const endpoint = new VPCEndpoint(vpcId, serviceName, config);
494
+ this.vpcEndpoints.set(endpoint.VpcEndpointId, endpoint);
495
+ return endpoint;
496
+ }
497
+
498
+ describeVPCEndpoints(filters = {}) {
499
+ let endpoints = Array.from(this.vpcEndpoints.values());
500
+
501
+ if (filters.vpcIds && Array.isArray(filters.vpcIds)) {
502
+ endpoints = endpoints.filter(e => filters.vpcIds.includes(e.VpcId));
503
+ }
504
+
505
+ if (filters.vpcEndpointIds && Array.isArray(filters.vpcEndpointIds)) {
506
+ endpoints = endpoints.filter(e => filters.vpcEndpointIds.includes(e.VpcEndpointId));
507
+ }
508
+
509
+ if (filters.serviceName) {
510
+ endpoints = endpoints.filter(e => e.ServiceName === filters.serviceName);
511
+ }
512
+
513
+ return endpoints;
514
+ }
515
+
516
+ getVPCEndpoint(vpcEndpointId) {
517
+ const endpoint = this.vpcEndpoints.get(vpcEndpointId);
518
+ if (!endpoint) {
519
+ throw new Error(`VPC Endpoint ${vpcEndpointId} not found`);
520
+ }
521
+ return endpoint;
522
+ }
523
+
524
+ deleteVPCEndpoint(vpcEndpointId) {
525
+ this.vpcEndpoints.delete(vpcEndpointId);
526
+ return true;
527
+ }
528
+
529
+ modifyVPCEndpoint(vpcEndpointId, changes) {
530
+ const endpoint = this.getVPCEndpoint(vpcEndpointId);
531
+
532
+ if (changes.RouteTableIds) {
533
+ endpoint.RouteTableIds = changes.RouteTableIds;
534
+ }
535
+
536
+ if (changes.SubnetIds) {
537
+ endpoint.SubnetIds = changes.SubnetIds;
538
+ }
539
+
540
+ if (changes.PolicyDocument) {
541
+ endpoint.PolicyDocument = changes.PolicyDocument;
542
+ }
543
+
544
+ return endpoint;
545
+ }
546
+
547
+ // ========== VPC Gateway Operations (NAT, VPN, etc) ==========
548
+
549
+ createNATGateway(subnetId, allocationId, config = {}) {
550
+ this.getSubnet(subnetId);
551
+
552
+ const natGw = new NATGateway(subnetId, allocationId, config);
553
+ this.natGateways.set(natGw.NatGatewayId, natGw);
554
+ return natGw;
555
+ }
556
+
557
+ describeNATGateways(filters = {}) {
558
+ let gateways = Array.from(this.natGateways.values());
559
+
560
+ if (filters.natGatewayIds && Array.isArray(filters.natGatewayIds)) {
561
+ gateways = gateways.filter(ng => filters.natGatewayIds.includes(ng.NatGatewayId));
562
+ }
563
+
564
+ if (filters.subnetIds && Array.isArray(filters.subnetIds)) {
565
+ gateways = gateways.filter(ng => filters.subnetIds.includes(ng.SubnetId));
566
+ }
567
+
568
+ return gateways;
569
+ }
570
+
571
+ deleteNATGateway(natGatewayId) {
572
+ this.natGateways.delete(natGatewayId);
573
+ return true;
574
+ }
575
+
576
+ // Utility methods
577
+
578
+ hasCIDROverlap(newCidr, existingCidrs) {
579
+ const parseNetworkCIDR = (cidr) => {
580
+ const [ip, prefix] = cidr.split('/');
581
+ const parts = ip.split('.').map(Number);
582
+ const ipNum = (parts[0] << 24) + (parts[1] << 16) + (parts[2] << 8) + parts[3];
583
+ const prefixNum = parseInt(prefix) || 32;
584
+ const mask = (0xffffffff << (32 - prefixNum)) >>> 0;
585
+ return { ipNum, mask };
586
+ };
587
+
588
+ const { ipNum: newIp, mask: newMask } = parseNetworkCIDR(newCidr);
589
+
590
+ for (const existingCidr of existingCidrs) {
591
+ const { ipNum: existingIp, mask: existingMask } = parseNetworkCIDR(existingCidr);
592
+
593
+ if ((newIp & newMask) === (existingIp & newMask)) {
594
+ return true;
595
+ }
596
+ }
597
+
598
+ return false;
599
+ }
600
+
601
+ getAllResources() {
602
+ return {
603
+ vpcs: Array.from(this.vpcs.values()),
604
+ subnets: Array.from(this.subnets.values()),
605
+ routeTables: Array.from(this.routeTables.values()),
606
+ internetGateways: Array.from(this.internetGateways.values()),
607
+ vpcEndpoints: Array.from(this.vpcEndpoints.values()),
608
+ natGateways: Array.from(this.natGateways.values())
609
+ };
610
+ }
611
+ }
612
+
613
+ module.exports = VPCSimulator;