"src/targets/gpu/vscode:/vscode.git/clone" did not exist on "9b929d4e6a6abec8e46d0e56fed9ddf2160957ed"
sshClientUtility.ts 6.15 KB
Newer Older
liuzhe-lz's avatar
liuzhe-lz committed
1
2
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
Deshui Yu's avatar
Deshui Yu committed
3
4
5

'use strict';

6
7
import * as assert from 'assert';
import * as os from 'os';
8
import * as path from 'path';
Deshui Yu's avatar
Deshui Yu committed
9
import { Client, ClientChannel, SFTPWrapper } from 'ssh2';
10
import * as stream from 'stream';
Deshui Yu's avatar
Deshui Yu committed
11
12
import { Deferred } from 'ts-deferred';
import { NNIError, NNIErrorNames } from '../../common/errors';
chicm-ms's avatar
chicm-ms committed
13
import { getLogger, Logger } from '../../common/log';
14
import { getRemoteTmpDir, uniqueString, unixPathJoin } from '../../common/utils';
15
import { execRemove, tarAdd } from '../common/util';
16
import { RemoteCommandResult } from './remoteMachineData';
Deshui Yu's avatar
Deshui Yu committed
17
18
19
20
21
22
23
24
25
26
27
28
29

/**
 *
 * Utility for frequent operations towards SSH client
 *
 */
export namespace SSHClientUtility {
    /**
     * Copy local file to remote path
     * @param localFilePath the path of local file
     * @param remoteFilePath the target path in remote machine
     * @param sshClient SSH Client
     */
chicm-ms's avatar
chicm-ms committed
30
    export function copyFileToRemote(localFilePath: string, remoteFilePath: string, sshClient: Client): Promise<boolean> {
chicm-ms's avatar
chicm-ms committed
31
32
        const log: Logger = getLogger();
        log.debug(`copyFileToRemote: localFilePath: ${localFilePath}, remoteFilePath: ${remoteFilePath}`);
33
34
        assert(sshClient !== undefined);
        const deferred: Deferred<boolean> = new Deferred<boolean>();
chicm-ms's avatar
chicm-ms committed
35
        sshClient.sftp((err: Error, sftp: SFTPWrapper) => {
36
            if (err !== undefined && err !== null) {
chicm-ms's avatar
chicm-ms committed
37
                log.error(`copyFileToRemote: ${err.message}, ${localFilePath}, ${remoteFilePath}`);
38
39
40
                deferred.reject(err);

                return;
Deshui Yu's avatar
Deshui Yu committed
41
            }
42
            assert(sftp !== undefined);
chicm-ms's avatar
chicm-ms committed
43
            sftp.fastPut(localFilePath, remoteFilePath, (fastPutErr: Error) => {
Deshui Yu's avatar
Deshui Yu committed
44
                sftp.end();
45
                if (fastPutErr !== undefined && fastPutErr !== null) {
46
                    deferred.reject(fastPutErr);
Deshui Yu's avatar
Deshui Yu committed
47
                } else {
48
                    deferred.resolve(true);
Deshui Yu's avatar
Deshui Yu committed
49
50
51
52
53
54
55
56
57
58
59
60
                }
            });
        });

        return deferred.promise;
    }

    /**
     * Execute command on remote machine
     * @param command the command to execute remotely
     * @param client SSH Client
     */
61
    // tslint:disable:no-unsafe-any no-any
chicm-ms's avatar
chicm-ms committed
62
    export function remoteExeCommand(command: string, client: Client): Promise<RemoteCommandResult> {
chicm-ms's avatar
chicm-ms committed
63
64
        const log: Logger = getLogger();
        log.debug(`remoteExeCommand: command: [${command}]`);
chicm-ms's avatar
chicm-ms committed
65
        const deferred: Deferred<RemoteCommandResult> = new Deferred<RemoteCommandResult>();
Deshui Yu's avatar
Deshui Yu committed
66
67
        let stdout: string = '';
        let stderr: string = '';
chicm-ms's avatar
chicm-ms committed
68
        let exitCode: number;
Deshui Yu's avatar
Deshui Yu committed
69

chicm-ms's avatar
chicm-ms committed
70
        client.exec(command, (err: Error, channel: ClientChannel) => {
71
            if (err !== undefined && err !== null) {
chicm-ms's avatar
chicm-ms committed
72
                log.error(`remoteExeCommand: ${err.message}`);
Deshui Yu's avatar
Deshui Yu committed
73
                deferred.reject(err);
74
75

                return;
Deshui Yu's avatar
Deshui Yu committed
76
77
            }

chicm-ms's avatar
chicm-ms committed
78
            channel.on('data', (data: any, dataStderr: any) => {
79
                if (dataStderr !== undefined && dataStderr !== null) {
Deshui Yu's avatar
Deshui Yu committed
80
                    stderr += data.toString();
81
                } else {
Deshui Yu's avatar
Deshui Yu committed
82
83
                    stdout += data.toString();
                }
84
            })
chicm-ms's avatar
chicm-ms committed
85
              .on('exit', (code: any, signal: any) => {
86
                exitCode = <number>code;
Deshui Yu's avatar
Deshui Yu committed
87
88
89
90
91
92
93
94
95
96
97
                deferred.resolve({
                    stdout : stdout,
                    stderr : stderr,
                    exitCode : exitCode
                });
            });
        });

        return deferred.promise;
    }

chicm-ms's avatar
chicm-ms committed
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    /**
     * Copy files and directories in local directory recursively to remote directory
     * @param localDirectory local diretory
     * @param remoteDirectory remote directory
     * @param sshClient SSH client
     */
    export async function copyDirectoryToRemote(localDirectory: string, remoteDirectory: string, sshClient: Client, remoteOS: string): Promise<void> {
        const deferred: Deferred<void> = new Deferred<void>();
        const tmpTarName: string = `${uniqueString(10)}.tar.gz`;
        const localTarPath: string = path.join(os.tmpdir(), tmpTarName);
        const remoteTarPath: string = unixPathJoin(getRemoteTmpDir(remoteOS), tmpTarName);

        // Compress files in local directory to experiment root directory
        await tarAdd(localTarPath, localDirectory);
        // Copy the compressed file to remoteDirectory and delete it
        await copyFileToRemote(localTarPath, remoteTarPath, sshClient);
        await execRemove(localTarPath);
        // Decompress the remote compressed file in and delete it
        await remoteExeCommand(`tar -oxzf ${remoteTarPath} -C ${remoteDirectory}`, sshClient);
        await remoteExeCommand(`rm ${remoteTarPath}`, sshClient);
        deferred.resolve();

        return deferred.promise;
    }

Deshui Yu's avatar
Deshui Yu committed
123
124
    export function getRemoteFileContent(filePath: string, sshClient: Client): Promise<string> {
        const deferred: Deferred<string> = new Deferred<string>();
chicm-ms's avatar
chicm-ms committed
125
        sshClient.sftp((err: Error, sftp: SFTPWrapper) => {
126
127
128
            if (err !== undefined && err !== null) {
                getLogger()
                  .error(`getRemoteFileContent: ${err.message}`);
Deshui Yu's avatar
Deshui Yu committed
129
                deferred.reject(new Error(`SFTP error: ${err.message}`));
130
131

                return;
Deshui Yu's avatar
Deshui Yu committed
132
133
            }
            try {
chicm-ms's avatar
chicm-ms committed
134
                const sftpStream: stream.Readable = sftp.createReadStream(filePath);
Deshui Yu's avatar
Deshui Yu committed
135
136

                let dataBuffer: string = '';
chicm-ms's avatar
chicm-ms committed
137
                sftpStream.on('data', (data: Buffer | string) => {
Deshui Yu's avatar
Deshui Yu committed
138
                    dataBuffer += data;
139
140
                })
                  .on('error', (streamErr: Error) => {
141
                    sftp.end();
Deshui Yu's avatar
Deshui Yu committed
142
                    deferred.reject(new NNIError(NNIErrorNames.NOT_FOUND, streamErr.message));
143
144
                })
                  .on('end', () => {
145
146
                    // sftp connection need to be released manually once operation is done
                    sftp.end();
Deshui Yu's avatar
Deshui Yu committed
147
148
149
                    deferred.resolve(dataBuffer);
                });
            } catch (error) {
150
151
                getLogger()
                  .error(`getRemoteFileContent: ${error.message}`);
152
                sftp.end();
Deshui Yu's avatar
Deshui Yu committed
153
154
155
156
157
158
                deferred.reject(new Error(`SFTP error: ${error.message}`));
            }
        });

        return deferred.promise;
    }
159
    // tslint:enable:no-unsafe-any no-any
Deshui Yu's avatar
Deshui Yu committed
160
}