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