From ea6f3d15797df1c74d97c53d8973aafc597ddf2d Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Mon, 5 May 2025 21:17:36 +0530 Subject: [PATCH] Cloudshell: Few Enhancement (#2128) * few enhancement * fix time --- .../CloudShellTab/CloudShellTerminalCore.tsx | 51 +++++++++---------- .../ShellTypes/AbstractShellHandler.tsx | 26 ++++++++++ .../ShellTypes/MongoShellHandler.test.tsx | 2 +- .../ShellTypes/MongoShellHandler.tsx | 11 +--- .../VCoreMongoShellHandler.test.tsx | 2 +- .../ShellTypes/VCoreMongoShellHandler.tsx | 24 +-------- .../Tabs/CloudShellTab/Utils/AttachAddOn.tsx | 26 ++++++++-- 7 files changed, 76 insertions(+), 66 deletions(-) diff --git a/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx b/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx index da692cc9b..8dff70d08 100644 --- a/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx +++ b/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalCore.tsx @@ -55,20 +55,16 @@ export const startCloudShellTerminal = async (terminal: Terminal, shellType: Ter TelemetryProcessor.trace( Action.CloudShellUserConsent, consentGranted ? ActionModifiers.Success : ActionModifiers.Cancel, - { dataExplorerArea: Areas.CloudShell }, + { + dataExplorerArea: Areas.CloudShell, + shellType: TerminalKind[shellType], + isConsent: consentGranted, + region: resolvedRegion, + }, + startKey, ); if (!consentGranted) { - TelemetryProcessor.traceCancel( - Action.CloudShellTerminalSession, - { - shellType: TerminalKind[shellType], - dataExplorerArea: Areas.CloudShell, - region: resolvedRegion, - isConsent: false, - }, - startKey, - ); terminal.writeln( formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."), ); @@ -262,28 +258,27 @@ export const configureSocketConnection = async ( }; export const sendTerminalStartupCommands = (socket: WebSocket, initCommands: string): void => { + // ensures connections don't remain open indefinitely by implementing an automatic timeout after 120 minutes. + const keepSocketAlive = (socket: WebSocket) => { + if (socket.readyState === WebSocket.OPEN) { + if (pingCount >= MAX_PING_COUNT) { + socket.close(); + } else { + pingCount++; + // The code uses a recursive setTimeout pattern rather than setInterval, + // which ensures each new ping only happens after the previous one completes + // and naturally stops if the socket closes. + keepAliveID = setTimeout(() => keepSocketAlive(socket), 1000); + } + } + }; + if (socket && socket.readyState === WebSocket.OPEN) { socket.send(initCommands); + keepSocketAlive(socket); } else { socket.onopen = () => { socket.send(initCommands); - - // ensures connections don't remain open indefinitely by implementing an automatic timeout after 20 minutes. - const keepSocketAlive = (socket: WebSocket) => { - if (socket.readyState === WebSocket.OPEN) { - if (pingCount >= MAX_PING_COUNT) { - socket.close(); - } else { - socket.send(""); - pingCount++; - // The code uses a recursive setTimeout pattern rather than setInterval, - // which ensures each new ping only happens after the previous one completes - // and naturally stops if the socket closes. - keepAliveID = setTimeout(() => keepSocketAlive(socket), 1000); - } - } - }; - keepSocketAlive(socket); }; } diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx index f72dda8f6..7d09000ae 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler.tsx @@ -56,4 +56,30 @@ export abstract class AbstractShellHandler { return allCommands.join("\n").concat("\n"); } + + /** + * Setup commands for MongoDB shell: + * + * 1. Check if mongosh is already installed + * 2. Download mongosh package if not installed + * 3. Extract the package to access mongosh binaries + * 4. Move extracted files to ~/mongosh directory + * 5. Add mongosh binary path to system PATH + * 6. Apply PATH changes by sourcing .bashrc + * + * Each command runs conditionally only if mongosh + * is not already present in the environment. + */ + protected mongoShellSetupCommands(): string[] { + const PACKAGE_VERSION: string = "2.5.0"; + return [ + "if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi", + `if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, + `if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, + `if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh/bin && mv mongosh-${PACKAGE_VERSION}-linux-x64/bin/mongosh ~/mongosh/bin/ && chmod +x ~/mongosh/bin/mongosh; fi`, + `if ! command -v mongosh &> /dev/null; then rm -rf mongosh-${PACKAGE_VERSION}-linux-x64 mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, + "if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi", + "source ~/.bashrc", + ]; + } } diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx index b320a9b68..ba6168f96 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.test.tsx @@ -68,7 +68,7 @@ describe("MongoShellHandler", () => { const commands = mongoShellHandler.getSetUpCommands(); expect(Array.isArray(commands)).toBe(true); - expect(commands.length).toBe(6); + expect(commands.length).toBe(7); expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz"); }); }); diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx index fce6be513..9da24717c 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/MongoShellHandler.tsx @@ -2,8 +2,6 @@ import { userContext } from "../../../../UserContext"; import { getHostFromUrl } from "../Utils/CommonUtils"; import { AbstractShellHandler } from "./AbstractShellHandler"; -const PACKAGE_VERSION: string = "2.5.0"; - export class MongoShellHandler extends AbstractShellHandler { private _key: string; private _endpoint: string | undefined; @@ -18,14 +16,7 @@ export class MongoShellHandler extends AbstractShellHandler { } public getSetUpCommands(): string[] { - return [ - "if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi", - `if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, - `if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, - `if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh && mv mongosh-${PACKAGE_VERSION}-linux-x64/* ~/mongosh/; fi`, - "if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi", - "source ~/.bashrc", - ]; + return this.mongoShellSetupCommands(); } public getConnectionCommand(): string { diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.test.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.test.tsx index 7c0baed84..a5d4e1336 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.test.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.test.tsx @@ -44,7 +44,7 @@ describe("VCoreMongoShellHandler", () => { const commands = vcoreMongoShellHandler.getSetUpCommands(); expect(Array.isArray(commands)).toBe(true); - expect(commands.length).toBe(6); + expect(commands.length).toBe(7); expect(commands[1]).toContain("mongosh-2.5.0-linux-x64.tgz"); expect(commands[0]).toContain("mongosh not found"); }); diff --git a/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx b/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx index 8a3a38610..3c6c859a8 100644 --- a/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx +++ b/src/Explorer/Tabs/CloudShellTab/ShellTypes/VCoreMongoShellHandler.tsx @@ -1,8 +1,6 @@ import { userContext } from "../../../../UserContext"; import { AbstractShellHandler } from "./AbstractShellHandler"; -const PACKAGE_VERSION: string = "2.5.0"; - export class VCoreMongoShellHandler extends AbstractShellHandler { private _endpoint: string | undefined; @@ -15,28 +13,8 @@ export class VCoreMongoShellHandler extends AbstractShellHandler { return "MongoDB VCore"; } - /** - * Setup commands for MongoDB VCore shell: - * - * 1. Check if mongosh is already installed - * 2. Download mongosh package if not installed - * 3. Extract the package to access mongosh binaries - * 4. Move extracted files to ~/mongosh directory - * 5. Add mongosh binary path to system PATH - * 6. Apply PATH changes by sourcing .bashrc - * - * Each command runs conditionally only if mongosh - * is not already present in the environment. - */ public getSetUpCommands(): string[] { - return [ - "if ! command -v mongosh &> /dev/null; then echo '⚠️ mongosh not found. Installing...'; fi", - `if ! command -v mongosh &> /dev/null; then curl -LO https://downloads.mongodb.com/compass/mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, - `if ! command -v mongosh &> /dev/null; then tar -xvzf mongosh-${PACKAGE_VERSION}-linux-x64.tgz; fi`, - `if ! command -v mongosh &> /dev/null; then mkdir -p ~/mongosh && mv mongosh-${PACKAGE_VERSION}-linux-x64/* ~/mongosh/; fi`, - "if ! command -v mongosh &> /dev/null; then echo 'export PATH=$HOME/mongosh/bin:$PATH' >> ~/.bashrc; fi", - "source ~/.bashrc", - ]; + return this.mongoShellSetupCommands(); } public getConnectionCommand(): string { diff --git a/src/Explorer/Tabs/CloudShellTab/Utils/AttachAddOn.tsx b/src/Explorer/Tabs/CloudShellTab/Utils/AttachAddOn.tsx index 9fc011c28..75a19c1aa 100644 --- a/src/Explorer/Tabs/CloudShellTab/Utils/AttachAddOn.tsx +++ b/src/Explorer/Tabs/CloudShellTab/Utils/AttachAddOn.tsx @@ -1,5 +1,6 @@ -import { AbstractShellHandler } from "Explorer/Tabs/CloudShellTab/ShellTypes/AbstractShellHandler"; import { IDisposable, ITerminalAddon, Terminal } from "@xterm/xterm"; +import { AbstractShellHandler } from "../ShellTypes/AbstractShellHandler"; +import { formatErrorMessage } from "./TerminalLogFormats"; interface IAttachOptions { bidirectional?: boolean; @@ -56,8 +57,27 @@ export class AttachAddon implements ITerminalAddon { this._disposables.push(terminal.onBinary((data) => this._sendBinary(data))); } - this._disposables.push(addSocketListener(this._socket, "close", () => this.dispose())); - this._disposables.push(addSocketListener(this._socket, "error", () => this.dispose())); + this._disposables.push(addSocketListener(this._socket, "close", () => this._handleSocketClose(terminal))); + this._disposables.push(addSocketListener(this._socket, "error", () => this._handleSocketClose(terminal))); + } + + /** + * Handles socket close events by terminating processes and showing a message + */ + private _handleSocketClose(terminal: Terminal): void { + if (terminal) { + terminal.writeln( + formatErrorMessage("Session ended. Please close this tab and initiate a new shell session if needed."), + ); + + // Send exit command to terminal + if (this._bidirectional) { + terminal.write(formatErrorMessage("exit\r\n")); + } + } + + // Clean up resources + this.dispose(); } /**