@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,270 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DB Engine - Gerencia múltiplas instâncias SQLite (uma por database lógico Aurora).
|
|
5
|
+
* Responsável por executar queries, gerenciar transações e ciclo de vida de conexões.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const Database = require('better-sqlite3');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const logger = require('../../utils/logger');
|
|
13
|
+
|
|
14
|
+
class DbEngine {
|
|
15
|
+
constructor(dataDir) {
|
|
16
|
+
this.dataDir = dataDir;
|
|
17
|
+
this.databases = new Map(); // dbName → better-sqlite3 Database instance
|
|
18
|
+
this.transactions = new Map(); // transactionId → { db, dbName, startedAt }
|
|
19
|
+
|
|
20
|
+
// Ensure data directory exists
|
|
21
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets or lazily creates a SQLite database file for the given database name.
|
|
26
|
+
* @param {string} dbName - Logical database name
|
|
27
|
+
* @returns {import('better-sqlite3').Database} The better-sqlite3 database instance
|
|
28
|
+
*/
|
|
29
|
+
getOrCreateDatabase(dbName) {
|
|
30
|
+
if (this.databases.has(dbName)) {
|
|
31
|
+
return this.databases.get(dbName);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const dbPath = path.join(this.dataDir, `${dbName}.db`);
|
|
35
|
+
const db = new Database(dbPath);
|
|
36
|
+
|
|
37
|
+
// Performance optimizations
|
|
38
|
+
db.pragma('journal_mode = WAL');
|
|
39
|
+
db.pragma('foreign_keys = ON');
|
|
40
|
+
|
|
41
|
+
this.databases.set(dbName, db);
|
|
42
|
+
logger.debug(`[Aurora DbEngine] Database '${dbName}' created/opened at ${dbPath}`);
|
|
43
|
+
|
|
44
|
+
return db;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Executes a SQL statement on the specified database.
|
|
49
|
+
* Detects statement type (SELECT, DML, DDL) and returns structured results.
|
|
50
|
+
* @param {string} dbName - Target database name
|
|
51
|
+
* @param {string} sql - SQL statement to execute
|
|
52
|
+
* @param {Array} params - Bound parameters
|
|
53
|
+
* @returns {{ type: string, rows?: Array, columns?: Array, changes?: number, lastInsertRowid?: number }}
|
|
54
|
+
*/
|
|
55
|
+
execute(dbName, sql, params) {
|
|
56
|
+
const db = this.getOrCreateDatabase(dbName);
|
|
57
|
+
return this._executeOnDb(db, sql, params);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Begins a new transaction on the specified database.
|
|
62
|
+
* @param {string} dbName - Target database name
|
|
63
|
+
* @returns {string} Unique transaction ID (UUID)
|
|
64
|
+
*/
|
|
65
|
+
beginTransaction(dbName) {
|
|
66
|
+
const db = this.getOrCreateDatabase(dbName);
|
|
67
|
+
const transactionId = crypto.randomUUID();
|
|
68
|
+
|
|
69
|
+
db.exec('BEGIN');
|
|
70
|
+
|
|
71
|
+
this.transactions.set(transactionId, {
|
|
72
|
+
db,
|
|
73
|
+
dbName,
|
|
74
|
+
startedAt: Date.now()
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
logger.debug(`[Aurora DbEngine] Transaction ${transactionId} started on '${dbName}'`);
|
|
78
|
+
return transactionId;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Executes a SQL statement within an active transaction.
|
|
83
|
+
* @param {string} transactionId - Active transaction ID
|
|
84
|
+
* @param {string} sql - SQL statement to execute
|
|
85
|
+
* @param {Array} params - Bound parameters
|
|
86
|
+
* @returns {{ type: string, rows?: Array, columns?: Array, changes?: number, lastInsertRowid?: number }}
|
|
87
|
+
*/
|
|
88
|
+
executeInTransaction(transactionId, sql, params) {
|
|
89
|
+
const transaction = this.transactions.get(transactionId);
|
|
90
|
+
if (!transaction) {
|
|
91
|
+
throw new Error(`Transaction ${transactionId} is not found`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return this._executeOnDb(transaction.db, sql, params);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Commits an active transaction.
|
|
99
|
+
* @param {string} transactionId - Transaction ID to commit
|
|
100
|
+
*/
|
|
101
|
+
commit(transactionId) {
|
|
102
|
+
const transaction = this.transactions.get(transactionId);
|
|
103
|
+
if (!transaction) {
|
|
104
|
+
throw new Error(`Transaction ${transactionId} is not found`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
transaction.db.exec('COMMIT');
|
|
108
|
+
this.transactions.delete(transactionId);
|
|
109
|
+
logger.debug(`[Aurora DbEngine] Transaction ${transactionId} committed`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Rolls back an active transaction.
|
|
114
|
+
* @param {string} transactionId - Transaction ID to rollback
|
|
115
|
+
*/
|
|
116
|
+
rollback(transactionId) {
|
|
117
|
+
const transaction = this.transactions.get(transactionId);
|
|
118
|
+
if (!transaction) {
|
|
119
|
+
throw new Error(`Transaction ${transactionId} is not found`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
transaction.db.exec('ROLLBACK');
|
|
123
|
+
this.transactions.delete(transactionId);
|
|
124
|
+
logger.debug(`[Aurora DbEngine] Transaction ${transactionId} rolled back`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Lists all existing databases (based on .db files in the data directory).
|
|
129
|
+
* @returns {string[]} Array of database names (without .db extension)
|
|
130
|
+
*/
|
|
131
|
+
listDatabases() {
|
|
132
|
+
if (!fs.existsSync(this.dataDir)) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return fs.readdirSync(this.dataDir)
|
|
137
|
+
.filter(file => file.endsWith('.db'))
|
|
138
|
+
.map(file => file.slice(0, -3));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Drops a database: closes connection and deletes the .db file.
|
|
143
|
+
* @param {string} dbName - Database name to drop
|
|
144
|
+
*/
|
|
145
|
+
dropDatabase(dbName) {
|
|
146
|
+
// Close connection if open
|
|
147
|
+
if (this.databases.has(dbName)) {
|
|
148
|
+
const db = this.databases.get(dbName);
|
|
149
|
+
db.close();
|
|
150
|
+
this.databases.delete(dbName);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Delete the .db file
|
|
154
|
+
const dbPath = path.join(this.dataDir, `${dbName}.db`);
|
|
155
|
+
if (fs.existsSync(dbPath)) {
|
|
156
|
+
fs.unlinkSync(dbPath);
|
|
157
|
+
logger.debug(`[Aurora DbEngine] Database '${dbName}' dropped`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Closes all open SQLite connections and clears the databases Map.
|
|
163
|
+
*/
|
|
164
|
+
close() {
|
|
165
|
+
for (const [dbName, db] of this.databases) {
|
|
166
|
+
try {
|
|
167
|
+
db.close();
|
|
168
|
+
} catch (err) {
|
|
169
|
+
logger.debug(`[Aurora DbEngine] Error closing database '${dbName}': ${err.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
this.databases.clear();
|
|
173
|
+
this.transactions.clear();
|
|
174
|
+
logger.debug('[Aurora DbEngine] All connections closed');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Resets the engine: closes all connections and deletes all .db files.
|
|
179
|
+
*/
|
|
180
|
+
reset() {
|
|
181
|
+
this.close();
|
|
182
|
+
|
|
183
|
+
// Delete all .db files in the data directory
|
|
184
|
+
if (fs.existsSync(this.dataDir)) {
|
|
185
|
+
const files = fs.readdirSync(this.dataDir).filter(f => f.endsWith('.db'));
|
|
186
|
+
for (const file of files) {
|
|
187
|
+
fs.unlinkSync(path.join(this.dataDir, file));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
logger.debug('[Aurora DbEngine] Engine reset complete');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Internal: executes SQL on a given database instance, detecting statement type.
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
_executeOnDb(db, sql, params) {
|
|
199
|
+
const trimmed = sql.trim();
|
|
200
|
+
const upper = trimmed.toUpperCase();
|
|
201
|
+
|
|
202
|
+
// Detect statement type
|
|
203
|
+
if (upper.startsWith('SELECT') || upper.startsWith('PRAGMA') || upper.startsWith('EXPLAIN')) {
|
|
204
|
+
return this._executeSelect(db, trimmed, params);
|
|
205
|
+
} else if (
|
|
206
|
+
upper.startsWith('INSERT') ||
|
|
207
|
+
upper.startsWith('UPDATE') ||
|
|
208
|
+
upper.startsWith('DELETE') ||
|
|
209
|
+
upper.startsWith('REPLACE')
|
|
210
|
+
) {
|
|
211
|
+
return this._executeDml(db, trimmed, params);
|
|
212
|
+
} else {
|
|
213
|
+
// DDL: CREATE, DROP, ALTER, etc.
|
|
214
|
+
return this._executeDdl(db, trimmed);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Executes a SELECT statement, returning rows and column metadata.
|
|
220
|
+
* @private
|
|
221
|
+
*/
|
|
222
|
+
_executeSelect(db, sql, params) {
|
|
223
|
+
const stmt = db.prepare(sql);
|
|
224
|
+
const rows = stmt.all(...(params || []));
|
|
225
|
+
const columns = stmt.columns();
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
type: 'select',
|
|
229
|
+
rows,
|
|
230
|
+
columns,
|
|
231
|
+
changes: 0,
|
|
232
|
+
lastInsertRowid: 0
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Executes a DML statement (INSERT/UPDATE/DELETE), returning change count.
|
|
238
|
+
* @private
|
|
239
|
+
*/
|
|
240
|
+
_executeDml(db, sql, params) {
|
|
241
|
+
const stmt = db.prepare(sql);
|
|
242
|
+
const result = stmt.run(...(params || []));
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
type: 'dml',
|
|
246
|
+
rows: [],
|
|
247
|
+
columns: [],
|
|
248
|
+
changes: result.changes,
|
|
249
|
+
lastInsertRowid: Number(result.lastInsertRowid)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Executes a DDL statement (CREATE/DROP/ALTER), returning zero changes.
|
|
255
|
+
* @private
|
|
256
|
+
*/
|
|
257
|
+
_executeDdl(db, sql) {
|
|
258
|
+
db.exec(sql);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
type: 'ddl',
|
|
262
|
+
rows: [],
|
|
263
|
+
columns: [],
|
|
264
|
+
changes: 0,
|
|
265
|
+
lastInsertRowid: 0
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = { DbEngine };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Aurora Service - Ponto de entrada
|
|
5
|
+
* Simula Amazon Aurora (MySQL/PostgreSQL-compatible) via Data API e RDS Management API.
|
|
6
|
+
* Porta padrão: 4510
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const AuroraServer = require('./server');
|
|
11
|
+
const { DbEngine } = require('./db-engine');
|
|
12
|
+
const { ClusterManager } = require('./cluster-manager');
|
|
13
|
+
const { DataApiHandler } = require('./data-api');
|
|
14
|
+
const { RdsApiHandler } = require('./rds-api');
|
|
15
|
+
const LocalStore = require('../../utils/local-store');
|
|
16
|
+
|
|
17
|
+
class AuroraService {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.name = 'aurora';
|
|
21
|
+
this.port = config?.ports?.aurora || 4510;
|
|
22
|
+
this.server = null;
|
|
23
|
+
this.dbEngine = null;
|
|
24
|
+
this.clusterManager = null;
|
|
25
|
+
this.dataApiHandler = null;
|
|
26
|
+
this.rdsApiHandler = null;
|
|
27
|
+
this.isRunning = false;
|
|
28
|
+
|
|
29
|
+
// Dependencies injected later via injectDependencies()
|
|
30
|
+
this.kmsService = null;
|
|
31
|
+
this.secretManagerService = null;
|
|
32
|
+
this.cloudTrailService = null;
|
|
33
|
+
this.cloudWatchService = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async initialize() {
|
|
37
|
+
const logger = require('../../utils/logger');
|
|
38
|
+
logger.debug(`Inicializando Aurora Service na porta ${this.port}...`);
|
|
39
|
+
|
|
40
|
+
// Data directory for SQLite files
|
|
41
|
+
const dataDir = path.join(this.config.dataDir || './data', 'aurora');
|
|
42
|
+
|
|
43
|
+
// Create LocalStore for JSON persistence
|
|
44
|
+
const store = new LocalStore(dataDir);
|
|
45
|
+
|
|
46
|
+
// 1. Create core components
|
|
47
|
+
this.dbEngine = new DbEngine(dataDir);
|
|
48
|
+
this.clusterManager = new ClusterManager(dataDir, store);
|
|
49
|
+
this.clusterManager.initialize();
|
|
50
|
+
|
|
51
|
+
// 2. Create handlers with dependencies
|
|
52
|
+
this.dataApiHandler = new DataApiHandler(this.dbEngine, null, this.clusterManager, logger);
|
|
53
|
+
this.rdsApiHandler = new RdsApiHandler(this.clusterManager, this.dbEngine, logger);
|
|
54
|
+
|
|
55
|
+
// 3. Create server and wire handlers
|
|
56
|
+
this.server = new AuroraServer(this.port, this.config);
|
|
57
|
+
this.server.dataApiHandler = this.dataApiHandler;
|
|
58
|
+
this.server.rdsApiHandler = this.rdsApiHandler;
|
|
59
|
+
this.server.clusterManager = this.clusterManager;
|
|
60
|
+
this.server.dbEngine = this.dbEngine;
|
|
61
|
+
await this.server.initialize();
|
|
62
|
+
|
|
63
|
+
logger.debug('Aurora Service inicializado');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
injectDependencies(server) {
|
|
67
|
+
const kms = server.getService('kms');
|
|
68
|
+
if (kms?.simulator) {
|
|
69
|
+
this.kmsService = kms.simulator;
|
|
70
|
+
if (this.rdsApiHandler) this.rdsApiHandler.kmsService = this.kmsService;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const secretManager = server.getService('secret-manager');
|
|
74
|
+
if (secretManager?.simulator) {
|
|
75
|
+
this.secretManagerService = secretManager.simulator;
|
|
76
|
+
if (this.dataApiHandler) this.dataApiHandler.secretManagerService = this.secretManagerService;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const cloudTrail = server.getService('cloudtrail');
|
|
80
|
+
if (cloudTrail?.simulator) {
|
|
81
|
+
this.cloudTrailService = cloudTrail.simulator;
|
|
82
|
+
if (this.rdsApiHandler) this.rdsApiHandler.cloudTrailService = this.cloudTrailService;
|
|
83
|
+
if (this.dataApiHandler) this.dataApiHandler.cloudTrailService = this.cloudTrailService;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const cloudWatch = server.getService('cloudwatch');
|
|
87
|
+
if (cloudWatch?.simulator) {
|
|
88
|
+
this.cloudWatchService = cloudWatch.simulator;
|
|
89
|
+
if (this.dataApiHandler) this.dataApiHandler.cloudWatchService = this.cloudWatchService;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async start() {
|
|
94
|
+
if (this.isRunning) return;
|
|
95
|
+
await this.server.start();
|
|
96
|
+
this.isRunning = true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async stop() {
|
|
100
|
+
if (!this.isRunning) return;
|
|
101
|
+
await this.server.stop();
|
|
102
|
+
if (this.dbEngine) this.dbEngine.close();
|
|
103
|
+
if (this.clusterManager?.metaDb) {
|
|
104
|
+
this.clusterManager.metaDb.close();
|
|
105
|
+
this.clusterManager.metaDb = null;
|
|
106
|
+
}
|
|
107
|
+
this.isRunning = false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async reset() {
|
|
111
|
+
// Reset cluster metadata first (clears rows but keeps metaDb open)
|
|
112
|
+
if (this.clusterManager) {
|
|
113
|
+
this.clusterManager.reset();
|
|
114
|
+
// Also clear the JSON persistence file
|
|
115
|
+
const dataDir = path.join(this.config.dataDir || './data', 'aurora');
|
|
116
|
+
const store = new LocalStore(dataDir);
|
|
117
|
+
store.delete('__aurora__');
|
|
118
|
+
}
|
|
119
|
+
// Close all user database connections and delete their files
|
|
120
|
+
if (this.dbEngine) {
|
|
121
|
+
this.dbEngine.close();
|
|
122
|
+
// Remove only databases managed by DbEngine (not metadata.db)
|
|
123
|
+
const fs = require('fs');
|
|
124
|
+
const dataDir = this.dbEngine.dataDir;
|
|
125
|
+
if (fs.existsSync(dataDir)) {
|
|
126
|
+
const files = fs.readdirSync(dataDir).filter(f => f.endsWith('.db') && f !== 'metadata.db');
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
fs.unlinkSync(path.join(dataDir, file));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getStatus() {
|
|
135
|
+
return {
|
|
136
|
+
running: this.isRunning,
|
|
137
|
+
port: this.port,
|
|
138
|
+
endpoint: `http://localhost:${this.port}`,
|
|
139
|
+
clusters: this.clusterManager?.listClusters?.()?.length || 0,
|
|
140
|
+
databases: this.dbEngine?.listDatabases?.()?.length || 0,
|
|
141
|
+
activeTransactions: this.dataApiHandler?.transactions?.size || 0
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getSimulator() {
|
|
146
|
+
return {
|
|
147
|
+
dataApiHandler: this.dataApiHandler,
|
|
148
|
+
rdsApiHandler: this.rdsApiHandler,
|
|
149
|
+
clusterManager: this.clusterManager,
|
|
150
|
+
dbEngine: this.dbEngine
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getServer() {
|
|
155
|
+
return this.server;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = { AuroraService };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RDS Management API Handler - Simula operações de gerenciamento RDS
|
|
5
|
+
* (CreateDBCluster, DeleteDBCluster, DescribeDBClusters, CreateDBInstance,
|
|
6
|
+
* DeleteDBInstance, DescribeDBInstances, ModifyDBCluster, StopDBCluster, StartDBCluster)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const logger = require('../../utils/logger');
|
|
10
|
+
|
|
11
|
+
class RdsApiHandler {
|
|
12
|
+
constructor(clusterManager, dbEngine, log) {
|
|
13
|
+
this.clusterManager = clusterManager;
|
|
14
|
+
this.dbEngine = dbEngine;
|
|
15
|
+
this.logger = log || logger;
|
|
16
|
+
|
|
17
|
+
// Optional service dependencies (injected later)
|
|
18
|
+
this.kmsService = null;
|
|
19
|
+
this.cloudTrailService = null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Routes an incoming RDS API action to the appropriate handler method.
|
|
24
|
+
* @param {string} action - The RDS API action name
|
|
25
|
+
* @param {Object} body - The request body/parameters
|
|
26
|
+
* @returns {Object} The response object
|
|
27
|
+
*/
|
|
28
|
+
async handleRequest(action, body) {
|
|
29
|
+
switch (action) {
|
|
30
|
+
case 'CreateDBCluster': return this.createDBCluster(body);
|
|
31
|
+
case 'DeleteDBCluster': return this.deleteDBCluster(body);
|
|
32
|
+
case 'DescribeDBClusters': return this.describeDBClusters(body);
|
|
33
|
+
case 'CreateDBInstance': return this.createDBInstance(body);
|
|
34
|
+
case 'DeleteDBInstance': return this.deleteDBInstance(body);
|
|
35
|
+
case 'DescribeDBInstances': return this.describeDBInstances(body);
|
|
36
|
+
case 'ModifyDBCluster': return this.modifyDBCluster(body);
|
|
37
|
+
case 'StopDBCluster': return this.stopDBCluster(body);
|
|
38
|
+
case 'StartDBCluster': return this.startDBCluster(body);
|
|
39
|
+
default: {
|
|
40
|
+
const error = new Error(`Unknown action: ${action}`);
|
|
41
|
+
error.code = 'InvalidAction';
|
|
42
|
+
error.statusCode = 400;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new Aurora DB cluster.
|
|
50
|
+
* Validates KMS key if StorageEncrypted, creates logical database if DatabaseName provided.
|
|
51
|
+
* Records CloudTrail event.
|
|
52
|
+
*/
|
|
53
|
+
async createDBCluster(params) {
|
|
54
|
+
// Validate KMS key if StorageEncrypted and KmsKeyId provided
|
|
55
|
+
if (params.StorageEncrypted && params.KmsKeyId && this.kmsService) {
|
|
56
|
+
try {
|
|
57
|
+
await this.kmsService.describeKey({ KeyId: params.KmsKeyId });
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const error = new Error(`KMS key not found or invalid: ${params.KmsKeyId}`);
|
|
60
|
+
error.code = 'InvalidParameterValue';
|
|
61
|
+
error.statusCode = 400;
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Create via ClusterManager (handles all validation)
|
|
67
|
+
const cluster = this.clusterManager.createCluster(params);
|
|
68
|
+
|
|
69
|
+
// If DatabaseName is provided, ensure the database exists in DbEngine
|
|
70
|
+
if (params.DatabaseName && this.dbEngine) {
|
|
71
|
+
this.dbEngine.getOrCreateDatabase(params.DatabaseName);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Record CloudTrail event
|
|
75
|
+
this._recordEvent('CreateDBCluster', params.DBClusterIdentifier);
|
|
76
|
+
|
|
77
|
+
return { DBCluster: cluster };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Deletes an existing Aurora DB cluster.
|
|
82
|
+
* Throws DBClusterNotFoundFault if cluster does not exist.
|
|
83
|
+
*/
|
|
84
|
+
async deleteDBCluster(params) {
|
|
85
|
+
const { DBClusterIdentifier } = params;
|
|
86
|
+
this.clusterManager.deleteCluster(DBClusterIdentifier);
|
|
87
|
+
this._recordEvent('DeleteDBCluster', DBClusterIdentifier);
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Describes one or all Aurora DB clusters.
|
|
93
|
+
* Returns all clusters when no filter, or a specific one by DBClusterIdentifier.
|
|
94
|
+
*/
|
|
95
|
+
async describeDBClusters(params) {
|
|
96
|
+
const { DBClusterIdentifier } = params || {};
|
|
97
|
+
const filters = DBClusterIdentifier ? { DBClusterIdentifier } : undefined;
|
|
98
|
+
const clusters = this.clusterManager.listClusters(filters);
|
|
99
|
+
return { DBClusters: clusters };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates a new Aurora DB instance within an existing cluster.
|
|
104
|
+
* Records CloudTrail event.
|
|
105
|
+
*/
|
|
106
|
+
async createDBInstance(params) {
|
|
107
|
+
const instance = this.clusterManager.createInstance(params);
|
|
108
|
+
this._recordEvent('CreateDBInstance', params.DBInstanceIdentifier);
|
|
109
|
+
return { DBInstance: instance };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Deletes an existing Aurora DB instance.
|
|
114
|
+
* Throws DBInstanceNotFoundFault if instance does not exist.
|
|
115
|
+
*/
|
|
116
|
+
async deleteDBInstance(params) {
|
|
117
|
+
const { DBInstanceIdentifier } = params;
|
|
118
|
+
this.clusterManager.deleteInstance(DBInstanceIdentifier);
|
|
119
|
+
this._recordEvent('DeleteDBInstance', DBInstanceIdentifier);
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Describes one or all Aurora DB instances.
|
|
125
|
+
* Returns all instances when no filter, or a specific one by DBInstanceIdentifier.
|
|
126
|
+
*/
|
|
127
|
+
async describeDBInstances(params) {
|
|
128
|
+
const { DBInstanceIdentifier } = params || {};
|
|
129
|
+
const filters = DBInstanceIdentifier ? { DBInstanceIdentifier } : undefined;
|
|
130
|
+
const instances = this.clusterManager.listInstances(filters);
|
|
131
|
+
return { DBInstances: instances };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Modifies an existing Aurora DB cluster's fields.
|
|
136
|
+
* Records CloudTrail event.
|
|
137
|
+
*/
|
|
138
|
+
async modifyDBCluster(params) {
|
|
139
|
+
const { DBClusterIdentifier, ...fields } = params;
|
|
140
|
+
const cluster = this.clusterManager.updateCluster(DBClusterIdentifier, fields);
|
|
141
|
+
this._recordEvent('ModifyDBCluster', DBClusterIdentifier);
|
|
142
|
+
return { DBCluster: cluster };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Stops an Aurora DB cluster (changes status to 'stopped').
|
|
147
|
+
* Records CloudTrail event.
|
|
148
|
+
*/
|
|
149
|
+
async stopDBCluster(params) {
|
|
150
|
+
const { DBClusterIdentifier } = params;
|
|
151
|
+
const cluster = this.clusterManager.updateCluster(DBClusterIdentifier, { Status: 'stopped' });
|
|
152
|
+
this._recordEvent('StopDBCluster', DBClusterIdentifier);
|
|
153
|
+
return { DBCluster: cluster };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Starts a stopped Aurora DB cluster (changes status to 'available').
|
|
158
|
+
* Records CloudTrail event.
|
|
159
|
+
*/
|
|
160
|
+
async startDBCluster(params) {
|
|
161
|
+
const { DBClusterIdentifier } = params;
|
|
162
|
+
const cluster = this.clusterManager.updateCluster(DBClusterIdentifier, { Status: 'available' });
|
|
163
|
+
this._recordEvent('StartDBCluster', DBClusterIdentifier);
|
|
164
|
+
return { DBCluster: cluster };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Records a CloudTrail event for an RDS Management API operation.
|
|
169
|
+
* Silently fails if CloudTrail service is unavailable or throws.
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
_recordEvent(eventName, resourceId) {
|
|
173
|
+
if (this.cloudTrailService) {
|
|
174
|
+
try {
|
|
175
|
+
this.cloudTrailService.recordEvent({
|
|
176
|
+
eventSource: 'rds.amazonaws.com',
|
|
177
|
+
eventName,
|
|
178
|
+
resources: [{ ResourceType: 'AWS::RDS::DBCluster', ResourceName: resourceId || 'unknown' }],
|
|
179
|
+
requestParameters: { dBClusterIdentifier: resourceId || 'unknown' }
|
|
180
|
+
});
|
|
181
|
+
} catch (e) {
|
|
182
|
+
// Don't fail the request if CloudTrail recording fails
|
|
183
|
+
this.logger.debug(`[Aurora RdsApiHandler] CloudTrail recording failed: ${e.message}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
module.exports = { RdsApiHandler };
|