@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.
- package/README.md +662 -658
- package/package.json +11 -4
- 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/handler-loader.js +50 -5
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +18 -12
- package/src/services/lambda/simulator.js +73 -17
- 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,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;
|