import * as path from "path" ; import * as vscode from "vscode" ; import { IDebugProvider , PortInfo , Cancellable } from "./debugProvider" ; import { suggestedShellForContainer } from '../utils/container-shell' ; import * as config from '../components/config/config' ; import { Kubectl } from "../kubectl" ; import { kubeChannel } from "../kubeChannel" ; import { ProcessInfo } from "./debugUtils" ; import { ExecResult } from "../binutilplusplus" ; import { IDockerfile } from "../docker/parser" ; import { Dictionary } from "../utils/dictionary" ; import * as extensionUtils from "../extensionUtils" ; import * as debugUtils from "./debugUtils" ; const debuggerType = 'nodejs' ; export class NodejsDebugProvider implements IDebugProvider { remoteRoot : string | undefined ; shell : string ; public getDebuggerType ( ) { return debuggerType ; } public async isDebuggerInstalled ( ) { return true ; } public async startDebugging ( workspaceFolder , sessionName , port , _pod , _pidToDebug ) { const debugConfiguration = { type : "node" , request : "attach" , name : sessionName , hostName : "localhost" , skipFiles : [ "<node_internals>/**/*.js" ] , port } ; if ( this . remoteRoot ) { debugConfiguration [ 'remoteRoot' ] = this . remoteRoot ; } const currentFolder = ( vscode . workspace . workspaceFolders || [ ] ) . find ( ( folder ) => folder . name === path . basename ( workspaceFolder ) ) ; return await vscode . debug . startDebugging ( currentFolder , debugConfiguration ) ; } isSupportedImage ( ) { return false ; } public async resolvePortsFromFile ( dockerfile , env ) { this . remoteRoot = dockerfile . getWorkDir ( ) ; const possiblePorts = dockerfile . getExposedPorts ( ) ; if ( ! extensionUtils . isNonEmptyArray ( possiblePorts ) ) { return undefined ; } const rawDebugPortInfo = config . getNodejsDebugPort ( ) || 9229 ; const rawAppPortInfo = await debugUtils . promptForAppPort ( possiblePorts , '8080' , env ) ; return { debugPort : Number ( rawDebugPortInfo ) , appPort : Number ( rawAppPortInfo ) } ; } public async resolvePortsFromContainer ( kubectl , pod , podNamespace , container ) { this . shell = await suggestedShellForContainer ( kubectl , pod , podNamespace , container ) ; const inspectModeResult = await this . setNodeInspectMode ( kubectl , pod , podNamespace , container ) ; if ( ! inspectModeResult ) { kubeChannel . showOutput ( 'Unable to set Node.js in the container to inspect mode.' ) ; return undefined ; } this . remoteRoot = await this . tryGetRemoteRoot ( kubectl , pod , podNamespace , container ) ; const rawDebugPortInfo = config . getNodejsDebugPort ( ) || 9229 ; return { debugPort : Number ( rawDebugPortInfo ) } ; } async setNodeInspectMode ( kubectl , pod , podNamespace , container ) { kubeChannel . showOutput ( 'Switching node to debug mode in container' ) ; const nsarg = podNamespace ? `--namespace ${ podNamespace }` : '' ; const containerCommand = container ? `-c ${ container }` : '' ; const execCmd = `exec ${ pod } ${ nsarg } ${ containerCommand } -- ${ this . shell } -c "kill -s USR1 1"` ; const execResult = await kubectl . invokeCommand ( execCmd ) ; return ExecResult . succeeded ( execResult ) ; } async tryGetRemoteRoot ( kubectl , podName , podNamespace , containerName ) { if ( config . getNodejsAutoDetectRemoteRoot ( ) ) { kubeChannel . showOutput ( "Trying to detect remote root." ) ; const nsarg = podNamespace ? `--namespace ${ podNamespace }` : '' ; const containerCommand = containerName ? `-c ${ containerName }` : '' ; const execCmd = `exec ${ podName } ${ nsarg } ${ containerCommand } -- ${ this . shell } -c "readlink /proc/1/cwd"` ; const execResult = await kubectl . invokeCommand ( execCmd ) ; if ( ExecResult . succeeded ( execResult ) ) { const remoteRoot = execResult . stdout . replace ( /(\r\n|\n|\r)/gm , '' ) ; kubeChannel . showOutput ( `Got remote root from container: ${ remoteRoot }` ) ; return remoteRoot ; } } if ( config . getNodejsRemoteRoot ( ) ) { const remoteRoot = config . getNodejsRemoteRoot ( ) ; kubeChannel . showOutput ( `Setting remote root to ${ remoteRoot }` ) ; return remoteRoot ; } return undefined ; } public filterSupportedProcesses ( _processes ) { return undefined ; } public isPortRequired ( ) { return true ; } public async getDebugArgs ( ) { const debugCommand = await vscode . window . showInputBox ( { prompt : 'Command to enable inspector in your container for debugging.' , placeHolder : 'Example: node --inspect app.js' } ) ; if ( ! debugCommand ) { return { cancelled : true } ; } return { cancelled : false , value : `-i --attach=false -- ${ debugCommand }` } ; } }