Skip to content

Commit cbe7588

Browse files
committed
Add unit tests for watch.
1 parent 77e0594 commit cbe7588

File tree

5 files changed

+172
-16
lines changed

5 files changed

+172
-16
lines changed

node-client/src/config.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,18 @@ export class KubeConfig {
6767
}
6868

6969
public getContextObject(name: string) {
70+
if (!this.contexts) {
71+
return null;
72+
}
7073
return KubeConfig.findObject(this.contexts, name, 'context');
7174
}
7275

73-
public getCurrentCluster() {
74-
return this.getCluster(this.getCurrentContextObject().cluster);
76+
public getCurrentCluster(): Cluster | null {
77+
const context = this.getCurrentContextObject();
78+
if (!context) {
79+
return null;
80+
}
81+
return this.getCluster(context.cluster);
7582
}
7683

7784
public getCluster(name: string): Cluster {
@@ -106,7 +113,7 @@ export class KubeConfig {
106113

107114
this.applyOptions(opts);
108115

109-
if (cluster.skipTLSVerify) {
116+
if (cluster && cluster.skipTLSVerify) {
110117
opts.strictSSL = false;
111118
}
112119

@@ -210,7 +217,11 @@ export class KubeConfig {
210217
}
211218

212219
public makeApiClient<T extends ApiType>(apiClientType: ApiConstructor<T>) {
213-
const apiClient = new apiClientType(this.getCurrentCluster().server);
220+
const cluster = this.getCurrentCluster();
221+
if (!cluster) {
222+
throw new Error('No active cluster!');
223+
}
224+
const apiClient = new apiClientType(cluster.server);
214225
apiClient.setDefaultAuthentication(this);
215226

216227
return apiClient;
@@ -234,7 +245,7 @@ export class KubeConfig {
234245
const cluster = this.getCurrentCluster();
235246
const user = this.getCurrentUser();
236247

237-
const ca = this.bufferFromFileOrString(cluster.caFile, cluster.caData);
248+
const ca = cluster != null ? this.bufferFromFileOrString(cluster.caFile, cluster.caData) : null;
238249
if (ca) {
239250
opts.ca = ca;
240251
}
@@ -345,7 +356,12 @@ export class Config {
345356
const kc = new KubeConfig();
346357
kc.loadFromCluster();
347358

348-
const k8sApi = new apiClientType(kc.getCurrentCluster().server);
359+
const cluster = kc.getCurrentCluster();
360+
if (!cluster) {
361+
throw new Error('No active cluster!');
362+
}
363+
364+
const k8sApi = new apiClientType(cluster.server);
349365
k8sApi.setDefaultAuthentication(kc);
350366

351367
return k8sApi;

node-client/src/config_test.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,13 @@ describe('KubeConfig', () => {
611611
delete process.env.KUBERNETES_SERVICE_HOST;
612612
delete process.env.KUBERNETES_SERVICE_PORT;
613613

614-
expect(kc.getCurrentCluster().caFile).to.equal('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt');
615-
expect(kc.getCurrentCluster().server).to.equal('https://kubernetes:443');
614+
const cluster = kc.getCurrentCluster();
615+
expect(cluster).to.not.be.null;
616+
if (!cluster) {
617+
return;
618+
}
619+
expect(cluster.caFile).to.equal('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt');
620+
expect(cluster.server).to.equal('https://kubernetes:443');
616621
expect(kc.getCurrentUser().token).to.equal(token);
617622
});
618623

@@ -634,7 +639,12 @@ describe('KubeConfig', () => {
634639
delete process.env.KUBERNETES_SERVICE_HOST;
635640
delete process.env.KUBERNETES_SERVICE_PORT;
636641

637-
expect(kc.getCurrentCluster().server).to.equal('http://kubernetes:80');
642+
const cluster = kc.getCurrentCluster();
643+
expect(cluster).to.not.be.null;
644+
if (!cluster) {
645+
return;
646+
}
647+
expect(cluster.server).to.equal('http://kubernetes:80');
638648
});
639649

640650
it('should default to localhost', () => {
@@ -644,8 +654,13 @@ describe('KubeConfig', () => {
644654
kc.loadFromDefault();
645655
process.env.HOME = currentHome;
646656

657+
const cluster = kc.getCurrentCluster();
658+
expect(cluster).to.not.be.null;
659+
if (!cluster) {
660+
return;
661+
}
647662
expect(kc.getCurrentUser().name).to.equal('user');
648-
expect(kc.getCurrentCluster().server).to.equal('http://localhost:8080');
663+
expect(cluster.server).to.equal('http://localhost:8080');
649664
});
650665
});
651666

node-client/src/watch.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,37 @@ export interface WatchUpdate {
77
object: object;
88
}
99

10+
export interface RequestInterface {
11+
webRequest(opts: request.Options, callback: (err, response, body) => void): any;
12+
}
13+
14+
export class DefaultRequest implements RequestInterface {
15+
public webRequest(opts: request.Options, callback: (err, response, body) => void): any {
16+
return request(opts, callback);
17+
}
18+
}
19+
1020
export class Watch {
11-
public 'config': KubeConfig;
21+
public config: KubeConfig;
22+
private readonly requestImpl: RequestInterface;
1223

13-
public constructor(config: KubeConfig) {
24+
public constructor(config: KubeConfig, requestImpl?: RequestInterface) {
1425
this.config = config;
26+
if (requestImpl) {
27+
this.requestImpl = requestImpl;
28+
} else {
29+
this.requestImpl = new DefaultRequest();
30+
}
1531
}
1632

1733
public watch(path: string, queryParams: any,
1834
callback: (phase: string, obj: any) => void,
1935
done: (err: any) => void): any {
20-
const url = this.config.getCurrentCluster().server + path;
36+
const cluster = this.config.getCurrentCluster();
37+
if (!cluster) {
38+
throw new Error('No currently active cluster');
39+
}
40+
const url = cluster.server + path;
2141

2242
queryParams.watch = true;
2343
const headerParams: any = {};
@@ -47,11 +67,12 @@ export class Watch {
4767
}
4868
});
4969

50-
const req = request(requestOptions, (error, response, body) => {
70+
const req = this.requestImpl.webRequest(requestOptions, (error, response, body) => {
5171
if (error) {
5272
done(error);
73+
} else {
74+
done(null);
5375
}
54-
done(null);
5576
});
5677
req.pipe(stream);
5778
return req;

node-client/src/watch_test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { expect } from 'chai';
2+
import request = require('request');
3+
import { ReadableStreamBuffer, WritableStreamBuffer } from 'stream-buffers';
4+
import { anyFunction, anything, capture, instance, mock, reset, verify, when } from 'ts-mockito';
5+
6+
import { KubeConfig } from './config';
7+
import { Cluster, Context, User } from './config_types';
8+
import { DefaultRequest, Watch } from './watch';
9+
10+
describe('Watch', () => {
11+
it('should construct correctly', () => {
12+
const kc = new KubeConfig();
13+
const watch = new Watch(kc);
14+
});
15+
16+
it('should watch correctly', () => {
17+
const kc = new KubeConfig();
18+
const server = 'foo.company.com';
19+
kc.clusters = [
20+
{
21+
name: 'cluster',
22+
server,
23+
} as Cluster,
24+
] as Cluster[];
25+
kc.contexts = [
26+
{
27+
cluster: 'cluster',
28+
user: 'user',
29+
} as Context,
30+
] as Context[];
31+
kc.users = [
32+
{
33+
name: 'user',
34+
} as User,
35+
];
36+
const fakeRequestor = mock(DefaultRequest);
37+
const watch = new Watch(kc, instance(fakeRequestor));
38+
39+
const obj1 = {
40+
type: 'ADDED',
41+
object: {
42+
foo: 'bar',
43+
},
44+
};
45+
46+
const obj2 = {
47+
type: 'MODIFIED',
48+
object: {
49+
baz: 'blah',
50+
},
51+
};
52+
53+
const fakeRequest = {
54+
pipe: (stream) => {
55+
stream.write(JSON.stringify(obj1) + '\n');
56+
stream.write(JSON.stringify(obj2) + '\n');
57+
},
58+
};
59+
60+
when(fakeRequestor.webRequest(anything(), anyFunction())).thenReturn(fakeRequest);
61+
62+
const path = '/some/path/to/object';
63+
64+
const receivedTypes: string[] = [];
65+
const receivedObjects: string[] = [];
66+
let doneCalled = false;
67+
let doneErr: any;
68+
69+
watch.watch(path, {}, (phase: string, obj: string) => {
70+
receivedTypes.push(phase);
71+
receivedObjects.push(obj);
72+
}, (err: any) => {
73+
doneCalled = true;
74+
doneErr = err;
75+
});
76+
77+
verify(fakeRequestor.webRequest(anything(), anyFunction()));
78+
79+
const [opts, doneCallback] = capture(fakeRequestor.webRequest).last();
80+
const reqOpts: request.OptionsWithUri = opts as request.OptionsWithUri;
81+
82+
expect(reqOpts.uri).to.equal(`${server}${path}`);
83+
expect(reqOpts.method).to.equal('GET');
84+
expect(reqOpts.json).to.equal(true);
85+
86+
expect(receivedTypes).to.deep.equal([obj1.type, obj2.type]);
87+
expect(receivedObjects).to.deep.equal([obj1.object, obj2.object]);
88+
89+
expect(doneCalled).to.equal(false);
90+
91+
doneCallback(null, null, null);
92+
93+
expect(doneCalled).to.equal(true);
94+
expect(doneErr).to.equal(null);
95+
96+
const errIn = {error: 'err'};
97+
doneCallback(errIn, null, null);
98+
expect(doneErr).to.deep.equal(errIn);
99+
});
100+
});

node-client/src/web-socket-handler.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@ export class WebSocketHandler implements WebSocketInterface {
8686
textHandler: ((text: string) => boolean) | null,
8787
binaryHandler: ((stream: number, buff: Buffer) => boolean) | null): Promise<ws.connection> {
8888

89-
const server = this.config.getCurrentCluster().server;
89+
const cluster = this.config.getCurrentCluster();
90+
if (!cluster) {
91+
throw new Error('No cluster is defined.');
92+
}
93+
const server = cluster.server;
9094
const ssl = server.startsWith('https://');
9195
const target = ssl ? server.substr(8) : server.substr(7);
9296
const proto = ssl ? 'wss' : 'ws';

0 commit comments

Comments
 (0)