Skip to content

Commit 7ce13b4

Browse files
authored
Merge pull request #1178 from brendandburns/top
Add an initial implementation of top
2 parents a9c6712 + d017e39 commit 7ce13b4

File tree

3 files changed

+241
-0
lines changed

3 files changed

+241
-0
lines changed

examples/src/main/java/io/kubernetes/client/examples/KubectlExample.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
import static io.kubernetes.client.extended.kubectl.Kubectl.portforward;
2121
import static io.kubernetes.client.extended.kubectl.Kubectl.scale;
2222
import static io.kubernetes.client.extended.kubectl.Kubectl.taint;
23+
import static io.kubernetes.client.extended.kubectl.Kubectl.top;
2324
import static io.kubernetes.client.extended.kubectl.Kubectl.version;
25+
import static io.kubernetes.client.extended.kubectl.KubectlTop.podMetricSum;
2426

2527
import com.google.common.io.ByteStreams;
2628
import io.kubernetes.client.common.KubernetesObject;
29+
import io.kubernetes.client.custom.NodeMetrics;
30+
import io.kubernetes.client.custom.PodMetrics;
2731
import io.kubernetes.client.extended.kubectl.KubectlExec;
2832
import io.kubernetes.client.extended.kubectl.KubectlPortForward;
2933
import io.kubernetes.client.extended.kubectl.exception.KubectlException;
@@ -35,11 +39,13 @@
3539
import io.kubernetes.client.openapi.models.V1Service;
3640
import io.kubernetes.client.util.Config;
3741
import java.util.Arrays;
42+
import java.util.List;
3843
import org.apache.commons.cli.CommandLine;
3944
import org.apache.commons.cli.DefaultParser;
4045
import org.apache.commons.cli.Option;
4146
import org.apache.commons.cli.Options;
4247
import org.apache.commons.cli.ParseException;
48+
import org.apache.commons.lang3.tuple.Pair;
4349

4450
/**
4551
* A Java equivalent for the kubectl command line tool. Not nearly as complete.
@@ -71,6 +77,15 @@ static Class<? extends KubernetesObject> getClassForKind(String kind) {
7177
return null;
7278
}
7379

80+
private static final String PADDING = " ";
81+
82+
private static String pad(String value) {
83+
while (value.length() < PADDING.length()) {
84+
value += " ";
85+
}
86+
return value;
87+
}
88+
7489
public static void main(String[] args)
7590
throws java.io.IOException, KubectlException, ParseException {
7691
ApiClient client = Config.defaultClient();
@@ -89,6 +104,44 @@ public static void main(String[] args)
89104
String name = null;
90105

91106
switch (verb) {
107+
case "top":
108+
String what = args[1];
109+
switch (what) {
110+
case "nodes":
111+
case "node":
112+
List<Pair<V1Node, NodeMetrics>> nodes =
113+
top(V1Node.class, NodeMetrics.class).apiClient(client).metric("cpu").execute();
114+
System.out.println(pad("Node") + "\tCPU\t\tMemory");
115+
for (Pair<V1Node, NodeMetrics> node : nodes) {
116+
System.out.println(
117+
pad(node.getLeft().getMetadata().getName())
118+
+ "\t"
119+
+ node.getRight().getUsage().get("cpu").getNumber()
120+
+ "\t"
121+
+ node.getRight().getUsage().get("memory").getNumber());
122+
}
123+
System.exit(0);
124+
case "pods":
125+
case "pod":
126+
List<Pair<V1Pod, PodMetrics>> pods =
127+
top(V1Pod.class, PodMetrics.class)
128+
.apiClient(client)
129+
.namespace(ns)
130+
.metric("cpu")
131+
.execute();
132+
System.out.println(pad("Pod") + "\tCPU\t\tMemory");
133+
for (Pair<V1Pod, PodMetrics> pod : pods) {
134+
System.out.println(
135+
pad(pod.getLeft().getMetadata().getName())
136+
+ "\t"
137+
+ podMetricSum(pod.getRight(), "cpu")
138+
+ "\t"
139+
+ podMetricSum(pod.getRight(), "memory"));
140+
}
141+
System.exit(0);
142+
}
143+
System.err.println("Unknown top argument: " + what);
144+
System.exit(-1);
92145
case "cp":
93146
String from = args[1];
94147
String to = args[2];

extended/src/main/java/io/kubernetes/client/extended/kubectl/Kubectl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@
2323
*/
2424
public class Kubectl {
2525

26+
/**
27+
* Equivalent for `kubectl top`
28+
*
29+
* @param apiTypeClass Must be either V1Pod.class or V1Node.class
30+
* @return the kubectl top implementation
31+
*/
32+
public static <ApiType extends KubernetesObject, MetricsType>
33+
KubectlTop<ApiType, MetricsType> top(
34+
Class<ApiType> apiTypeClass, Class<MetricsType> metricsTypeClass) {
35+
return new KubectlTop<ApiType, MetricsType>(apiTypeClass);
36+
}
37+
2638
/** Equivalence for `kubectl taint`. */
2739
public static KubectlTaint taint() {
2840
return new KubectlTaint();
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.extended.kubectl;
14+
15+
import io.kubernetes.client.Metrics;
16+
import io.kubernetes.client.common.KubernetesObject;
17+
import io.kubernetes.client.custom.ContainerMetrics;
18+
import io.kubernetes.client.custom.NodeMetrics;
19+
import io.kubernetes.client.custom.NodeMetricsList;
20+
import io.kubernetes.client.custom.PodMetrics;
21+
import io.kubernetes.client.custom.PodMetricsList;
22+
import io.kubernetes.client.custom.Quantity;
23+
import io.kubernetes.client.extended.kubectl.exception.KubectlException;
24+
import io.kubernetes.client.openapi.ApiClient;
25+
import io.kubernetes.client.openapi.ApiException;
26+
import io.kubernetes.client.openapi.apis.CoreV1Api;
27+
import io.kubernetes.client.openapi.models.V1Node;
28+
import io.kubernetes.client.openapi.models.V1NodeList;
29+
import io.kubernetes.client.openapi.models.V1Pod;
30+
import io.kubernetes.client.openapi.models.V1PodList;
31+
import java.io.IOException;
32+
import java.util.ArrayList;
33+
import java.util.Collections;
34+
import java.util.Comparator;
35+
import java.util.List;
36+
import org.apache.commons.lang3.tuple.ImmutablePair;
37+
import org.apache.commons.lang3.tuple.Pair;
38+
39+
public class KubectlTop<ApiType extends KubernetesObject, MetricsType>
40+
extends Kubectl.ResourceBuilder<ApiType, KubectlTop<ApiType, MetricsType>>
41+
implements Kubectl.Executable<List<Pair<ApiType, MetricsType>>> {
42+
String metricName;
43+
44+
KubectlTop(Class<ApiType> apiTypeClass) {
45+
super(apiTypeClass);
46+
this.metricName = "cpu";
47+
}
48+
49+
public KubectlTop<ApiType, MetricsType> metric(String metricName) {
50+
this.metricName = metricName;
51+
return this;
52+
}
53+
54+
@Override
55+
public List<Pair<ApiType, MetricsType>> execute() throws KubectlException {
56+
switch (metricName) {
57+
case "cpu":
58+
case "memory":
59+
break;
60+
default:
61+
throw new KubectlException("Unknown metric: " + metricName);
62+
}
63+
try {
64+
CoreV1Api api = new CoreV1Api(apiClient);
65+
if (apiTypeClass.equals(V1Node.class)) {
66+
return (List<Pair<ApiType, MetricsType>>) topNodes(api, apiClient, metricName);
67+
} else if (apiTypeClass.equals(V1Pod.class)) {
68+
return (List<Pair<ApiType, MetricsType>>) topPods(api, apiClient, metricName);
69+
} else {
70+
throw new KubectlException("Can not perform top for " + apiTypeClass.getName());
71+
}
72+
} catch (ApiException | IOException ex) {
73+
throw new KubectlException(ex);
74+
}
75+
}
76+
77+
private NodeMetrics findNodeMetric(String nodeName, NodeMetricsList list) {
78+
for (NodeMetrics metric : list.getItems()) {
79+
if (metric.getMetadata().getName().equals(nodeName)) {
80+
return metric;
81+
}
82+
}
83+
return null;
84+
}
85+
86+
private NodeMetrics findNodeMetric(V1Node node, NodeMetricsList list) {
87+
return findNodeMetric(node.getMetadata().getName(), list);
88+
}
89+
90+
static double findNodePercentage(V1Node node, Quantity value, String metric) {
91+
Quantity capacity = node.getStatus().getCapacity().get(metric);
92+
if (capacity == null) {
93+
return Double.POSITIVE_INFINITY;
94+
}
95+
return value.getNumber().doubleValue() / capacity.getNumber().doubleValue();
96+
}
97+
98+
private List<Pair<ApiType, MetricsType>> topNodes(
99+
CoreV1Api api, ApiClient apiClient, String metricName)
100+
throws KubectlException, ApiException, IOException {
101+
V1NodeList nodes = api.listNode(null, null, null, null, null, null, null, null, null);
102+
NodeMetricsList metrics = new Metrics(apiClient).getNodeMetrics();
103+
List<V1Node> items = nodes.getItems();
104+
Collections.sort(
105+
items,
106+
new Comparator<V1Node>() {
107+
@Override
108+
public int compare(V1Node arg0, V1Node arg1) {
109+
Quantity m0 =
110+
findNodeMetric(arg0.getMetadata().getName(), metrics).getUsage().get(metricName);
111+
Quantity m1 =
112+
findNodeMetric(arg1.getMetadata().getName(), metrics).getUsage().get(metricName);
113+
double p0 = findNodePercentage(arg0, m0, metricName);
114+
double p1 = findNodePercentage(arg1, m1, metricName);
115+
return Double.compare(p0, p1) * -1; // sort high to low
116+
}
117+
});
118+
119+
List<Pair<ApiType, MetricsType>> result = new ArrayList<>();
120+
for (V1Node node : items) {
121+
result.add(new ImmutablePair<>((ApiType) node, (MetricsType) findNodeMetric(node, metrics)));
122+
}
123+
return result;
124+
}
125+
126+
private static PodMetrics findPodMetric(String podName, PodMetricsList list) {
127+
for (PodMetrics metric : list.getItems()) {
128+
if (metric.getMetadata().getName().equals(podName)) {
129+
return metric;
130+
}
131+
}
132+
return null;
133+
}
134+
135+
private static PodMetrics findPodMetric(V1Pod pod, PodMetricsList list) {
136+
return findPodMetric(pod.getMetadata().getName(), list);
137+
}
138+
139+
public static double podMetricSum(PodMetrics podMetrics, String metricName) {
140+
double sum = 0;
141+
for (ContainerMetrics containerMetrics : podMetrics.getContainers()) {
142+
Quantity value = containerMetrics.getUsage().get(metricName);
143+
if (value != null) {
144+
sum += value.getNumber().doubleValue();
145+
}
146+
}
147+
return sum;
148+
}
149+
150+
private List<Pair<ApiType, MetricsType>> topPods(
151+
CoreV1Api api, ApiClient apiClient, String metricName)
152+
throws KubectlException, ApiException, IOException {
153+
V1PodList pods =
154+
api.listNamespacedPod(namespace, null, null, null, null, null, null, null, null, null);
155+
PodMetricsList metrics = new Metrics(apiClient).getPodMetrics(namespace);
156+
List<V1Pod> items = pods.getItems();
157+
Collections.sort(
158+
items,
159+
new Comparator<V1Pod>() {
160+
@Override
161+
public int compare(V1Pod arg0, V1Pod arg1) {
162+
double m0 =
163+
podMetricSum(findPodMetric(arg0.getMetadata().getName(), metrics), metricName);
164+
double m1 =
165+
podMetricSum(findPodMetric(arg1.getMetadata().getName(), metrics), metricName);
166+
return Double.compare(m0, m1) * -1; // sort high to low
167+
}
168+
});
169+
170+
List<Pair<ApiType, MetricsType>> result = new ArrayList<>();
171+
for (V1Pod pod : items) {
172+
result.add(new ImmutablePair<>((ApiType) pod, (MetricsType) findPodMetric(pod, metrics)));
173+
}
174+
return result;
175+
}
176+
}

0 commit comments

Comments
 (0)