@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,410 @@
1
+ /**
2
+ * ASG Simulator - Auto Scaling Group Simulator com persistência
3
+ */
4
+
5
+ const path = require('path');
6
+ const AutoScalingGroup = require('./models/autoScalingGroup');
7
+ const LaunchTemplate = require('./models/launchTemplate');
8
+ const ScalingPolicy = require('./models/scalingPolicy');
9
+ const LocalStore = require('../../utils/local-store');
10
+ const logger = require('../../utils/logger');
11
+
12
+ class ASGSimulator {
13
+ constructor(config = {}) {
14
+ this.config = config;
15
+ this.autoScalingGroups = new Map();
16
+ this.launchTemplates = new Map();
17
+ this.scalingPolicies = new Map();
18
+ this.ec2Service = null;
19
+ this.albService = null;
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, 'asg');
24
+ this.store = new LocalStore(this.dataDir);
25
+ }
26
+
27
+ async initialize() {
28
+ // 1. Carregar dados persistidos
29
+ await this.loadFromPersistence();
30
+
31
+ // 2. Pré-provisionar ASGs se configurado e não existirem
32
+ if (this.autoScalingGroups.size === 0 && this.config.autoScalingGroups && Array.isArray(this.config.autoScalingGroups)) {
33
+ logger.info('📚 Carregando ASGs da configuração inicial...');
34
+ for (const asgConfig of this.config.autoScalingGroups) {
35
+ // Criar launch template primeiro
36
+ const lt = new LaunchTemplate(
37
+ asgConfig.launchTemplateName || asgConfig.AutoScalingGroupName,
38
+ asgConfig.imageId || 'ami-0c55b159cbfafe1f0',
39
+ asgConfig.instanceType || 't2.micro',
40
+ asgConfig.launchTemplateConfig || {}
41
+ );
42
+ this.launchTemplates.set(lt.LaunchTemplateId, lt);
43
+
44
+ // Criar ASG
45
+ this.createAutoScalingGroup(
46
+ asgConfig.AutoScalingGroupName,
47
+ lt.LaunchTemplateId,
48
+ asgConfig
49
+ );
50
+ }
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Carrega dados persistidos do storage
56
+ */
57
+ async loadFromPersistence() {
58
+ try {
59
+ // Carregar Launch Templates
60
+ const ltsData = this.store.read('launch-templates');
61
+ if (Array.isArray(ltsData)) {
62
+ logger.info(`📂 Carregando ${ltsData.length} Launch Templates do storage...`);
63
+ for (const ltData of ltsData) {
64
+ const lt = new LaunchTemplate(ltData.LaunchTemplateName, ltData.ImageId, ltData.InstanceType);
65
+ lt.LaunchTemplateId = ltData.LaunchTemplateId;
66
+ this.launchTemplates.set(lt.LaunchTemplateId, lt);
67
+ }
68
+ }
69
+
70
+ // Carregar ASGs
71
+ const asgsData = this.store.read('auto-scaling-groups');
72
+ if (Array.isArray(asgsData)) {
73
+ logger.info(`📂 Carregando ${asgsData.length} ASGs do storage...`);
74
+ for (const asgData of asgsData) {
75
+ const asg = new AutoScalingGroup(
76
+ asgData.AutoScalingGroupName,
77
+ asgData.LaunchTemplateId,
78
+ asgData
79
+ );
80
+ asg.AutoScalingGroupARN = asgData.AutoScalingGroupARN;
81
+ asg.MinSize = asgData.MinSize;
82
+ asg.MaxSize = asgData.MaxSize;
83
+ asg.DesiredCapacity = asgData.DesiredCapacity;
84
+ asg.Instances = asgData.Instances || [];
85
+ this.autoScalingGroups.set(asg.AutoScalingGroupName, asg);
86
+ }
87
+ }
88
+
89
+ // Carregar Scaling Policies
90
+ const polsData = this.store.read('scaling-policies');
91
+ if (Array.isArray(polsData)) {
92
+ logger.info(`📂 Carregando ${polsData.length} Scaling Policies do storage...`);
93
+ for (const polData of polsData) {
94
+ const policy = new ScalingPolicy(polData.PolicyName, polData.AutoScalingGroupName, polData.PolicyType);
95
+ policy.PolicyARN = polData.PolicyARN;
96
+ this.scalingPolicies.set(policy.PolicyARN, policy);
97
+ }
98
+ }
99
+ } catch (error) {
100
+ logger.warn(`⚠️ Erro ao carregar dados persistidos ASG: ${error.message}`);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Persiste estado atual em storage
106
+ */
107
+ persistData() {
108
+ try {
109
+ this.store.write('launch-templates', Array.from(this.launchTemplates.values()).map(lt => lt.toJSON ? lt.toJSON() : lt));
110
+ this.store.write('auto-scaling-groups', Array.from(this.autoScalingGroups.values()).map(asg => asg.toJSON ? asg.toJSON() : asg));
111
+ this.store.write('scaling-policies', Array.from(this.scalingPolicies.values()).map(pol => pol.toJSON ? pol.toJSON() : pol));
112
+ } catch (error) {
113
+ logger.error(`Erro ao persistir dados ASG: ${error.message}`);
114
+ }
115
+ }
116
+
117
+ // ========== Launch Template Operations ==========
118
+
119
+ createLaunchTemplate(name, imageId, instanceType, config = {}) {
120
+ const lt = new LaunchTemplate(name, imageId, instanceType, config);
121
+ this.launchTemplates.set(lt.LaunchTemplateId, lt);
122
+ logger.info(`✅ Launch Template criado: ${name} (${lt.LaunchTemplateId})`);
123
+ // ✅ Persistir mudanças
124
+ this.persistData();
125
+ return lt;
126
+ }
127
+
128
+ describeLaunchTemplates(filters = {}) {
129
+ let templates = Array.from(this.launchTemplates.values());
130
+
131
+ if (filters.names && Array.isArray(filters.names)) {
132
+ templates = templates.filter(t => filters.names.includes(t.LaunchTemplateName));
133
+ }
134
+
135
+ if (filters.ids && Array.isArray(filters.ids)) {
136
+ templates = templates.filter(t => filters.ids.includes(t.LaunchTemplateId));
137
+ }
138
+
139
+ return templates;
140
+ }
141
+
142
+ getLaunchTemplate(templateId) {
143
+ const lt = this.launchTemplates.get(templateId);
144
+ if (!lt) {
145
+ throw new Error(`Launch Template ${templateId} not found`);
146
+ }
147
+ return lt;
148
+ }
149
+
150
+ deleteLaunchTemplate(templateId) {
151
+ const lt = this.getLaunchTemplate(templateId);
152
+ this.launchTemplates.delete(templateId);
153
+ logger.info(`🗑️ Launch Template deletado: ${lt.LaunchTemplateName}`);
154
+ // ✅ Persistir mudanças
155
+ this.persistData();
156
+ return true;
157
+ }
158
+
159
+ // ========== Auto Scaling Group Operations ==========
160
+
161
+ createAutoScalingGroup(name, launchTemplateId, config = {}) {
162
+ const lt = this.getLaunchTemplate(launchTemplateId);
163
+
164
+ const asg = new AutoScalingGroup(name, lt, {
165
+ minSize: config.minSize || 1,
166
+ maxSize: config.maxSize || 5,
167
+ desiredCapacity: config.desiredCapacity || config.minSize || 1,
168
+ vpcZoneIdentifier: config.vpcZoneIdentifier || [],
169
+ availabilityZones: config.availabilityZones || ['us-east-1a'],
170
+ targetGroupARNs: config.targetGroupARNs || [],
171
+ loadBalancerNames: config.loadBalancerNames || [],
172
+ healthCheckType: config.healthCheckType || 'ELB',
173
+ healthCheckGracePeriod: config.healthCheckGracePeriod || 300,
174
+ tags: config.tags || [],
175
+ enabledMetrics: config.enabledMetrics || ['GroupInServiceInstances']
176
+ });
177
+
178
+ this.autoScalingGroups.set(asg.AutoScalingGroupName, asg);
179
+ logger.info(`✅ Auto Scaling Group criado: ${name}`);
180
+
181
+ // Se desiredCapacity > 0, criar instâncias
182
+ if (asg.DesiredCapacity > 0 && this.ec2Service) {
183
+ this._scaleToDesiredCapacity(asg);
184
+ }
185
+
186
+ // ✅ Persistir mudanças
187
+ this.persistData();
188
+
189
+ return asg;
190
+ }
191
+
192
+ describeAutoScalingGroups(filters = {}) {
193
+ let asgs = Array.from(this.autoScalingGroups.values());
194
+
195
+ if (filters.names && Array.isArray(filters.names)) {
196
+ asgs = asgs.filter(a => filters.names.includes(a.AutoScalingGroupName));
197
+ }
198
+
199
+ return asgs;
200
+ }
201
+
202
+ getAutoScalingGroup(name) {
203
+ const asg = this.autoScalingGroups.get(name);
204
+ if (!asg) {
205
+ throw new Error(`Auto Scaling Group ${name} not found`);
206
+ }
207
+ return asg;
208
+ }
209
+
210
+ deleteAutoScalingGroup(name, forceDelete = false) {
211
+ const asg = this.getAutoScalingGroup(name);
212
+
213
+ // Se forceDelete, terminar todas as instâncias
214
+ if (forceDelete && this.ec2Service) {
215
+ for (const inst of asg.Instances) {
216
+ try {
217
+ const ec2Sim = this.ec2Service.getSimulator();
218
+ ec2Sim.terminateInstances([inst.InstanceId]);
219
+ } catch (e) {
220
+ logger.warn(`Erro ao terminar instância ${inst.InstanceId}: ${e.message}`);
221
+ }
222
+ }
223
+ }
224
+
225
+ if (asg.Instances.length > 0 && !forceDelete) {
226
+ throw new Error(`Auto Scaling Group ${name} has running instances. Use forceDelete=true to terminate them.`);
227
+ }
228
+
229
+ this.autoScalingGroups.delete(name);
230
+ logger.info(`🗑️ Auto Scaling Group deletado: ${name}`);
231
+ // ✅ Persistir mudanças
232
+ this.persistData();
233
+ return true;
234
+ }
235
+
236
+ // ========== Scaling Operations ==========
237
+
238
+ setDesiredCapacity(asgName, desiredCapacity) {
239
+ const asg = this.getAutoScalingGroup(asgName);
240
+
241
+ if (desiredCapacity < asg.MinSize || desiredCapacity > asg.MaxSize) {
242
+ throw new Error(
243
+ `Desired capacity ${desiredCapacity} must be between ${asg.MinSize} and ${asg.MaxSize}`
244
+ );
245
+ }
246
+
247
+ const oldCapacity = asg.DesiredCapacity;
248
+ asg.DesiredCapacity = desiredCapacity;
249
+
250
+ logger.info(`🎯 ASG ${asgName}: Desired capacity alterado de ${oldCapacity} para ${desiredCapacity}`);
251
+
252
+ // Aplicar scaling imediatamente se EC2 service está disponível
253
+ if (this.ec2Service) {
254
+ this._scaleToDesiredCapacity(asg);
255
+ }
256
+
257
+ // ✅ Persistir mudanças
258
+ this.persistData();
259
+
260
+ return asg;
261
+ }
262
+
263
+ // ========== Scaling Policy Operations ==========
264
+
265
+ attachScalingPolicy(asgName, policyName, policyType, config = {}) {
266
+ const asg = this.getAutoScalingGroup(asgName);
267
+
268
+ const policy = new ScalingPolicy(policyName, asgName, policyType, config);
269
+ const policyKey = `${asgName}:${policyName}`;
270
+ this.scalingPolicies.set(policyKey, policy);
271
+
272
+ asg.scalingPolicies.push(policy);
273
+ logger.info(`✅ Scaling Policy criado: ${policyName} para ASG ${asgName}`);
274
+ // ✅ Persistir mudanças
275
+ this.persistData();
276
+ return policy;
277
+ }
278
+
279
+ describeScalingPolicies(asgName) {
280
+ const asg = this.getAutoScalingGroup(asgName);
281
+ return asg.scalingPolicies;
282
+ }
283
+
284
+ detachScalingPolicy(asgName, policyName) {
285
+ const asg = this.getAutoScalingGroup(asgName);
286
+ const policyKey = `${asgName}:${policyName}`;
287
+
288
+ asg.scalingPolicies = asg.scalingPolicies.filter(p => p.PolicyName !== policyName);
289
+ this.scalingPolicies.delete(policyKey);
290
+
291
+ logger.info(`🗑️ Scaling Policy deletado: ${policyName}`);
292
+ // ✅ Persistir mudanças
293
+ this.persistData();
294
+ return true;
295
+ }
296
+
297
+ // ========== Internal Scaling Logic ==========
298
+
299
+ _scaleToDesiredCapacity(asg) {
300
+ const currentCount = asg.getInstanceCount();
301
+ const desiredCount = asg.DesiredCapacity;
302
+
303
+ if (currentCount === desiredCount) {
304
+ return;
305
+ }
306
+
307
+ const ec2Sim = this.ec2Service.getSimulator();
308
+
309
+ if (currentCount < desiredCount) {
310
+ // Scale up
311
+ const instancesToCreate = desiredCount - currentCount;
312
+ logger.debug(`🔺 Escalando UP: criando ${instancesToCreate} instâncias`);
313
+
314
+ for (let i = 0; i < instancesToCreate; i++) {
315
+ try {
316
+ const runResult = ec2Sim.runInstances({
317
+ ImageId: asg.LaunchTemplate.ImageId,
318
+ MinCount: 1,
319
+ MaxCount: 1,
320
+ InstanceType: asg.LaunchTemplate.InstanceType,
321
+ SecurityGroupIds: asg.LaunchTemplate.SecurityGroupIds,
322
+ Tags: [
323
+ { Key: 'aws:autoscaling:groupName', Value: asg.AutoScalingGroupName },
324
+ ...asg.Tags
325
+ ]
326
+ });
327
+
328
+ const newInstance = runResult.Instances[0];
329
+ asg.addInstance({
330
+ InstanceId: newInstance.InstanceId,
331
+ InstanceType: newInstance.InstanceType,
332
+ AvailabilityZone: newInstance.AvailabilityZone
333
+ });
334
+
335
+ // Registrar em target groups do ALB se configurado
336
+ if (asg.TargetGroupARNs.length > 0 && this.albService) {
337
+ for (const tgArn of asg.TargetGroupARNs) {
338
+ try {
339
+ const albSim = this.albService.getSimulator();
340
+ albSim.registerTarget(tgArn, newInstance.InstanceId, newInstance.NetworkInterfaces[0].Port);
341
+ } catch (e) {
342
+ logger.warn(`Erro ao registrar target no ALB: ${e.message}`);
343
+ }
344
+ }
345
+ }
346
+
347
+ logger.info(`✅ Instância ${newInstance.InstanceId} criada por ASG`);
348
+ } catch (e) {
349
+ logger.error(`Erro ao criar instância: ${e.message}`);
350
+ }
351
+ }
352
+ } else {
353
+ // Scale down
354
+ const instancesToTerminate = currentCount - desiredCount;
355
+ logger.debug(`🔻 Escalando DOWN: terminando ${instancesToTerminate} instâncias`);
356
+
357
+ const instancesToRemove = asg.Instances.slice(0, instancesToTerminate);
358
+
359
+ for (const inst of instancesToRemove) {
360
+ try {
361
+ ec2Sim.terminateInstances([inst.InstanceId]);
362
+ asg.removeInstance(inst.InstanceId);
363
+ logger.info(`🗑️ Instância ${inst.InstanceId} terminada por ASG`);
364
+ } catch (e) {
365
+ logger.warn(`Erro ao terminar instância: ${e.message}`);
366
+ }
367
+ }
368
+ }
369
+
370
+ asg.lastScalingTime = new Date();
371
+ }
372
+
373
+ evaluateScalingPolicies(asgName) {
374
+ const asg = this.getAutoScalingGroup(asgName);
375
+
376
+ for (const policy of asg.scalingPolicies) {
377
+ if (!policy.Enabled) continue;
378
+
379
+ // Target tracking: simular com métrica aleatória
380
+ if (policy.PolicyType === 'TargetTrackingScaling') {
381
+ const randomMetric = Math.random() * 100;
382
+
383
+ if (randomMetric > policy.TargetValue * 1.2 && asg.canScaleUp()) {
384
+ logger.info(`📈 Scaling up: métrica ${randomMetric.toFixed(2)}% > target ${policy.TargetValue}%`);
385
+ this.setDesiredCapacity(asgName, asg.DesiredCapacity + 1);
386
+ } else if (randomMetric < policy.TargetValue * 0.8 && asg.canScaleDown()) {
387
+ logger.info(`📉 Scaling down: métrica ${randomMetric.toFixed(2)}% < target ${policy.TargetValue * 0.8}%`);
388
+ this.setDesiredCapacity(asgName, asg.DesiredCapacity - 1);
389
+ }
390
+ }
391
+ }
392
+ }
393
+
394
+ // ========== Utility ==========
395
+
396
+ injectDependencies(ec2Service, albService) {
397
+ this.ec2Service = ec2Service;
398
+ this.albService = albService;
399
+ }
400
+
401
+ getAllResources() {
402
+ return {
403
+ autoScalingGroups: Array.from(this.autoScalingGroups.values()),
404
+ launchTemplates: Array.from(this.launchTemplates.values()),
405
+ scalingPolicies: Array.from(this.scalingPolicies.values())
406
+ };
407
+ }
408
+ }
409
+
410
+ module.exports = ASGSimulator;