Skip to content

Commit 1728757

Browse files
committed
getPodsMetrics method
1 parent 8db238a commit 1728757

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export * from './top';
1313
export * from './object';
1414
export * from './cp';
1515
export * from './patch';
16+
export * from './metrics';
1617
export { ConfigOptions, User, Cluster, Context } from './config_types';

src/metrics.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import * as request from 'request';
2+
import { KubeConfig } from './config';
3+
import { HttpError, ObjectSerializer, Authentication } from './gen/api';
4+
5+
export interface ContainerMetric {
6+
name: string,
7+
usage:{
8+
cpu: string,
9+
memory: string
10+
}
11+
}
12+
13+
export interface PodMetric {
14+
metadata: {
15+
name: string;
16+
namespace: string;
17+
selfLink: string;
18+
creationTimestamp: string;
19+
}
20+
timestamp: string,
21+
window: string,
22+
containers: Array<ContainerMetric>
23+
}
24+
25+
export interface PodMetricsList {
26+
kind: 'PodMetricsList';
27+
apiVersion: 'metrics.k8s.io/v1beta1';
28+
metadata: {
29+
selfLink: string
30+
};
31+
items: Array<PodMetric>
32+
}
33+
34+
export class Metrics {
35+
private config: KubeConfig;
36+
37+
public constructor(config: KubeConfig) {
38+
this.config = config;
39+
}
40+
41+
// TODO getNodeMetrics
42+
43+
public async getPodMetrics(namespace?: string): Promise<PodMetricsList> {
44+
45+
let path:string;
46+
47+
if (namespace !== undefined && namespace.length > 0){
48+
path = `/apis/metrics.k8s.io/v1beta1/${namespace}/default/pods`;
49+
} else {
50+
path = "/apis/metrics.k8s.io/v1beta1/pods";
51+
}
52+
53+
const cluster = this.config.getCurrentCluster();
54+
if (!cluster) {
55+
throw new Error('No currently active cluster');
56+
}
57+
58+
const requestOptions: request.Options = {
59+
method: 'GET',
60+
uri: cluster.server + path,
61+
};
62+
await this.config.applyToRequest(requestOptions);
63+
64+
return new Promise((resolve, reject) => {
65+
const req = request(requestOptions, (error, response, body) => {
66+
if (error) {
67+
reject(error);
68+
} else if (response.statusCode !== 200) {
69+
try {
70+
const deserializedBody = ObjectSerializer.deserialize(JSON.parse(body), 'V1Status');
71+
reject(new HttpError(response, deserializedBody, response.statusCode));
72+
} catch (e) {
73+
reject(new HttpError(response, body, response.statusCode));
74+
}
75+
} else {
76+
const result: PodMetricsList = JSON.parse(body)
77+
resolve(result);
78+
}
79+
});
80+
});
81+
82+
}
83+
}

src/metrics_test.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { fail } from 'assert';
2+
import { expect } from 'chai';
3+
import * as nock from 'nock';
4+
import { KubeConfig } from './config';
5+
import { V1Status, HttpError } from './gen/api';
6+
import { Metrics, PodMetricsList } from './metrics';
7+
8+
const emptyPodMetrics: PodMetricsList = {
9+
kind: 'PodMetricsList',
10+
apiVersion: 'metrics.k8s.io/v1beta1',
11+
metadata: {
12+
selfLink: '/apis/metrics.k8s.io/v1beta1/pods',
13+
},
14+
items: [],
15+
};
16+
17+
const mockedPodMetrics: PodMetricsList = {
18+
kind: 'PodMetricsList',
19+
apiVersion: 'metrics.k8s.io/v1beta1',
20+
metadata: { selfLink: '/apis/metrics.k8s.io/v1beta1/pods/' },
21+
items: [
22+
{
23+
metadata: {
24+
name: 'dice-roller-7c76898b4d-shm9p',
25+
namespace: 'default',
26+
selfLink: '/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/dice-roller-7c76898b4d-shm9p',
27+
creationTimestamp: '2021-09-26T11:57:27Z',
28+
},
29+
timestamp: '2021-09-26T11:57:21Z',
30+
window: '30s',
31+
containers: [{ name: 'nginx', usage: { cpu: '10', memory: '3912Ki' } }],
32+
},
33+
{
34+
metadata: {
35+
name: 'other-pod-7c76898b4e-12kj',
36+
namespace: 'default',
37+
selfLink: '/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/other-pod-7c76898b4e-12kj',
38+
creationTimestamp: '2021-09-26T11:57:27Z',
39+
},
40+
timestamp: '2021-09-26T11:57:21Z',
41+
window: '30s',
42+
containers: [{ name: 'nginx', usage: { cpu: '15', memory: '4012Ki' } },{ name: 'sidecar', usage: { cpu: '16', memory: '3012Ki' } }],
43+
},
44+
],
45+
};
46+
47+
const TEST_NAMESPACE = 'test-namespace';
48+
49+
const testConfigOptions: any = {
50+
clusters: [{ name: 'cluster', server: 'https://127.0.0.1:51010' }],
51+
users: [{ name: 'user', password: 'password' }],
52+
contexts: [{ name: 'currentContext', cluster: 'cluster', user: 'user' }],
53+
currentContext: 'currentContext',
54+
};
55+
56+
57+
const systemUnderTest = ():[Metrics, nock.Scope] => {
58+
const kc = new KubeConfig();
59+
kc.loadFromOptions(testConfigOptions);
60+
const metricsClient = new Metrics(kc);
61+
62+
const scope = nock(testConfigOptions.clusters[0].server)
63+
64+
return [metricsClient, scope]
65+
}
66+
67+
describe('Metrics', () => {
68+
69+
describe('getPodMetrics', () => {
70+
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);
75+
76+
const response = await metricsClient.getPodMetrics();
77+
expect(response.items.length).to.equal(0);
78+
s.done();
79+
});
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);
85+
86+
const response = await metricsClient.getPodMetrics("");
87+
expect(response.items.length).to.equal(0);
88+
s.done();
89+
});
90+
it('should return namespace scope empty pods list', async () => {
91+
const [metricsClient, scope] = systemUnderTest()
92+
const s = scope
93+
.get(`/apis/metrics.k8s.io/v1beta1/${TEST_NAMESPACE}/default/pods`)
94+
.reply(200, emptyPodMetrics);
95+
96+
const response = await metricsClient.getPodMetrics(TEST_NAMESPACE);
97+
expect(response.items.length).to.equal(0);
98+
s.done();
99+
});
100+
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);
105+
106+
const response = await metricsClient.getPodMetrics();
107+
expect(response).to.deep.equal(mockedPodMetrics);
108+
109+
s.done();
110+
});
111+
it('should return namespace scope pods metric list', async () => {
112+
const [metricsClient, scope] = systemUnderTest()
113+
const s = scope
114+
.get(`/apis/metrics.k8s.io/v1beta1/${TEST_NAMESPACE}/default/pods`)
115+
.reply(200, mockedPodMetrics);
116+
117+
const response = await metricsClient.getPodMetrics(TEST_NAMESPACE);
118+
expect(response).to.deep.equal(mockedPodMetrics);
119+
s.done();
120+
});
121+
it('should resolve to error when 500', async () => {
122+
123+
const response: V1Status = {
124+
code: 12345,
125+
message: 'some message',
126+
};
127+
const [metricsClient, scope] = systemUnderTest()
128+
const s = scope
129+
.get('/apis/metrics.k8s.io/v1beta1/pods')
130+
.reply(500, response);
131+
132+
try {
133+
await metricsClient.getPodMetrics();
134+
fail("expected thrown error");
135+
} 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);
141+
}
142+
s.done();
143+
});
144+
});
145+
});

0 commit comments

Comments
 (0)