UNPKG

sap-cap-debugger

Version:

NPX tool for remote debugging SAP CAP applications on Cloud Foundry (CAP v7 and earlier)

170 lines 7.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CloudFoundryClient = void 0; const command_1 = require("../utils/command"); class CloudFoundryClient { constructor(logger) { this.logger = logger; this.commandExecutor = new command_1.CommandExecutor(logger); } async checkPrerequisites() { this.logger.info('Checking prerequisites...'); const cfExists = await this.commandExecutor.checkCommandExists('cf'); if (!cfExists) { this.logger.error('Cloud Foundry CLI (cf) is not installed or not in PATH'); return false; } const netstatExists = await this.commandExecutor.checkCommandExists('netstat'); if (!netstatExists) { this.logger.error('netstat command is not available'); return false; } this.logger.success('Prerequisites check passed'); return true; } async getApps() { const result = await this.commandExecutor.execute('cf', ['apps']); if (!result.success) { throw new Error(`Failed to get apps: ${result.error}`); } const lines = result.output.split('\n'); const apps = []; for (const line of lines) { if (line.includes('started') || line.includes('stopped')) { const parts = line.trim().split(/\s+/); if (parts.length >= 4) { apps.push({ name: parts[0], status: parts[1], instances: parts[2], memory: parts[3], disk: parts[4] || '', urls: parts.slice(5) || [] }); } } } return apps; } async getAppStatus(appName) { const apps = await this.getApps(); return apps.find(app => app.name === appName) || null; } async startApp(appName) { this.logger.info(`Starting application: ${appName}`); const result = await this.commandExecutor.execute('cf', ['start', appName]); if (result.success) { this.logger.success(`Application ${appName} started successfully`); return true; } else { this.logger.error(`Failed to start application: ${result.error}`); return false; } } async enableSSH(appName) { this.logger.info(`Enabling SSH access for application: ${appName}`); const result = await this.commandExecutor.execute('cf', ['enable-ssh', appName]); if (result.success) { this.logger.success('SSH access enabled'); return true; } else { this.logger.warning('SSH access may already be enabled or failed to enable'); return false; } } async checkSSHEnabled(appName) { this.logger.info(`Checking SSH access for application: ${appName}`); const result = await this.commandExecutor.execute('cf', ['ssh-enabled', appName]); if (result.success) { const output = result.output.toLowerCase(); if (output.includes('ssh is enabled')) { this.logger.success('SSH access is already enabled'); return true; } else if (output.includes('ssh is disabled')) { this.logger.info('SSH access is disabled'); return false; } } // If we can't determine status, assume it's disabled this.logger.warning('Could not determine SSH status, assuming disabled'); return false; } async startAppProcess(appName) { this.logger.info('Starting application process...'); // First, try to detect the correct entry point const entryPoint = await this.detectEntryPoint(appName); const command = `export PATH='/home/vcap/deps/0/bin:$PATH' && cd /home/vcap/app && /home/vcap/deps/0/bin/node ${entryPoint}`; return await this.commandExecutor.execute('cf', ['ssh', appName, '-c', command]); } async detectEntryPoint(appName) { this.logger.info('Detecting application entry point...'); // Check common CAP entry points in order of preference const possibleEntryPoints = [ 'server.js', // Most common for CAP apps 'srv/server.js', // Standard CAP structure 'app/server.js', // Alternative structure 'index.js' // Fallback ]; for (const entryPoint of possibleEntryPoints) { const result = await this.commandExecutor.execute('cf', ['ssh', appName, '-c', `test -f /home/vcap/app/${entryPoint} && echo "exists"`]); if (result.success && result.output.trim() === 'exists') { this.logger.success(`Found entry point: ${entryPoint}`); return entryPoint; } } // If no entry point found, default to server.js and let it fail with a clear error this.logger.warning('Could not detect entry point, defaulting to server.js'); return 'server.js'; } async findNodeProcess(appName) { this.logger.info('Finding Node.js process...'); const maxAttempts = 10; let attempt = 1; while (attempt <= maxAttempts) { const result = await this.commandExecutor.execute('cf', ['ssh', appName, '-c', 'ps aux | grep node | grep -v grep']); if (result.success && result.output.trim()) { const lines = result.output.trim().split('\n'); for (const line of lines) { const parts = line.trim().split(/\s+/); if (parts.length >= 11 && parts[10] && parts[10].includes('node')) { const pid = parseInt(parts[1]); if (!isNaN(pid)) { this.logger.success(`Found Node.js process with PID: ${pid}`); return { pid, name: parts[10], command: line }; } } } } this.logger.info(`Waiting for Node.js process... (attempt ${attempt}/${maxAttempts})`); await new Promise(resolve => setTimeout(resolve, 3000)); attempt++; } this.logger.error('Could not find Node.js process after maximum attempts'); return null; } async enableDebugging(appName, pid) { this.logger.info(`Enabling debugging on process ${pid}...`); const result = await this.commandExecutor.execute('cf', ['ssh', appName, '-c', `kill -USR1 ${pid}`]); if (result.success) { this.logger.success(`Debugging enabled on process ${pid}`); return true; } else { this.logger.error('Failed to enable debugging'); return false; } } async getAppLogs(appName) { const result = await this.commandExecutor.execute('cf', ['logs', appName, '--recent']); return result.output; } } exports.CloudFoundryClient = CloudFoundryClient; //# sourceMappingURL=cloudfoundry.js.map