Skip to content

Commit ad6e451

Browse files
authored
Merge pull request #668 from brendandburns/bigint
Add supports for more unit notations
2 parents 74c2872 + bfe3abe commit ad6e451

File tree

3 files changed

+97
-34
lines changed

3 files changed

+97
-34
lines changed

src/top.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { CoreV1Api, V1Node, V1Pod } from './gen/api';
2-
import { podsForNode, quantityToScalar, totalCPU, totalMemory } from './util';
2+
import { add, podsForNode, quantityToScalar, totalCPU, totalMemory } from './util';
33

44
export class ResourceUsage {
55
constructor(
6-
public readonly Capacity: number,
7-
public readonly RequestTotal: number,
8-
public readonly LimitTotal: number,
6+
public readonly Capacity: number | BigInt,
7+
public readonly RequestTotal: number | BigInt,
8+
public readonly LimitTotal: number | BigInt,
99
) {}
1010
}
1111

@@ -24,20 +24,20 @@ export async function topNodes(api: CoreV1Api): Promise<NodeStatus[]> {
2424
for (const node of nodes.body!.items) {
2525
const availableCPU = quantityToScalar(node.status!.allocatable!.cpu);
2626
const availableMem = quantityToScalar(node.status!.allocatable!.memory);
27-
let totalPodCPU = 0;
28-
let totalPodCPULimit = 0;
29-
let totalPodMem = 0;
30-
let totalPodMemLimit = 0;
27+
let totalPodCPU: number | bigint = 0;
28+
let totalPodCPULimit: number | bigint = 0;
29+
let totalPodMem: number | bigint = 0;
30+
let totalPodMemLimit: number | bigint = 0;
3131
let pods = await podsForNode(api, node.metadata!.name!);
3232
pods = pods.filter((pod: V1Pod) => pod.status!.phase === 'Running');
3333
pods.forEach((pod: V1Pod) => {
3434
const cpuTotal = totalCPU(pod);
35-
totalPodCPU += cpuTotal.request;
36-
totalPodCPULimit += cpuTotal.limit;
35+
totalPodCPU = add(totalPodCPU, cpuTotal.request);
36+
totalPodCPULimit = add(totalPodCPULimit, cpuTotal.limit);
3737

3838
const memTotal = totalMemory(pod);
39-
totalPodMem += memTotal.request;
40-
totalPodMemLimit += memTotal.limit;
39+
totalPodMem = add(totalPodMem, memTotal.request);
40+
totalPodMemLimit = add(totalPodMemLimit, memTotal.limit);
4141
});
4242

4343
const cpuUsage = new ResourceUsage(availableCPU, totalPodCPU, totalPodCPULimit);

src/util.ts

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,65 @@
1+
import { isNumber } from 'underscore';
12
import { CoreV1Api, V1Container, V1Pod } from './gen/api';
23

34
export async function podsForNode(api: CoreV1Api, nodeName: string): Promise<V1Pod[]> {
45
const allPods = await api.listPodForAllNamespaces();
56
return allPods.body.items.filter((pod: V1Pod) => pod.spec!.nodeName === nodeName);
67
}
78

8-
export function quantityToScalar(quantity: string): number {
9+
export function findSuffix(quantity: string): string {
10+
let ix = quantity.length - 1;
11+
while (ix >= 0 && !/[\.0-9]/.test(quantity.charAt(ix))) {
12+
ix--;
13+
}
14+
return ix === -1 ? '' : quantity.substring(ix + 1);
15+
}
16+
17+
export function quantityToScalar(quantity: string): number | bigint {
918
if (!quantity) {
1019
return 0;
1120
}
12-
if (quantity.endsWith('m')) {
13-
return parseInt(quantity.substr(0, quantity.length - 1), 10) / 1000.0;
14-
}
15-
if (quantity.endsWith('Ki')) {
16-
return parseInt(quantity.substr(0, quantity.length - 2), 10) * 1024;
21+
const suffix = findSuffix(quantity);
22+
if (suffix === '') {
23+
const num = Number(quantity).valueOf();
24+
if (isNaN(num)) {
25+
throw new Error('Unknown quantity ' + quantity);
26+
}
27+
return num;
1728
}
18-
const num = parseInt(quantity, 10);
19-
if (isNaN(num)) {
20-
throw new Error('Unknown quantity ' + quantity);
29+
switch (suffix) {
30+
case 'm':
31+
return Number(quantity.substr(0, quantity.length - 1)).valueOf() / 1000.0;
32+
case 'Ki':
33+
return BigInt(quantity.substr(0, quantity.length - 2)) * BigInt(1024);
34+
case 'Mi':
35+
return BigInt(quantity.substr(0, quantity.length - 2)) * BigInt(1024 * 1024);
36+
case 'Gi':
37+
return BigInt(quantity.substr(0, quantity.length - 2)) * BigInt(1024 * 1024 * 1024);
38+
case 'Ti':
39+
return (
40+
BigInt(quantity.substr(0, quantity.length - 2)) * BigInt(1024 * 1024 * 1024) * BigInt(1024)
41+
);
42+
case 'Pi':
43+
return (
44+
BigInt(quantity.substr(0, quantity.length - 2)) *
45+
BigInt(1024 * 1024 * 1024) *
46+
BigInt(1024 * 1024)
47+
);
48+
case 'Ei':
49+
return (
50+
BigInt(quantity.substr(0, quantity.length - 2)) *
51+
BigInt(1024 * 1024 * 1024) *
52+
BigInt(1024 * 1024 * 1024)
53+
);
54+
default:
55+
throw new Error(`Unknown suffix: ${suffix}`);
2156
}
22-
return num;
2357
}
2458

2559
export class ResourceStatus {
2660
constructor(
27-
public readonly request: number,
28-
public readonly limit: number,
61+
public readonly request: bigint | number,
62+
public readonly limit: bigint | number,
2963
public readonly resourceType: string,
3064
) {}
3165
}
@@ -38,16 +72,28 @@ export function totalMemory(pod: V1Pod): ResourceStatus {
3872
return totalForResource(pod, 'memory');
3973
}
4074

75+
export function add(n1: number | bigint, n2: number | bigint): number | bigint {
76+
if (isNumber(n1) && isNumber(n2)) {
77+
return n1 + n2;
78+
}
79+
if (isNumber(n1)) {
80+
return BigInt(Math.round(n1)) + (n2 as bigint);
81+
} else if (isNumber(n2)) {
82+
return (n1 as bigint) + BigInt(Math.round(n2));
83+
}
84+
return ((n1 as bigint) + n2) as bigint;
85+
}
86+
4187
export function totalForResource(pod: V1Pod, resource: string): ResourceStatus {
42-
let reqTotal = 0;
43-
let limitTotal = 0;
88+
let reqTotal: number | bigint = 0;
89+
let limitTotal: number | bigint = 0;
4490
pod.spec!.containers.forEach((container: V1Container) => {
4591
if (container.resources) {
4692
if (container.resources.requests) {
47-
reqTotal += quantityToScalar(container.resources.requests[resource]);
93+
reqTotal = add(reqTotal, quantityToScalar(container.resources.requests[resource]));
4894
}
4995
if (container.resources.limits) {
50-
limitTotal += quantityToScalar(container.resources.limits[resource]);
96+
limitTotal = add(limitTotal, quantityToScalar(container.resources.limits[resource]));
5197
}
5298
}
5399
});

src/util_test.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { expect } from 'chai';
1+
import { expect, should } from 'chai';
22
import { CoreV1Api, V1Container, V1Pod } from './api';
3-
import { podsForNode, quantityToScalar, totalCPU, totalMemory } from './util';
3+
import { findSuffix, podsForNode, quantityToScalar, totalCPU, totalMemory } from './util';
44

55
describe('Utils', () => {
66
it('should get zero pods for a node', async () => {
@@ -55,13 +55,24 @@ describe('Utils', () => {
5555
expect(quantityToScalar('')).to.equal(0);
5656

5757
expect(quantityToScalar('100m')).to.equal(0.1);
58+
expect(quantityToScalar('0.2')).to.equal(0.2);
5859
expect(quantityToScalar('1976m')).to.equal(1.976);
5960

60-
expect(quantityToScalar('10Ki')).to.equal(10240);
61-
6261
expect(quantityToScalar('1024')).to.equal(1024);
62+
expect(quantityToScalar('1024')).to.equal(1024);
63+
expect(quantityToScalar('10e3')).to.equal(10000);
64+
65+
expect(quantityToScalar('10Ki')).to.equal(BigInt(10240));
66+
expect(quantityToScalar('20Mi')).to.equal(BigInt(1024 * 1024 * 20));
67+
expect(quantityToScalar('30Gi')).to.equal(BigInt(1024 * 1024 * 1024 * 30));
68+
expect(quantityToScalar('40Ti')).to.equal(BigInt(1024 * 1024 * 1024 * 1024 * 40));
69+
expect(quantityToScalar('50Pi')).to.equal(BigInt(1024 * 1024 * 1024 * 1024 * 1024 * 50));
70+
expect(quantityToScalar('60Ei')).to.equal(
71+
BigInt(1024 * 1024 * 1024) * BigInt(1024 * 1024 * 1024 * 60),
72+
);
6373

6474
expect(() => quantityToScalar('foobar')).to.throw('Unknown quantity foobar');
75+
expect(() => quantityToScalar('100foobar')).to.throw('Unknown suffix: foobar');
6576
});
6677

6778
it('should get resources for pod', () => {
@@ -100,7 +111,13 @@ describe('Utils', () => {
100111
expect(cpuResult.limit).to.equal(0.2);
101112

102113
const memResult = totalMemory(pod);
103-
expect(memResult.request).to.equal(10240);
104-
expect(memResult.limit).to.equal(20480);
114+
expect(memResult.request).to.equal(BigInt(10240));
115+
expect(memResult.limit).to.equal(BigInt(20480));
116+
});
117+
118+
it('should find the suffix correctly', () => {
119+
expect(findSuffix('1234567')).to.equal('');
120+
expect(findSuffix('1234asdf')).to.equal('asdf');
121+
expect(findSuffix('1.0')).to.equal('');
105122
});
106123
});

0 commit comments

Comments
 (0)