sap-cap-debugger
Version: 
NPX tool for remote debugging SAP CAP applications on Cloud Foundry (CAP v7 and earlier)
170 lines • 7.32 kB
JavaScript
"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