Skip to content

Commit 6dd1af8

Browse files
committed
add node metrics list
1 parent 1728757 commit 6dd1af8

File tree

2 files changed

+156
-61
lines changed

2 files changed

+156
-61
lines changed

src/metrics.ts

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import * as request from 'request';
2+
23
import { KubeConfig } from './config';
3-
import { HttpError, ObjectSerializer, Authentication } from './gen/api';
4+
import { HttpError, ObjectSerializer } from './gen/api';
5+
6+
export interface Usage {
7+
cpu: string;
8+
memory: string;
9+
}
410

511
export interface ContainerMetric {
6-
name: string,
7-
usage:{
8-
cpu: string,
9-
memory: string
10-
}
12+
name: string;
13+
usage: Usage;
1114
}
1215

1316
export interface PodMetric {
@@ -16,19 +19,39 @@ export interface PodMetric {
1619
namespace: string;
1720
selfLink: string;
1821
creationTimestamp: string;
19-
}
20-
timestamp: string,
21-
window: string,
22-
containers: Array<ContainerMetric>
22+
};
23+
timestamp: string;
24+
window: string;
25+
containers: ContainerMetric[];
26+
}
27+
28+
export interface NodeMetric {
29+
metadata: {
30+
name: string;
31+
selfLink: string;
32+
creationTimestamp: string;
33+
};
34+
timestamp: string;
35+
window: string;
36+
usage: Usage;
2337
}
2438

2539
export interface PodMetricsList {
2640
kind: 'PodMetricsList';
2741
apiVersion: 'metrics.k8s.io/v1beta1';
2842
metadata: {
29-
selfLink: string
43+
selfLink: string;
44+
};
45+
items: PodMetric[];
46+
}
47+
48+
export interface NodeMetricsList {
49+
kind: 'NodeMetricsList';
50+
apiVersion: 'metrics.k8s.io/v1beta1';
51+
metadata: {
52+
selfLink: string;
3053
};
31-
items: Array<PodMetric>
54+
items: NodeMetric[];
3255
}
3356

3457
export class Metrics {
@@ -38,16 +61,30 @@ export class Metrics {
3861
this.config = config;
3962
}
4063

41-
// TODO getNodeMetrics
42-
43-
public async getPodMetrics(namespace?: string): Promise<PodMetricsList> {
64+
public async getNodeMetrics(): Promise<NodeMetricsList> {
65+
const path = '/apis/metrics.k8s.io/v1beta1/nodes';
4466

45-
let path:string;
67+
const cluster = this.config.getCurrentCluster();
68+
if (!cluster) {
69+
throw new Error('No currently active cluster');
70+
}
4671

47-
if (namespace !== undefined && namespace.length > 0){
48-
path = `/apis/metrics.k8s.io/v1beta1/${namespace}/default/pods`;
72+
const requestOptions: request.Options = {
73+
method: 'GET',
74+
uri: cluster.server + path,
75+
};
76+
await this.config.applyToRequest(requestOptions);
77+
78+
return this.handleResponse<NodeMetricsList>(requestOptions, JSON.parse);
79+
}
80+
81+
public async getPodMetrics(namespace?: string): Promise<PodMetricsList> {
82+
let path: string;
83+
84+
if (namespace !== undefined && namespace.length > 0) {
85+
path = `/apis/metrics.k8s.io/v1beta1/${namespace}/default/pods`;
4986
} else {
50-
path = "/apis/metrics.k8s.io/v1beta1/pods";
87+
path = '/apis/metrics.k8s.io/v1beta1/pods';
5188
}
5289

5390
const cluster = this.config.getCurrentCluster();
@@ -60,7 +97,11 @@ export class Metrics {
6097
uri: cluster.server + path,
6198
};
6299
await this.config.applyToRequest(requestOptions);
63-
100+
101+
return this.handleResponse<PodMetricsList>(requestOptions, JSON.parse);
102+
}
103+
104+
private handleResponse<T>(requestOptions: request.Options, deserialize: (body: any) => T): Promise<T> {
64105
return new Promise((resolve, reject) => {
65106
const req = request(requestOptions, (error, response, body) => {
66107
if (error) {
@@ -73,11 +114,9 @@ export class Metrics {
73114
reject(new HttpError(response, body, response.statusCode));
74115
}
75116
} else {
76-
const result: PodMetricsList = JSON.parse(body)
77-
resolve(result);
117+
resolve(deserialize(body));
78118
}
79119
});
80120
});
81-
82121
}
83122
}

src/metrics_test.ts

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { expect } from 'chai';
33
import * as nock from 'nock';
44
import { KubeConfig } from './config';
55
import { V1Status, HttpError } from './gen/api';
6-
import { Metrics, PodMetricsList } from './metrics';
6+
import { Metrics, NodeMetricsList, PodMetricsList } from './metrics';
77

88
const emptyPodMetrics: PodMetricsList = {
99
kind: 'PodMetricsList',
@@ -39,7 +39,35 @@ const mockedPodMetrics: PodMetricsList = {
3939
},
4040
timestamp: '2021-09-26T11:57:21Z',
4141
window: '30s',
42-
containers: [{ name: 'nginx', usage: { cpu: '15', memory: '4012Ki' } },{ name: 'sidecar', usage: { cpu: '16', memory: '3012Ki' } }],
42+
containers: [
43+
{ name: 'nginx', usage: { cpu: '15', memory: '4012Ki' } },
44+
{ name: 'sidecar', usage: { cpu: '16', memory: '3012Ki' } },
45+
],
46+
},
47+
],
48+
};
49+
50+
const emptyNodeMetrics: NodeMetricsList = {
51+
kind: 'NodeMetricsList',
52+
apiVersion: 'metrics.k8s.io/v1beta1',
53+
metadata: { selfLink: '/apis/metrics.k8s.io/v1beta1/nodes/' },
54+
items: [],
55+
};
56+
57+
const mockedNodeMetrics: NodeMetricsList = {
58+
kind: 'NodeMetricsList',
59+
apiVersion: 'metrics.k8s.io/v1beta1',
60+
metadata: { selfLink: '/apis/metrics.k8s.io/v1beta1/nodes/' },
61+
items: [
62+
{
63+
metadata: {
64+
name: 'a-node',
65+
selfLink: '/apis/metrics.k8s.io/v1beta1/nodes/a-node',
66+
creationTimestamp: '2021-09-26T16:01:53Z',
67+
},
68+
timestamp: '2021-09-26T16:01:11Z',
69+
window: '30s',
70+
usage: { cpu: '214650124n', memory: '801480Ki' },
4371
},
4472
],
4573
};
@@ -53,63 +81,55 @@ const testConfigOptions: any = {
5381
currentContext: 'currentContext',
5482
};
5583

56-
57-
const systemUnderTest = ():[Metrics, nock.Scope] => {
84+
const systemUnderTest = (): [Metrics, nock.Scope] => {
5885
const kc = new KubeConfig();
5986
kc.loadFromOptions(testConfigOptions);
6087
const metricsClient = new Metrics(kc);
6188

62-
const scope = nock(testConfigOptions.clusters[0].server)
89+
const scope = nock(testConfigOptions.clusters[0].server);
6390

64-
return [metricsClient, scope]
65-
}
91+
return [metricsClient, scope];
92+
};
6693

6794
describe('Metrics', () => {
68-
6995
describe('getPodMetrics', () => {
7096
it('should return cluster scope empty pods list', async () => {
71-
const [metricsClient, scope] = systemUnderTest()
72-
const s = scope
73-
.get('/apis/metrics.k8s.io/v1beta1/pods')
74-
.reply(200, emptyPodMetrics);
97+
const [metricsClient, scope] = systemUnderTest();
98+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/pods').reply(200, emptyPodMetrics);
7599

76100
const response = await metricsClient.getPodMetrics();
77-
expect(response.items.length).to.equal(0);
101+
expect(response).to.deep.equal(emptyPodMetrics);
78102
s.done();
79103
});
80-
it('should return cluster scope empty pods list when namespace is empty string', async () => {
81-
const [metricsClient, scope] = systemUnderTest()
82-
const s = scope
83-
.get('/apis/metrics.k8s.io/v1beta1/pods')
84-
.reply(200, emptyPodMetrics);
104+
it('should return cluster scope empty pods list when namespace is empty string', async () => {
105+
const [metricsClient, scope] = systemUnderTest();
106+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/pods').reply(200, emptyPodMetrics);
85107

86-
const response = await metricsClient.getPodMetrics("");
87-
expect(response.items.length).to.equal(0);
108+
const response = await metricsClient.getPodMetrics('');
109+
expect(response).to.deep.equal(emptyPodMetrics);
88110
s.done();
89111
});
90112
it('should return namespace scope empty pods list', async () => {
91-
const [metricsClient, scope] = systemUnderTest()
113+
const [metricsClient, scope] = systemUnderTest();
92114
const s = scope
93115
.get(`/apis/metrics.k8s.io/v1beta1/${TEST_NAMESPACE}/default/pods`)
94116
.reply(200, emptyPodMetrics);
95117

96118
const response = await metricsClient.getPodMetrics(TEST_NAMESPACE);
97-
expect(response.items.length).to.equal(0);
119+
expect(response).to.deep.equal(emptyPodMetrics);
98120
s.done();
99121
});
100122
it('should return cluster scope pods metrics list', async () => {
101-
const [metricsClient, scope] = systemUnderTest()
102-
const s = scope
103-
.get('/apis/metrics.k8s.io/v1beta1/pods')
104-
.reply(200, mockedPodMetrics);
123+
const [metricsClient, scope] = systemUnderTest();
124+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/pods').reply(200, mockedPodMetrics);
105125

106126
const response = await metricsClient.getPodMetrics();
107127
expect(response).to.deep.equal(mockedPodMetrics);
108128

109129
s.done();
110130
});
111131
it('should return namespace scope pods metric list', async () => {
112-
const [metricsClient, scope] = systemUnderTest()
132+
const [metricsClient, scope] = systemUnderTest();
113133
const s = scope
114134
.get(`/apis/metrics.k8s.io/v1beta1/${TEST_NAMESPACE}/default/pods`)
115135
.reply(200, mockedPodMetrics);
@@ -119,25 +139,61 @@ describe('Metrics', () => {
119139
s.done();
120140
});
121141
it('should resolve to error when 500', async () => {
122-
123142
const response: V1Status = {
124143
code: 12345,
125144
message: 'some message',
126145
};
127-
const [metricsClient, scope] = systemUnderTest()
128-
const s = scope
129-
.get('/apis/metrics.k8s.io/v1beta1/pods')
130-
.reply(500, response);
146+
const [metricsClient, scope] = systemUnderTest();
147+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/pods').reply(500, response);
131148

132149
try {
133150
await metricsClient.getPodMetrics();
134-
fail("expected thrown error");
151+
fail('expected thrown error');
152+
} catch (e) {
153+
if (!(e instanceof HttpError)) {
154+
fail('expected HttpError error');
155+
}
156+
expect(e.body.code).to.equal(response.code);
157+
expect(e.body.message).to.equal(response.message);
158+
}
159+
s.done();
160+
});
161+
});
162+
describe('getNodeMetrics', () => {
163+
it('should return empty nodes list', async () => {
164+
const [metricsClient, scope] = systemUnderTest();
165+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/nodes').reply(200, emptyNodeMetrics);
166+
167+
const response = await metricsClient.getNodeMetrics();
168+
expect(response).to.deep.equal(emptyNodeMetrics);
169+
s.done();
170+
});
171+
it('should return nodes metrics list', async () => {
172+
const [metricsClient, scope] = systemUnderTest();
173+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/nodes').reply(200, mockedNodeMetrics);
174+
175+
const response = await metricsClient.getNodeMetrics();
176+
expect(response).to.deep.equal(mockedNodeMetrics);
177+
178+
s.done();
179+
});
180+
it('should resolve to error when 500', async () => {
181+
const response: V1Status = {
182+
code: 12345,
183+
message: 'some message',
184+
};
185+
const [metricsClient, scope] = systemUnderTest();
186+
const s = scope.get('/apis/metrics.k8s.io/v1beta1/nodes').reply(500, response);
187+
188+
try {
189+
await metricsClient.getNodeMetrics();
190+
fail('expected thrown error');
135191
} catch (e) {
136-
if (!(e instanceof HttpError)){
137-
fail("expected HttpError error");
138-
}
139-
expect(e.body.code).to.equal(response.code);
140-
expect(e.body.message).to.equal(response.message);
192+
if (!(e instanceof HttpError)) {
193+
fail('expected HttpError error');
194+
}
195+
expect(e.body.code).to.equal(response.code);
196+
expect(e.body.message).to.equal(response.message);
141197
}
142198
s.done();
143199
});

0 commit comments

Comments
 (0)