Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
import io.kubernetes.client.openapi.models.V1Namespace;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.generic.KubernetesApiResponse;
import io.kubernetes.client.util.generic.options.ListOptions;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesListObject;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
import io.kubernetes.client.util.generic.dynamic.Dynamics;
import org.junit.jupiter.api.Test;
Expand All @@ -41,4 +44,35 @@ void dynamicApiCreateAndDeleteNamespace() throws Exception {
dynamicApi.delete("e2e-dynamic").throwsApiException().getObject();
assertThat(deleted).isNotNull();
}

@Test
void dynamicApiListWithResourceVersionMatchAndWatchBookmarks() throws Exception {
ApiClient client = ClientBuilder.defaultClient();
DynamicKubernetesApi dynamicApi =
new DynamicKubernetesApi("", "v1", "namespaces", client);
String namespaceName = "e2e-dynamic-list-options";
V1Namespace namespace = new V1Namespace().metadata(new V1ObjectMeta().name(namespaceName));

DynamicKubernetesObject createdNamespace =
dynamicApi.create(Dynamics.newFromJson(JSON.serialize(namespace))).getObject();
assertThat(createdNamespace).isNotNull();

try {
KubernetesApiResponse<DynamicKubernetesListObject> listResponse =
dynamicApi.list(
ListOptions.builder()
.fieldSelector("metadata.name=" + namespaceName)
.resourceVersion(createdNamespace.getMetadata().getResourceVersion())
.resourceVersionMatch("NotOlderThan")
.allowWatchBookmarks(true)
.build());

assertThat(listResponse.isSuccess()).isTrue();
assertThat(listResponse.getObject()).isNotNull();
assertThat(listResponse.getObject().getItems())
.anySatisfy(item -> assertThat(item.getMetadata().getName()).isEqualTo(namespaceName));
} finally {
dynamicApi.delete(namespaceName).throwsApiException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -579,11 +579,13 @@ private CallBuilder makeClusterListCallBuilder(final ListOptions listOptions) {
adaptListCall(
customObjectsApi.getApiClient(),
customObjectsApi.listClusterCustomObject( this.apiGroup, this.apiVersion, this.resourcePlural)
.allowWatchBookmarks(listOptions.getAllowWatchBookmarks())
._continue(listOptions.getContinue())
.fieldSelector(listOptions.getFieldSelector())
.labelSelector(listOptions.getLabelSelector())
.limit(listOptions.getLimit())
.resourceVersion(listOptions.getResourceVersion())
.resourceVersionMatch(listOptions.getResourceVersionMatch())
.timeoutSeconds(listOptions.getTimeoutSeconds())
.watch(false)
.buildCall(null),
Expand Down Expand Up @@ -626,11 +628,13 @@ private CallBuilder makeNamespacedListCallBuilder(
adaptListCall(
customObjectsApi.getApiClient(),
customObjectsApi.listNamespacedCustomObject(this.apiGroup, this.apiVersion, namespace, this.resourcePlural)
.allowWatchBookmarks(listOptions.getAllowWatchBookmarks())
._continue(listOptions.getContinue())
.fieldSelector(listOptions.getFieldSelector())
.labelSelector(listOptions.getLabelSelector())
.limit(listOptions.getLimit())
.resourceVersion(listOptions.getResourceVersion())
.resourceVersionMatch(listOptions.getResourceVersionMatch())
.timeoutSeconds(listOptions.getTimeoutSeconds())
.watch(false)
.buildCall(null),
Expand Down Expand Up @@ -1229,11 +1233,13 @@ public Watchable<ApiType> watch(final ListOptions listOptions) throws ApiExcepti
this.apiGroup,
this.apiVersion,
this.resourcePlural)
.allowWatchBookmarks(listOptions.getAllowWatchBookmarks())
._continue(listOptions.getContinue())
.fieldSelector(listOptions.getFieldSelector())
.labelSelector(listOptions.getLabelSelector())
.limit(listOptions.getLimit())
.resourceVersion(listOptions.getResourceVersion())
.resourceVersionMatch(listOptions.getResourceVersionMatch())
.timeoutSeconds(listOptions.getTimeoutSeconds())
.watch(true)
.buildCall(null);
Expand Down Expand Up @@ -1264,11 +1270,13 @@ public Watchable<ApiType> watch(String namespace, final ListOptions listOptions)
this.apiVersion,
namespace,
this.resourcePlural)
.allowWatchBookmarks(listOptions.getAllowWatchBookmarks())
._continue(listOptions.getContinue())
.fieldSelector(listOptions.getFieldSelector())
.labelSelector(listOptions.getLabelSelector())
.limit(listOptions.getLimit())
.resourceVersion(listOptions.getResourceVersion())
.resourceVersionMatch(listOptions.getResourceVersionMatch())
.timeoutSeconds(listOptions.getTimeoutSeconds())
.watch(true)
.buildCall(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public class ListOptions {
@SerializedName("resourceVersion")
private String resourceVersion;

@SerializedName("resourceVersionMatch")
private String resourceVersionMatch;

@SerializedName("allowWatchBookmarks")
private Boolean allowWatchBookmarks;

@SerializedName("timeoutSeconds")
private Integer timeoutSeconds;

Expand All @@ -36,6 +42,10 @@ public class ListOptions {
@SerializedName("isPartialObjectMetadataListRequest")
private Boolean isPartialObjectMetadataListRequest;

public static Builder builder() {
return new Builder();
}

public ListOptions fieldSelector(String fieldSelector) {
this.fieldSelector = fieldSelector;
return this;
Expand Down Expand Up @@ -75,6 +85,32 @@ public void setResourceVersion(String resourceVersion) {
this.resourceVersion = resourceVersion;
}

public ListOptions resourceVersionMatch(String resourceVersionMatch) {
this.resourceVersionMatch = resourceVersionMatch;
return this;
}

public String getResourceVersionMatch() {
return resourceVersionMatch;
}

public void setResourceVersionMatch(String resourceVersionMatch) {
this.resourceVersionMatch = resourceVersionMatch;
}

public ListOptions allowWatchBookmarks(Boolean allowWatchBookmarks) {
this.allowWatchBookmarks = allowWatchBookmarks;
return this;
}

public Boolean getAllowWatchBookmarks() {
return allowWatchBookmarks;
}

public void setAllowWatchBookmarks(Boolean allowWatchBookmarks) {
this.allowWatchBookmarks = allowWatchBookmarks;
}

public ListOptions limit(Integer limit) {
this.limit = limit;
return this;
Expand Down Expand Up @@ -127,4 +163,71 @@ public Boolean isPartialObjectMetadataListRequest() {
public void setPartialObjectMetadataListRequest(Boolean isPartialObjectMetadataListRequest) {
this.isPartialObjectMetadataListRequest = isPartialObjectMetadataListRequest;
}

public static final class Builder {
private final ListOptions options;

private Builder() {
this.options = new ListOptions();
}

public Builder fieldSelector(String fieldSelector) {
options.fieldSelector(fieldSelector);
return this;
}

public Builder labelSelector(String labelSelector) {
options.labelSelector(labelSelector);
return this;
}

public Builder resourceVersion(String resourceVersion) {
options.resourceVersion(resourceVersion);
return this;
}

public Builder resourceVersionMatch(String resourceVersionMatch) {
options.resourceVersionMatch(resourceVersionMatch);
return this;
}

public Builder allowWatchBookmarks(Boolean allowWatchBookmarks) {
options.allowWatchBookmarks(allowWatchBookmarks);
return this;
}

public Builder timeoutSeconds(Integer timeoutSeconds) {
options.timeoutSeconds(timeoutSeconds);
return this;
}

public Builder limit(Integer limit) {
options.limit(limit);
return this;
}

public Builder _continue(String _continue) {
options._continue(_continue);
return this;
}

public Builder isPartialObjectMetadataListRequest(Boolean partialMetadata) {
options.isPartialObjectMetadataListRequest(partialMetadata);
return this;
}

public ListOptions build() {
ListOptions built = new ListOptions();
built.setFieldSelector(options.getFieldSelector());
built.setLabelSelector(options.getLabelSelector());
built.setResourceVersion(options.getResourceVersion());
built.setResourceVersionMatch(options.getResourceVersionMatch());
built.setAllowWatchBookmarks(options.getAllowWatchBookmarks());
built.setTimeoutSeconds(options.getTimeoutSeconds());
built.setLimit(options.getLimit());
built.setContinue(options.getContinue());
built.setPartialObjectMetadataListRequest(options.isPartialObjectMetadataListRequest());
return built;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,32 @@ void listNamespacedJobReturningObject() {
apiServer.verify(1, getRequestedFor(urlPathEqualTo("/apis/batch/v1/namespaces/default/jobs")));
}

@Test
void listNamespacedJobWithResourceVersionMatchAndWatchBookmarks() {
V1JobList jobList = new V1JobList().kind("JobList").metadata(new V1ListMeta());

apiServer.stubFor(
get(urlPathEqualTo("/apis/batch/v1/namespaces/default/jobs"))
.willReturn(aResponse().withStatus(200).withBody(json.serialize(jobList))));

KubernetesApiResponse<V1JobList> jobListResp =
jobClient.list(
"default",
new ListOptions()
.resourceVersion("123")
.resourceVersionMatch("NotOlderThan")
.allowWatchBookmarks(true));
assertThat(jobListResp.isSuccess()).isTrue();
assertThat(jobListResp.getObject()).isEqualTo(jobList);
assertThat(jobListResp.getStatus()).isNull();
apiServer.verify(
1,
getRequestedFor(urlPathEqualTo("/apis/batch/v1/namespaces/default/jobs"))
.withQueryParam("resourceVersion", equalTo("123"))
.withQueryParam("resourceVersionMatch", equalTo("NotOlderThan"))
.withQueryParam("allowWatchBookmarks", equalTo("true")));
}

@Test
void listNamespacedJobWithPartialMetadataObjectListHeader() {
V1JobList jobList =
Expand Down Expand Up @@ -279,6 +305,30 @@ void watchNamespacedJobReturningObject() throws ApiException {
.withQueryParam("watch", equalTo("true")));
}

@Test
void watchNamespacedJobWithResourceVersionMatchAndWatchBookmarks() throws ApiException {
V1JobList jobList = new V1JobList().kind("JobList").metadata(new V1ListMeta());

apiServer.stubFor(
get(urlPathEqualTo("/apis/batch/v1/namespaces/default/jobs"))
.willReturn(aResponse().withStatus(200).withBody(json.serialize(jobList))));
Watchable<V1Job> jobListWatch =
jobClient.watch(
"default",
new ListOptions()
.resourceVersion("123")
.resourceVersionMatch("NotOlderThan")
.allowWatchBookmarks(true));
assertThat((Object) jobListWatch).isNotNull();
apiServer.verify(
1,
getRequestedFor(urlPathEqualTo("/apis/batch/v1/namespaces/default/jobs"))
.withQueryParam("watch", equalTo("true"))
.withQueryParam("resourceVersion", equalTo("123"))
.withQueryParam("resourceVersionMatch", equalTo("NotOlderThan"))
.withQueryParam("allowWatchBookmarks", equalTo("true")));
}

@Test
void readTimeoutShouldThrowException() {
ApiClient apiClient =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Copyright 2026 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package io.kubernetes.client.util.generic.options;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

class ListOptionsBuilderTest {

@Test
void listOptionsBuilderShouldPopulateAllFields() {
ListOptions options =
ListOptions.builder()
.fieldSelector("metadata.name=test")
.labelSelector("app=test")
.resourceVersion("100")
.resourceVersionMatch("NotOlderThan")
.allowWatchBookmarks(true)
.timeoutSeconds(30)
.limit(50)
._continue("next-page-token")
.isPartialObjectMetadataListRequest(true)
.build();

assertThat(options.getFieldSelector()).isEqualTo("metadata.name=test");
assertThat(options.getLabelSelector()).isEqualTo("app=test");
assertThat(options.getResourceVersion()).isEqualTo("100");
assertThat(options.getResourceVersionMatch()).isEqualTo("NotOlderThan");
assertThat(options.getAllowWatchBookmarks()).isTrue();
assertThat(options.getTimeoutSeconds()).isEqualTo(30);
assertThat(options.getLimit()).isEqualTo(50);
assertThat(options.getContinue()).isEqualTo("next-page-token");
assertThat(options.isPartialObjectMetadataListRequest()).isTrue();
}
}
Loading