@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,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;
|