Skip to content

Commit a9d5851

Browse files
authored
Merge pull request #180 from brendandburns/test
Fix sending protocols for web sockets.
2 parents 7639c00 + 9219683 commit a9d5851

File tree

4 files changed

+54
-12
lines changed

4 files changed

+54
-12
lines changed
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import k8s = require('@kubernetes/client-node');
1+
import * as k8s from '@kubernetes/client-node';
2+
import * as stream from 'stream';
23

34
const command = process.argv[2];
45

@@ -7,4 +8,11 @@ kc.loadFromDefault();
78

89
const exec = new k8s.Exec(kc);
910
exec.exec('default', 'nginx-4217019353-9gl4s', 'nginx', command,
10-
process.stdout, process.stderr, process.stdin, true /* tty */);
11+
process.stdout as stream.Writable, process.stderr as stream.Writable, process.stdin as stream.Readable,
12+
true /* tty */,
13+
(status: k8s.V1Status) => {
14+
// tslint:disable-next-line:no-console
15+
console.log('Exited with status:');
16+
// tslint:disable-next-line:no-console
17+
console.log(JSON.stringify(status, null, 2));
18+
});

src/exec.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import WebSocket = require('isomorphic-ws');
22
import querystring = require('querystring');
33
import stream = require('stream');
44

5+
import { V1Status } from './api';
56
import { KubeConfig } from './config';
67
import { WebSocketHandler, WebSocketInterface } from './web-socket-handler';
78

@@ -17,9 +18,23 @@ export class Exec {
1718
}
1819

1920
// TODO: make command an array and support multiple args
21+
/**
22+
* @param {string} namespace - The namespace of the pod to exec the command inside.
23+
* @param {string} podName - The name of the pod to exec the command inside.
24+
* @param {string} containerName - The name of the container in the pod to exec the command inside.
25+
* @param {string} command - The command to execute.
26+
* @param {stream.Writable} stdout - The stream to write stdout data from the command.
27+
* @param {stream.Writable} stderr - The stream to write stderr data from the command.
28+
* @param {stream.Readable} stdin - The strream to write stdin data into the command.
29+
* @param {boolean} tty - Should the command execute in a TTY enabled session.
30+
* @param {(V1Status) => void} statusCallback -
31+
* A callback to received the status (e.g. exit code) from the command, optional.
32+
* @return {string} This is the result
33+
*/
2034
public async exec(namespace: string, podName: string, containerName: string, command: string,
21-
stdout: stream.Writable | any, stderr: stream.Writable | any, stdin: stream.Readable | any,
22-
tty: boolean): Promise<WebSocket> {
35+
stdout: stream.Writable | null, stderr: stream.Writable | null, stdin: stream.Readable | null,
36+
tty: boolean,
37+
statusCallback?: (status: V1Status) => void): Promise<WebSocket> {
2338
const query = {
2439
stdout: stdout != null,
2540
stderr: stderr != null,
@@ -31,7 +46,13 @@ export class Exec {
3146
const queryStr = querystring.stringify(query);
3247
const path = `/api/v1/namespaces/${namespace}/pods/${podName}/exec?${queryStr}`;
3348
const conn = await this.handler.connect(path, null, (streamNum: number, buff: Buffer): boolean => {
34-
WebSocketHandler.handleStandardStreams(streamNum, buff, stdout, stderr);
49+
const status = WebSocketHandler.handleStandardStreams(streamNum, buff, stdout, stderr);
50+
if (status != null) {
51+
if (statusCallback) {
52+
statusCallback(status);
53+
}
54+
return false;
55+
}
3556
return true;
3657
});
3758
if (stdin != null) {

src/exec_test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import WebSocket = require('isomorphic-ws');
33
import { ReadableStreamBuffer, WritableStreamBuffer } from 'stream-buffers';
44
import { anyFunction, capture, instance, mock, verify, when } from 'ts-mockito';
55

6+
import { V1Status } from './api';
67
import { KubeConfig } from './config';
78
import { Exec } from './exec';
89
import { WebSocketHandler, WebSocketInterface } from './web-socket-handler';
@@ -65,11 +66,15 @@ describe('Exec', () => {
6566
const path = `/api/v1/namespaces/${namespace}/pods/${pod}/exec`;
6667
const args = `stdout=true&stderr=true&stdin=true&tty=false&command=${cmd}&container=${container}`;
6768

69+
let statusOut = {} as V1Status;
70+
6871
const fakeConn: WebSocket = mock(WebSocket);
6972
when(fakeWebSocket.connect(`${path}?${args}`, null, anyFunction())).thenResolve(fakeConn);
7073

7174
await exec.exec(
72-
namespace, pod, container, cmd, osStream, errStream, isStream, false);
75+
namespace, pod, container, cmd, osStream, errStream, isStream, false, (status: V1Status) => {
76+
statusOut = status;
77+
});
7378

7479
const [, , outputFn] = capture(fakeWebSocket.connect).last();
7580

@@ -102,6 +107,14 @@ describe('Exec', () => {
102107
isStream.put(msg);
103108
verify(fakeConn.send(msg));
104109

110+
const statusIn = {
111+
code: 100,
112+
message: 'this is a test',
113+
} as V1Status;
114+
115+
outputFn(WebSocketHandler.StatusStream, Buffer.from(JSON.stringify(statusIn)));
116+
expect(statusOut).to.deep.equal(statusIn);
117+
105118
isStream.stop();
106119
verify(fakeConn.close());
107120
});

src/web-socket-handler.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@ export class WebSocketHandler implements WebSocketInterface {
2727

2828
public static handleStandardStreams(
2929
streamNum: number, buff: Buffer,
30-
stdout: stream.Writable, stderr: stream.Writable,
30+
stdout: stream.Writable | null, stderr: stream.Writable | null,
3131
): V1Status | null {
3232
if (buff.length < 1) {
3333
return null;
3434
}
35-
if (streamNum === WebSocketHandler.StdoutStream) {
35+
if (stdout && streamNum === WebSocketHandler.StdoutStream) {
3636
stdout.write(buff);
37-
} else if (streamNum === WebSocketHandler.StderrStream) {
37+
} else if (stderr && streamNum === WebSocketHandler.StderrStream) {
3838
stderr.write(buff);
3939
} else if (streamNum === WebSocketHandler.StatusStream) {
4040
// stream closing.
41-
if (stdout) {
41+
if (stdout && stdout !== process.stdout) {
4242
stdout.end();
4343
}
44-
if (stderr) {
44+
if (stderr && stderr !== process.stderr) {
4545
stderr.end();
4646
}
4747
return JSON.parse(buff.toString('utf8')) as V1Status;
@@ -109,7 +109,7 @@ export class WebSocketHandler implements WebSocketInterface {
109109
this.config.applytoHTTPSOptions(opts);
110110

111111
return new Promise((resolve, reject) => {
112-
const client = (this.socketFactory ? this.socketFactory(uri, opts) : new WebSocket(uri, opts));
112+
const client = (this.socketFactory ? this.socketFactory(uri, opts) : new WebSocket(uri, protocols, opts));
113113
let resolved = false;
114114

115115
client.onopen = () => {

0 commit comments

Comments
 (0)