@padua/cli 1.5.0 → 1.6.0

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 CHANGED
@@ -17,7 +17,7 @@ npm install -g @padua/cli
17
17
  **Optional:**
18
18
  - [npm](https://nodejs.org/) (for CodeArtifact registry authentication)
19
19
  - [Docker](https://www.docker.com/) (for ECR registry authentication)
20
- - [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) (for tunneling - coming soon)
20
+ - [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) (for `padua tunnel` command)
21
21
 
22
22
  > **Note:** `padua init` validates prerequisites and displays install URLs for any missing tools.
23
23
 
@@ -48,6 +48,7 @@ The `init` command will:
48
48
  | | ECR | Docker registry authentication |
49
49
  | **Config** | Init | Automatic SSO discovery and profile generation |
50
50
  | | Status | Health check for all authentication services |
51
+ | **Tunnel** | RDS | Secure tunnels to RDS/Aurora databases via SSM |
51
52
 
52
53
  ## Login Command
53
54
 
@@ -147,13 +148,61 @@ padua profile # Interactive AWS profile selection
147
148
  padua profile --setup # Output shell function for paws command
148
149
  ```
149
150
 
151
+ ## Tunnel Command
152
+
153
+ Create secure tunnels to RDS databases via SSM Session Manager.
154
+
155
+ ### Basic Usage
156
+
157
+ ```bash
158
+ padua tunnel roma rds # Interactive (prompts for port/cluster)
159
+ padua tunnel roma rds -p paduafg-development # Use specific profile
160
+ ```
161
+
162
+ ### Options
163
+
164
+ | Flag | Description |
165
+ |------|-------------|
166
+ | `-p, --profile <name>` | AWS profile to use |
167
+ | `--local-port <port>` | Local port for the tunnel |
168
+ | `--cluster <name>` | RDS cluster or instance name |
169
+ | `-v, --verbose` | Show detailed output |
170
+ | `--no-color` | Disable colored output |
171
+
172
+ ### System Configuration
173
+
174
+ Different systems use different EC2 instances as tunnel endpoints:
175
+
176
+ | System | EC2 Tag | Default Port | Default Cluster |
177
+ |--------|---------|--------------|-----------------|
178
+ | roma | bastion-host | 3306 (MySQL) | roma-backend |
179
+ | (others) | jumpbox | 5432 (PostgreSQL) | (none) |
180
+
181
+ ### Example Output
182
+
183
+ ```
184
+ Checking port 3306 availability...
185
+ ✓ Port 3306 is available
186
+ Looking up bastion-host instance...
187
+ ✓ Found instance: i-0abc123def456
188
+ Looking up RDS endpoint for roma-backend...
189
+ ✓ Found endpoint: roma-backend.cluster-xxx.ap-southeast-2.rds.amazonaws.com
190
+
191
+ Starting tunnel...
192
+ System: roma
193
+ Profile: paduafg-development
194
+ Local port: 3306
195
+ Remote: roma-backend.cluster-xxx.ap-southeast-2.rds.amazonaws.com:3306
196
+ Via: i-0abc123def456 (bastion-host)
197
+
198
+ Press Ctrl+C to close the tunnel
199
+ ```
200
+
150
201
  ## Planned Features
151
202
 
152
203
  The following commands are planned for future releases:
153
204
 
154
205
  ```bash
155
- padua tunnel rds # RDS PostgreSQL via jumpbox
156
- padua tunnel aurora # Aurora PostgreSQL via jumpbox
157
206
  padua tunnel opensearch # OpenSearch/Kibana dashboard
158
207
  padua tunnel ec2 <name> # SSM session to EC2 instance
159
208
  padua ecs exec <cluster> <task> # Execute in ECS container
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Get EC2 instance ID by Name tag
3
+ */
4
+ export declare function getInstanceIdByTag(tagName: string, profile: string, verbose?: boolean): string;
5
+ /**
6
+ * Get RDS cluster endpoint (Aurora)
7
+ */
8
+ export declare function getRdsClusterEndpoint(clusterName: string, profile: string, endpointType?: 'reader' | 'writer', verbose?: boolean): string | null;
9
+ /**
10
+ * Get RDS instance endpoint (non-Aurora)
11
+ */
12
+ export declare function getRdsInstanceEndpoint(instanceName: string, profile: string, verbose?: boolean): string | null;
13
+ /**
14
+ * Get RDS endpoint - tries cluster first, then instance
15
+ */
16
+ export declare function getRdsEndpoint(name: string, profile: string, verbose?: boolean): string;
17
+ //# sourceMappingURL=aws.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws.d.ts","sourceRoot":"","sources":["../../../src/commands/tunnel/aws.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CA2CR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,GAAE,QAAQ,GAAG,QAAmB,EAC5C,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAuCf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAsCf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CAcR"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInstanceIdByTag = getInstanceIdByTag;
4
+ exports.getRdsClusterEndpoint = getRdsClusterEndpoint;
5
+ exports.getRdsInstanceEndpoint = getRdsInstanceEndpoint;
6
+ exports.getRdsEndpoint = getRdsEndpoint;
7
+ const child_process_1 = require("child_process");
8
+ const utils_1 = require("../login/utils");
9
+ const AWS_TIMEOUT_MS = 30000;
10
+ /**
11
+ * Get EC2 instance ID by Name tag
12
+ */
13
+ function getInstanceIdByTag(tagName, profile, verbose) {
14
+ if (verbose) {
15
+ console.log(` [verbose] Looking up EC2 instance with Name tag: ${tagName}`);
16
+ }
17
+ const result = (0, child_process_1.spawnSync)('aws', [
18
+ 'ec2',
19
+ 'describe-instances',
20
+ '--filter', `Name=tag:Name,Values=${tagName}`,
21
+ '--query', 'Reservations[].Instances[?State.Name == `running`].InstanceId[]',
22
+ '--output', 'text',
23
+ '--profile', profile,
24
+ ], {
25
+ shell: false,
26
+ timeout: AWS_TIMEOUT_MS,
27
+ maxBuffer: 1024 * 1024,
28
+ stdio: ['pipe', 'pipe', 'pipe'],
29
+ env: { ...(0, utils_1.getSubprocessEnv)(), AWS_PROFILE: profile },
30
+ });
31
+ if (result.error) {
32
+ throw new Error(`Failed to query EC2: ${(0, utils_1.sanitizeOutput)(result.error.message)}`);
33
+ }
34
+ if (result.status !== 0) {
35
+ const stderr = result.stderr?.toString() || '';
36
+ throw new Error(`EC2 lookup failed: ${(0, utils_1.sanitizeOutput)(stderr)}`);
37
+ }
38
+ const instanceId = result.stdout?.toString().trim();
39
+ if (!instanceId) {
40
+ throw new Error(`No running EC2 instance found with Name tag: ${tagName}`);
41
+ }
42
+ if (verbose) {
43
+ console.log(` [verbose] Found instance: ${instanceId}`);
44
+ }
45
+ return instanceId;
46
+ }
47
+ /**
48
+ * Get RDS cluster endpoint (Aurora)
49
+ */
50
+ function getRdsClusterEndpoint(clusterName, profile, endpointType = 'reader', verbose) {
51
+ if (verbose) {
52
+ console.log(` [verbose] Looking up RDS cluster endpoint: ${clusterName} (${endpointType})`);
53
+ }
54
+ const result = (0, child_process_1.spawnSync)('aws', [
55
+ 'rds',
56
+ 'describe-db-cluster-endpoints',
57
+ '--db-cluster-identifier', clusterName,
58
+ '--filter', `Name=db-cluster-endpoint-type,Values=${endpointType}`,
59
+ '--query', 'DBClusterEndpoints[].Endpoint',
60
+ '--output', 'text',
61
+ '--profile', profile,
62
+ ], {
63
+ shell: false,
64
+ timeout: AWS_TIMEOUT_MS,
65
+ maxBuffer: 1024 * 1024,
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ env: { ...(0, utils_1.getSubprocessEnv)(), AWS_PROFILE: profile },
68
+ });
69
+ if (result.error || result.status !== 0) {
70
+ return null;
71
+ }
72
+ const endpoint = result.stdout?.toString().trim();
73
+ if (!endpoint || endpoint === 'None') {
74
+ return null;
75
+ }
76
+ if (verbose) {
77
+ console.log(` [verbose] Found cluster endpoint: ${endpoint}`);
78
+ }
79
+ return endpoint;
80
+ }
81
+ /**
82
+ * Get RDS instance endpoint (non-Aurora)
83
+ */
84
+ function getRdsInstanceEndpoint(instanceName, profile, verbose) {
85
+ if (verbose) {
86
+ console.log(` [verbose] Looking up RDS instance endpoint: ${instanceName}`);
87
+ }
88
+ const result = (0, child_process_1.spawnSync)('aws', [
89
+ 'rds',
90
+ 'describe-db-instances',
91
+ '--db-instance-identifier', instanceName,
92
+ '--query', 'DBInstances[0].Endpoint.Address',
93
+ '--output', 'text',
94
+ '--profile', profile,
95
+ ], {
96
+ shell: false,
97
+ timeout: AWS_TIMEOUT_MS,
98
+ maxBuffer: 1024 * 1024,
99
+ stdio: ['pipe', 'pipe', 'pipe'],
100
+ env: { ...(0, utils_1.getSubprocessEnv)(), AWS_PROFILE: profile },
101
+ });
102
+ if (result.error || result.status !== 0) {
103
+ return null;
104
+ }
105
+ const endpoint = result.stdout?.toString().trim();
106
+ if (!endpoint || endpoint === 'None') {
107
+ return null;
108
+ }
109
+ if (verbose) {
110
+ console.log(` [verbose] Found instance endpoint: ${endpoint}`);
111
+ }
112
+ return endpoint;
113
+ }
114
+ /**
115
+ * Get RDS endpoint - tries cluster first, then instance
116
+ */
117
+ function getRdsEndpoint(name, profile, verbose) {
118
+ // Try cluster endpoint first (Aurora)
119
+ const clusterEndpoint = getRdsClusterEndpoint(name, profile, 'reader', verbose);
120
+ if (clusterEndpoint) {
121
+ return clusterEndpoint;
122
+ }
123
+ // Fall back to instance endpoint
124
+ const instanceEndpoint = getRdsInstanceEndpoint(name, profile, verbose);
125
+ if (instanceEndpoint) {
126
+ return instanceEndpoint;
127
+ }
128
+ throw new Error(`Could not find RDS cluster or instance: ${name}`);
129
+ }
130
+ //# sourceMappingURL=aws.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws.js","sourceRoot":"","sources":["../../../src/commands/tunnel/aws.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ import { TunnelOptions } from './types';
3
+ /**
4
+ * Tunnel command - create secure tunnels to AWS resources via SSM
5
+ */
6
+ export declare const tunnelCommand: Command;
7
+ /**
8
+ * Handle the tunnel command
9
+ */
10
+ declare function handleTunnel(system: string, target: string, options: TunnelOptions): Promise<void>;
11
+ export { handleTunnel };
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/tunnel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIxC;;GAEG;AACH,eAAO,MAAM,aAAa,SASH,CAAC;AAExB;;GAEG;AACH,iBAAe,YAAY,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAGD,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tunnelCommand = void 0;
7
+ exports.handleTunnel = handleTunnel;
8
+ const commander_1 = require("commander");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const rds_1 = require("./rds");
11
+ const prerequisites_1 = require("../init/prerequisites");
12
+ /**
13
+ * Tunnel command - create secure tunnels to AWS resources via SSM
14
+ */
15
+ exports.tunnelCommand = new commander_1.Command('tunnel')
16
+ .description('Create secure tunnels to AWS resources via SSM Session Manager')
17
+ .argument('<system>', 'Target system (e.g., roma, padua-iam)')
18
+ .argument('<target>', 'Target service type (rds)')
19
+ .option('-p, --profile <name>', 'AWS profile to use')
20
+ .option('--local-port <port>', 'Local port for the tunnel')
21
+ .option('--cluster <name>', 'RDS cluster or instance name')
22
+ .option('-v, --verbose', 'Show detailed output')
23
+ .option('--no-color', 'Disable colored output')
24
+ .action(handleTunnel);
25
+ /**
26
+ * Handle the tunnel command
27
+ */
28
+ async function handleTunnel(system, target, options) {
29
+ const noColor = options.noColor ?? false;
30
+ // Check prerequisites
31
+ const ssmCheck = (0, prerequisites_1.checkSessionManagerPlugin)();
32
+ if (!ssmCheck.installed) {
33
+ if (!noColor) {
34
+ console.error(chalk_1.default.red('Session Manager Plugin is required for tunneling.'));
35
+ console.error(chalk_1.default.gray(`Install: ${ssmCheck.installUrl}`));
36
+ }
37
+ else {
38
+ console.error('Session Manager Plugin is required for tunneling.');
39
+ console.error(`Install: ${ssmCheck.installUrl}`);
40
+ }
41
+ process.exit(1);
42
+ }
43
+ // Validate target type
44
+ const validTargets = ['rds'];
45
+ if (!validTargets.includes(target.toLowerCase())) {
46
+ if (!noColor) {
47
+ console.error(chalk_1.default.red(`Invalid target: ${target}`));
48
+ console.error(chalk_1.default.gray(`Valid targets: ${validTargets.join(', ')}`));
49
+ }
50
+ else {
51
+ console.error(`Invalid target: ${target}`);
52
+ console.error(`Valid targets: ${validTargets.join(', ')}`);
53
+ }
54
+ process.exit(1);
55
+ }
56
+ try {
57
+ switch (target.toLowerCase()) {
58
+ case 'rds':
59
+ await (0, rds_1.startRdsTunnel)(system, options);
60
+ break;
61
+ default:
62
+ throw new Error(`Unsupported target: ${target}`);
63
+ }
64
+ }
65
+ catch (error) {
66
+ const message = error.message;
67
+ if (!noColor) {
68
+ console.error(chalk_1.default.red(`Error: ${message}`));
69
+ }
70
+ else {
71
+ console.error(`Error: ${message}`);
72
+ }
73
+ process.exit(1);
74
+ }
75
+ }
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/tunnel/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Check if a local port is available
3
+ */
4
+ export declare function isPortAvailable(port: number): Promise<boolean>;
5
+ /**
6
+ * Validate port number is in valid range
7
+ */
8
+ export declare function isValidPort(port: number): boolean;
9
+ /**
10
+ * Parse port from string, return undefined if invalid
11
+ */
12
+ export declare function parsePort(value: string): number | undefined;
13
+ //# sourceMappingURL=port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../../src/commands/tunnel/port.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoB9D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM3D"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isPortAvailable = isPortAvailable;
37
+ exports.isValidPort = isValidPort;
38
+ exports.parsePort = parsePort;
39
+ const net = __importStar(require("net"));
40
+ /**
41
+ * Check if a local port is available
42
+ */
43
+ function isPortAvailable(port) {
44
+ return new Promise((resolve) => {
45
+ const server = net.createServer();
46
+ server.once('error', (err) => {
47
+ if (err.code === 'EADDRINUSE') {
48
+ resolve(false);
49
+ }
50
+ else {
51
+ resolve(false);
52
+ }
53
+ });
54
+ server.once('listening', () => {
55
+ server.close(() => {
56
+ resolve(true);
57
+ });
58
+ });
59
+ server.listen(port, '127.0.0.1');
60
+ });
61
+ }
62
+ /**
63
+ * Validate port number is in valid range
64
+ */
65
+ function isValidPort(port) {
66
+ return Number.isInteger(port) && port >= 1 && port <= 65535;
67
+ }
68
+ /**
69
+ * Parse port from string, return undefined if invalid
70
+ */
71
+ function parsePort(value) {
72
+ const port = parseInt(value, 10);
73
+ if (isNaN(port) || !isValidPort(port)) {
74
+ return undefined;
75
+ }
76
+ return port;
77
+ }
78
+ //# sourceMappingURL=port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.js","sourceRoot":"","sources":["../../../src/commands/tunnel/port.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,0CAoBC;AAKD,kCAEC;AAKD,8BAMC;AA3CD,yCAA2B;AAE3B;;GAEG;AACH,SAAgB,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;QAElC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;YAClD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC9B,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;YAC5B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBAChB,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW,CAAC,IAAY;IACtC,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS,CAAC,KAAa;IACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { TunnelOptions } from './types';
2
+ /**
3
+ * Start an RDS tunnel via SSM Session Manager
4
+ */
5
+ export declare function startRdsTunnel(system: string, options: TunnelOptions): Promise<void>;
6
+ //# sourceMappingURL=rds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rds.d.ts","sourceRoot":"","sources":["../../../src/commands/tunnel/rds.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAmB,MAAM,SAAS,CAAC;AAMzD;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CA0Kf"}
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startRdsTunnel = startRdsTunnel;
7
+ const child_process_1 = require("child_process");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const inquirer_1 = __importDefault(require("inquirer"));
10
+ const types_1 = require("./types");
11
+ const aws_1 = require("./aws");
12
+ const port_1 = require("./port");
13
+ const utils_1 = require("../login/utils");
14
+ const config_1 = require("../login/config");
15
+ /**
16
+ * Start an RDS tunnel via SSM Session Manager
17
+ */
18
+ async function startRdsTunnel(system, options) {
19
+ const noColor = options.noColor ?? false;
20
+ // Load config and get profile
21
+ const config = await (0, config_1.loadConfig)();
22
+ const profile = options.profile || config?.defaultProfile || 'development';
23
+ // Get system configuration
24
+ const systemConfig = (0, types_1.getSystemConfig)(system);
25
+ // Step 1: Ask for local port
26
+ let localPort;
27
+ if (options.localPort) {
28
+ const parsed = (0, port_1.parsePort)(options.localPort);
29
+ if (!parsed) {
30
+ throw new Error(`Invalid port: ${options.localPort}`);
31
+ }
32
+ localPort = parsed;
33
+ }
34
+ else {
35
+ const portAnswer = await inquirer_1.default.prompt([
36
+ {
37
+ type: 'input',
38
+ name: 'port',
39
+ message: 'Local RDS port:',
40
+ default: systemConfig.defaultPort.toString(),
41
+ validate: (input) => {
42
+ const p = (0, port_1.parsePort)(input);
43
+ if (!p)
44
+ return 'Please enter a valid port number (1-65535)';
45
+ return true;
46
+ },
47
+ },
48
+ ]);
49
+ localPort = (0, port_1.parsePort)(portAnswer.port);
50
+ }
51
+ // Step 2: Verify port availability
52
+ if (!noColor) {
53
+ console.log(chalk_1.default.gray(`Checking port ${localPort} availability...`));
54
+ }
55
+ const portAvailable = await (0, port_1.isPortAvailable)(localPort);
56
+ if (!portAvailable) {
57
+ throw new Error(`Port ${localPort} is already in use. Please choose a different port.`);
58
+ }
59
+ if (!noColor) {
60
+ console.log(chalk_1.default.green(`✓ Port ${localPort} is available`));
61
+ }
62
+ else {
63
+ console.log(`Port ${localPort} is available`);
64
+ }
65
+ // Step 3: Ask for cluster name
66
+ let clusterName;
67
+ if (options.cluster) {
68
+ clusterName = options.cluster;
69
+ }
70
+ else {
71
+ const clusterAnswer = await inquirer_1.default.prompt([
72
+ {
73
+ type: 'input',
74
+ name: 'cluster',
75
+ message: 'RDS cluster/instance name:',
76
+ default: systemConfig.defaultCluster,
77
+ validate: (input) => {
78
+ if (!input || input.trim().length === 0) {
79
+ return 'Cluster name is required';
80
+ }
81
+ return true;
82
+ },
83
+ },
84
+ ]);
85
+ clusterName = clusterAnswer.cluster.trim();
86
+ }
87
+ // Step 4: Get EC2 instance ID
88
+ if (!noColor) {
89
+ console.log(chalk_1.default.gray(`Looking up ${systemConfig.ec2Tag} instance...`));
90
+ }
91
+ else {
92
+ console.log(`Looking up ${systemConfig.ec2Tag} instance...`);
93
+ }
94
+ const instanceId = (0, aws_1.getInstanceIdByTag)(systemConfig.ec2Tag, profile, options.verbose);
95
+ if (!noColor) {
96
+ console.log(chalk_1.default.green(`✓ Found instance: ${instanceId}`));
97
+ }
98
+ else {
99
+ console.log(`Found instance: ${instanceId}`);
100
+ }
101
+ // Step 5: Get RDS endpoint
102
+ if (!noColor) {
103
+ console.log(chalk_1.default.gray(`Looking up RDS endpoint for ${clusterName}...`));
104
+ }
105
+ else {
106
+ console.log(`Looking up RDS endpoint for ${clusterName}...`);
107
+ }
108
+ const endpoint = (0, aws_1.getRdsEndpoint)(clusterName, profile, options.verbose);
109
+ if (!noColor) {
110
+ console.log(chalk_1.default.green(`✓ Found endpoint: ${endpoint}`));
111
+ }
112
+ else {
113
+ console.log(`Found endpoint: ${endpoint}`);
114
+ }
115
+ // Step 6: Start SSM session
116
+ console.log('');
117
+ if (!noColor) {
118
+ console.log(chalk_1.default.cyan.bold('Starting tunnel...'));
119
+ console.log(chalk_1.default.gray(` System: ${system}`));
120
+ console.log(chalk_1.default.gray(` Profile: ${profile}`));
121
+ console.log(chalk_1.default.gray(` Local port: ${localPort}`));
122
+ console.log(chalk_1.default.gray(` Remote: ${endpoint}:${systemConfig.defaultPort}`));
123
+ console.log(chalk_1.default.gray(` Via: ${instanceId} (${systemConfig.ec2Tag})`));
124
+ }
125
+ else {
126
+ console.log('Starting tunnel...');
127
+ console.log(` System: ${system}`);
128
+ console.log(` Profile: ${profile}`);
129
+ console.log(` Local port: ${localPort}`);
130
+ console.log(` Remote: ${endpoint}:${systemConfig.defaultPort}`);
131
+ console.log(` Via: ${instanceId} (${systemConfig.ec2Tag})`);
132
+ }
133
+ console.log('');
134
+ if (!noColor) {
135
+ console.log(chalk_1.default.yellow('Press Ctrl+C to close the tunnel'));
136
+ }
137
+ else {
138
+ console.log('Press Ctrl+C to close the tunnel');
139
+ }
140
+ console.log('');
141
+ // Start the SSM session
142
+ const child = (0, child_process_1.spawn)('aws', [
143
+ 'ssm',
144
+ 'start-session',
145
+ '--target', instanceId,
146
+ '--document-name', 'AWS-StartPortForwardingSessionToRemoteHost',
147
+ '--parameters', `host="${endpoint}",portNumber="${systemConfig.defaultPort}",localPortNumber="${localPort}"`,
148
+ '--profile', profile,
149
+ ], {
150
+ stdio: 'inherit',
151
+ env: { ...(0, utils_1.getSubprocessEnv)(), AWS_PROFILE: profile },
152
+ });
153
+ // Handle process exit
154
+ child.on('error', (error) => {
155
+ throw new Error(`Failed to start SSM session: ${error.message}`);
156
+ });
157
+ child.on('exit', (code) => {
158
+ if (code !== 0 && code !== null) {
159
+ console.log('');
160
+ if (!noColor) {
161
+ console.log(chalk_1.default.red(`Tunnel closed with exit code: ${code}`));
162
+ }
163
+ else {
164
+ console.log(`Tunnel closed with exit code: ${code}`);
165
+ }
166
+ }
167
+ });
168
+ // Keep process running until SSM session ends
169
+ await new Promise((resolve) => {
170
+ child.on('close', () => {
171
+ resolve();
172
+ });
173
+ });
174
+ }
175
+ //# sourceMappingURL=rds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rds.js","sourceRoot":"","sources":["../../../src/commands/tunnel/rds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Types for the tunnel command
3
+ */
4
+ /**
5
+ * Tunnel target types
6
+ */
7
+ export type TunnelTarget = 'rds' | 'opensearch' | 'ec2';
8
+ /**
9
+ * Options for the tunnel command
10
+ */
11
+ export interface TunnelOptions {
12
+ profile?: string;
13
+ localPort?: string;
14
+ cluster?: string;
15
+ noColor?: boolean;
16
+ verbose?: boolean;
17
+ }
18
+ /**
19
+ * System configuration for tunneling
20
+ */
21
+ export interface SystemConfig {
22
+ /** EC2 Name tag to find bastion/jumpbox */
23
+ ec2Tag: string;
24
+ /** Default RDS cluster name */
25
+ defaultCluster?: string;
26
+ /** Default database port */
27
+ defaultPort: number;
28
+ }
29
+ /**
30
+ * System configurations
31
+ * roma = old world (bastion-host, MySQL)
32
+ * others = new world (jumpbox, typically PostgreSQL)
33
+ */
34
+ export declare const SYSTEM_CONFIGS: Record<string, SystemConfig>;
35
+ /**
36
+ * Default configuration for new/unknown systems
37
+ */
38
+ export declare const DEFAULT_SYSTEM_CONFIG: SystemConfig;
39
+ /**
40
+ * Get system configuration
41
+ */
42
+ export declare function getSystemConfig(system: string): SystemConfig;
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/commands/tunnel/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,YAAY,GAAG,KAAK,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAMvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAGnC,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAE5D"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Types for the tunnel command
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_SYSTEM_CONFIG = exports.SYSTEM_CONFIGS = void 0;
7
+ exports.getSystemConfig = getSystemConfig;
8
+ /**
9
+ * System configurations
10
+ * roma = old world (bastion-host, MySQL)
11
+ * others = new world (jumpbox, typically PostgreSQL)
12
+ */
13
+ exports.SYSTEM_CONFIGS = {
14
+ roma: {
15
+ ec2Tag: 'bastion-host',
16
+ defaultCluster: 'roma-backend',
17
+ defaultPort: 3306, // MySQL
18
+ },
19
+ };
20
+ /**
21
+ * Default configuration for new/unknown systems
22
+ */
23
+ exports.DEFAULT_SYSTEM_CONFIG = {
24
+ ec2Tag: 'jumpbox',
25
+ defaultPort: 5432, // PostgreSQL
26
+ };
27
+ /**
28
+ * Get system configuration
29
+ */
30
+ function getSystemConfig(system) {
31
+ return exports.SYSTEM_CONFIGS[system.toLowerCase()] || exports.DEFAULT_SYSTEM_CONFIG;
32
+ }
33
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/commands/tunnel/types.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAsDH,0CAEC;AA1BD;;;;GAIG;AACU,QAAA,cAAc,GAAiC;IAC1D,IAAI,EAAE;QACJ,MAAM,EAAE,cAAc;QACtB,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,IAAI,EAAE,QAAQ;KAC5B;CACF,CAAC;AAEF;;GAEG;AACU,QAAA,qBAAqB,GAAiB;IACjD,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,IAAI,EAAE,aAAa;CACjC,CAAC;AAEF;;GAEG;AACH,SAAgB,eAAe,CAAC,MAAc;IAC5C,OAAO,sBAAc,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,6BAAqB,CAAC;AACvE,CAAC"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ const init_1 = require("./commands/init");
8
8
  const login_1 = require("./commands/login");
9
9
  const status_1 = require("./commands/status");
10
10
  const profile_1 = require("./commands/profile");
11
+ const tunnel_1 = require("./commands/tunnel");
11
12
  // Read version from package.json
12
13
  const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', 'package.json'), 'utf-8'));
13
14
  const program = new commander_1.Command();
@@ -20,5 +21,6 @@ program.addCommand(init_1.initCommand);
20
21
  program.addCommand(login_1.loginCommand);
21
22
  program.addCommand(status_1.statusCommand);
22
23
  program.addCommand(profile_1.profileCommand);
24
+ program.addCommand(tunnel_1.tunnelCommand);
23
25
  program.parse();
24
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,2BAAkC;AAClC,+BAA4B;AAC5B,0CAA8C;AAC9C,4CAAgD;AAChD,8CAAkD;AAClD,gDAAoD;AAEpD,iCAAiC;AACjC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAC7D,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,2CAA2C,CAAC;KACxD,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAEhC,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AAEnC,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,2BAAkC;AAClC,+BAA4B;AAC5B,0CAA8C;AAC9C,4CAAgD;AAChD,8CAAkD;AAClD,gDAAoD;AACpD,8CAAkD;AAElD,iCAAiC;AACjC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAC5B,IAAA,iBAAY,EAAC,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAC7D,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,2CAA2C,CAAC;KACxD,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAEhC,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,kBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,oBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,wBAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,sBAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@padua/cli",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Unified AWS infrastructure management CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",