From f57f6e3b1c447968166dd38716a90e6644b51e8d Mon Sep 17 00:00:00 2001 From: Dustin Jenkins Date: Thu, 3 Apr 2025 09:40:00 -0700 Subject: [PATCH 1/5] feat: add label selector to metrics call --- .../java/io/kubernetes/client/Metrics.java | 26 +++++++++++++++---- .../io/kubernetes/client/MetricsTest.java | 19 ++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/util/src/main/java/io/kubernetes/client/Metrics.java b/util/src/main/java/io/kubernetes/client/Metrics.java index a655ea72b3..d884ae82c1 100644 --- a/util/src/main/java/io/kubernetes/client/Metrics.java +++ b/util/src/main/java/io/kubernetes/client/Metrics.java @@ -20,8 +20,11 @@ import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.Configuration; import io.kubernetes.client.util.generic.GenericKubernetesApi; +import io.kubernetes.client.util.generic.options.ListOptions; public class Metrics { + private static final String API_GROUP = "metrics.k8s.io"; + private static final String API_VERSION = "v1beta1"; private ApiClient apiClient; /** Simple Metrics API constructor, uses default configuration */ @@ -61,17 +64,30 @@ public NodeMetricsList getNodeMetrics() throws ApiException { new GenericKubernetesApi<>( NodeMetrics.class, NodeMetricsList.class, - "metrics.k8s.io", - "v1beta1", + Metrics.API_GROUP, + Metrics.API_VERSION, "nodes", apiClient); return metricsClient.list().throwsApiException().getObject(); } public PodMetricsList getPodMetrics(String namespace) throws ApiException { + return getPodMetrics(namespace, null); + } + + /** + * Obtain Pod Metrics in the given Namespace with an optional label selector. + * @param namespace The Namespace to look in. + * @param labelSelector The label selector, optional. Use comma-delimited for multiple labels. + * @return PodMetricList, never null. + * @throws ApiException If the ApiClient cannot complete the request. + */ + public PodMetricsList getPodMetrics(String namespace, String labelSelector) throws ApiException { GenericKubernetesApi metricsClient = - new GenericKubernetesApi<>( - PodMetrics.class, PodMetricsList.class, "metrics.k8s.io", "v1beta1", "pods", apiClient); - return metricsClient.list(namespace).throwsApiException().getObject(); + new GenericKubernetesApi<>( + PodMetrics.class, PodMetricsList.class, Metrics.API_GROUP, Metrics.API_VERSION, "pods", apiClient); + final ListOptions listOptions = new ListOptions(); + listOptions.setLabelSelector(labelSelector); + return metricsClient.list(namespace, listOptions).throwsApiException().getObject(); } } diff --git a/util/src/test/java/io/kubernetes/client/MetricsTest.java b/util/src/test/java/io/kubernetes/client/MetricsTest.java index 39eae73d55..cfdce46776 100644 --- a/util/src/test/java/io/kubernetes/client/MetricsTest.java +++ b/util/src/test/java/io/kubernetes/client/MetricsTest.java @@ -57,6 +57,25 @@ void getPodMetricsThrowsAPIExceptionWhenServerReturnsError() { } } + @Test + void getPodMetricsWithLabelSelectorThrowsAPIExceptionWhenServerReturnsError() { + String namespace = "default"; + Metrics metrics = new Metrics(client); + apiServer.stubFor( + get(urlPathMatching("^/apis/metrics.k8s.io/v1beta1/namespaces/" + namespace + "/pods.*")) + .willReturn( + aResponse() + .withStatus(503) + .withHeader("Content-Type", "text/plain") + .withBody("Service Unavailable"))); + try { + metrics.getPodMetrics(namespace, "foo=bar"); + failBecauseExceptionWasNotThrown(ApiException.class); + } catch (ApiException ex) { + assertThat(ex.getCode()).isEqualTo(503); + } + } + @Test void getNodeMetricsThrowsAPIExceptionWhenServerReturnsError() { Metrics metrics = new Metrics(client); From d67c840368d91cd59e9beb14d20b096c5d8e7a73 Mon Sep 17 00:00:00 2001 From: Dustin Jenkins Date: Wed, 30 Jul 2025 08:53:46 -0700 Subject: [PATCH 2/5] refactor: make constants from strings --- util/src/main/java/io/kubernetes/client/Metrics.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util/src/main/java/io/kubernetes/client/Metrics.java b/util/src/main/java/io/kubernetes/client/Metrics.java index d884ae82c1..5c8cdf05bf 100644 --- a/util/src/main/java/io/kubernetes/client/Metrics.java +++ b/util/src/main/java/io/kubernetes/client/Metrics.java @@ -25,6 +25,9 @@ public class Metrics { private static final String API_GROUP = "metrics.k8s.io"; private static final String API_VERSION = "v1beta1"; + private static final String PODS = "pods"; + private static final String NODES = "nodes"; + private ApiClient apiClient; /** Simple Metrics API constructor, uses default configuration */ @@ -66,7 +69,7 @@ public NodeMetricsList getNodeMetrics() throws ApiException { NodeMetricsList.class, Metrics.API_GROUP, Metrics.API_VERSION, - "nodes", + Metrics.NODES, apiClient); return metricsClient.list().throwsApiException().getObject(); } @@ -85,7 +88,7 @@ public PodMetricsList getPodMetrics(String namespace) throws ApiException { public PodMetricsList getPodMetrics(String namespace, String labelSelector) throws ApiException { GenericKubernetesApi metricsClient = new GenericKubernetesApi<>( - PodMetrics.class, PodMetricsList.class, Metrics.API_GROUP, Metrics.API_VERSION, "pods", apiClient); + PodMetrics.class, PodMetricsList.class, Metrics.API_GROUP, Metrics.API_VERSION, Metrics.PODS, apiClient); final ListOptions listOptions = new ListOptions(); listOptions.setLabelSelector(labelSelector); return metricsClient.list(namespace, listOptions).throwsApiException().getObject(); From 56628df6eb59aeaa8dbae2bc77ebf027e82d6d40 Mon Sep 17 00:00:00 2001 From: Dustin Jenkins Date: Thu, 31 Jul 2025 09:12:12 -0700 Subject: [PATCH 3/5] refactor: clearly mark optional argument --- util/src/main/java/io/kubernetes/client/Metrics.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/src/main/java/io/kubernetes/client/Metrics.java b/util/src/main/java/io/kubernetes/client/Metrics.java index 5c8cdf05bf..24e691c0b9 100644 --- a/util/src/main/java/io/kubernetes/client/Metrics.java +++ b/util/src/main/java/io/kubernetes/client/Metrics.java @@ -22,6 +22,8 @@ import io.kubernetes.client.util.generic.GenericKubernetesApi; import io.kubernetes.client.util.generic.options.ListOptions; +import javax.annotation.Nullable; + public class Metrics { private static final String API_GROUP = "metrics.k8s.io"; private static final String API_VERSION = "v1beta1"; @@ -85,7 +87,7 @@ public PodMetricsList getPodMetrics(String namespace) throws ApiException { * @return PodMetricList, never null. * @throws ApiException If the ApiClient cannot complete the request. */ - public PodMetricsList getPodMetrics(String namespace, String labelSelector) throws ApiException { + public PodMetricsList getPodMetrics(String namespace, @Nullable String labelSelector) throws ApiException { GenericKubernetesApi metricsClient = new GenericKubernetesApi<>( PodMetrics.class, PodMetricsList.class, Metrics.API_GROUP, Metrics.API_VERSION, Metrics.PODS, apiClient); From 86c9e73d2b081efab7e7463cae9d3c51f6b756e9 Mon Sep 17 00:00:00 2001 From: Dustin Jenkins Date: Mon, 25 Aug 2025 08:41:50 -0700 Subject: [PATCH 4/5] refactor: review rework to use assert throws --- .../io/kubernetes/client/MetricsTest.java | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/util/src/test/java/io/kubernetes/client/MetricsTest.java b/util/src/test/java/io/kubernetes/client/MetricsTest.java index cfdce46776..568b60852e 100644 --- a/util/src/test/java/io/kubernetes/client/MetricsTest.java +++ b/util/src/test/java/io/kubernetes/client/MetricsTest.java @@ -16,6 +16,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import io.kubernetes.client.openapi.ApiClient; @@ -49,12 +50,7 @@ void getPodMetricsThrowsAPIExceptionWhenServerReturnsError() { .withStatus(503) .withHeader("Content-Type", "text/plain") .withBody("Service Unavailable"))); - try { - metrics.getPodMetrics(namespace); - failBecauseExceptionWasNotThrown(ApiException.class); - } catch (ApiException ex) { - assertThat(ex.getCode()).isEqualTo(503); - } + assertThrows(ApiException.class, () -> metrics.getPodMetrics(namespace)); } @Test @@ -62,18 +58,13 @@ void getPodMetricsWithLabelSelectorThrowsAPIExceptionWhenServerReturnsError() { String namespace = "default"; Metrics metrics = new Metrics(client); apiServer.stubFor( - get(urlPathMatching("^/apis/metrics.k8s.io/v1beta1/namespaces/" + namespace + "/pods.*")) - .willReturn( - aResponse() - .withStatus(503) - .withHeader("Content-Type", "text/plain") - .withBody("Service Unavailable"))); - try { - metrics.getPodMetrics(namespace, "foo=bar"); - failBecauseExceptionWasNotThrown(ApiException.class); - } catch (ApiException ex) { - assertThat(ex.getCode()).isEqualTo(503); - } + get(urlPathMatching("^/apis/metrics.k8s.io/v1beta1/namespaces/" + namespace + "/pods.*")) + .willReturn( + aResponse() + .withStatus(503) + .withHeader("Content-Type", "text/plain") + .withBody("Service Unavailable"))); + assertThrows(ApiException.class, () -> metrics.getPodMetrics(namespace, "foo=bar")); } @Test @@ -86,11 +77,6 @@ void getNodeMetricsThrowsAPIExceptionWhenServerReturnsError() { .withStatus(503) .withHeader("Content-Type", "text/plain") .withBody("Service Unavailable"))); - try { - metrics.getNodeMetrics(); - failBecauseExceptionWasNotThrown(ApiException.class); - } catch (ApiException ex) { - assertThat(ex.getCode()).isEqualTo(503); - } + assertThrows(ApiException.class, metrics::getNodeMetrics); } } From e614ea84d1e582c6e42220ba5e83d27caeb4ea2c Mon Sep 17 00:00:00 2001 From: Dustin Jenkins Date: Thu, 28 Aug 2025 09:59:55 -0700 Subject: [PATCH 5/5] refactor: review rework --- .../client/examples/MetricsExample.java | 17 +++++++++++++++++ .../main/java/io/kubernetes/client/Metrics.java | 14 +++++++++++--- .../java/io/kubernetes/client/MetricsTest.java | 16 ---------------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/examples/examples-release-latest/src/main/java/io/kubernetes/client/examples/MetricsExample.java b/examples/examples-release-latest/src/main/java/io/kubernetes/client/examples/MetricsExample.java index 0d8c10e30b..93815c0694 100644 --- a/examples/examples-release-latest/src/main/java/io/kubernetes/client/examples/MetricsExample.java +++ b/examples/examples-release-latest/src/main/java/io/kubernetes/client/examples/MetricsExample.java @@ -64,5 +64,22 @@ public static void main(String[] args) throws IOException, ApiException { System.out.println(); } } + + for (PodMetrics item : metrics.getPodMetrics("default", "foo=bar").getItems()) { + System.out.println(item.getMetadata().getName()); + System.out.println("------------------------------"); + if (item.getContainers() == null) { + continue; + } + for (ContainerMetrics container : item.getContainers()) { + System.out.println(container.getName()); + System.out.println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-"); + for (String key : container.getUsage().keySet()) { + System.out.println("\t" + key); + System.out.println("\t" + container.getUsage().get(key)); + } + System.out.println(); + } + } } } diff --git a/util/src/main/java/io/kubernetes/client/Metrics.java b/util/src/main/java/io/kubernetes/client/Metrics.java index 24e691c0b9..320eeeaf36 100644 --- a/util/src/main/java/io/kubernetes/client/Metrics.java +++ b/util/src/main/java/io/kubernetes/client/Metrics.java @@ -20,6 +20,7 @@ import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.Configuration; import io.kubernetes.client.util.generic.GenericKubernetesApi; +import io.kubernetes.client.util.generic.KubernetesApiResponse; import io.kubernetes.client.util.generic.options.ListOptions; import javax.annotation.Nullable; @@ -91,8 +92,15 @@ public PodMetricsList getPodMetrics(String namespace, @Nullable String labelSele GenericKubernetesApi metricsClient = new GenericKubernetesApi<>( PodMetrics.class, PodMetricsList.class, Metrics.API_GROUP, Metrics.API_VERSION, Metrics.PODS, apiClient); - final ListOptions listOptions = new ListOptions(); - listOptions.setLabelSelector(labelSelector); - return metricsClient.list(namespace, listOptions).throwsApiException().getObject(); + final KubernetesApiResponse response; + if (labelSelector == null || labelSelector.trim().isEmpty()) { + response = metricsClient.list(namespace); + } else { + final ListOptions listOptions = new ListOptions(); + listOptions.setLabelSelector(labelSelector); + response = metricsClient.list(namespace, listOptions); + } + + return response.throwsApiException().getObject(); } } diff --git a/util/src/test/java/io/kubernetes/client/MetricsTest.java b/util/src/test/java/io/kubernetes/client/MetricsTest.java index 568b60852e..c7b36f4ed8 100644 --- a/util/src/test/java/io/kubernetes/client/MetricsTest.java +++ b/util/src/test/java/io/kubernetes/client/MetricsTest.java @@ -14,8 +14,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.junit.jupiter.api.Assertions.assertThrows; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; @@ -53,20 +51,6 @@ void getPodMetricsThrowsAPIExceptionWhenServerReturnsError() { assertThrows(ApiException.class, () -> metrics.getPodMetrics(namespace)); } - @Test - void getPodMetricsWithLabelSelectorThrowsAPIExceptionWhenServerReturnsError() { - String namespace = "default"; - Metrics metrics = new Metrics(client); - apiServer.stubFor( - get(urlPathMatching("^/apis/metrics.k8s.io/v1beta1/namespaces/" + namespace + "/pods.*")) - .willReturn( - aResponse() - .withStatus(503) - .withHeader("Content-Type", "text/plain") - .withBody("Service Unavailable"))); - assertThrows(ApiException.class, () -> metrics.getPodMetrics(namespace, "foo=bar")); - } - @Test void getNodeMetricsThrowsAPIExceptionWhenServerReturnsError() { Metrics metrics = new Metrics(client);