
io.fabric8.kubernetes.client.dsl.base.BaseOperation Maven / Gradle / Ivy
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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.fabric8.kubernetes.client.dsl.base;
import io.fabric8.kubernetes.api.model.ObjectReference;
import io.fabric8.kubernetes.client.dsl.WritableOperation;
import io.fabric8.kubernetes.client.utils.CreateOrReplaceHelper;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.fabric8.kubernetes.api.builder.TypedVisitor;
import io.fabric8.kubernetes.api.builder.Visitor;
import io.fabric8.kubernetes.api.model.DeletionPropagation;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.api.model.LabelSelector;
import io.fabric8.kubernetes.api.model.ListOptions;
import io.fabric8.kubernetes.api.model.ListOptionsBuilder;
import io.fabric8.kubernetes.api.model.Status;
import io.fabric8.kubernetes.api.model.autoscaling.v1.Scale;
import io.fabric8.kubernetes.api.model.extensions.DeploymentRollback;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.KubernetesClientTimeoutException;
import io.fabric8.kubernetes.client.OperationInfo;
import io.fabric8.kubernetes.client.ResourceNotFoundException;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.dsl.EditReplacePatchDeletable;
import io.fabric8.kubernetes.client.dsl.FilterNested;
import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable;
import io.fabric8.kubernetes.client.dsl.Gettable;
import io.fabric8.kubernetes.client.dsl.Informable;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.ReplaceDeletable;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.internal.DefaultOperationInfo;
import io.fabric8.kubernetes.client.dsl.internal.WatchConnectionManager;
import io.fabric8.kubernetes.client.dsl.internal.WatchHTTPManager;
import io.fabric8.kubernetes.client.http.HttpRequest;
import io.fabric8.kubernetes.client.informers.ListerWatcher;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import io.fabric8.kubernetes.client.informers.SharedIndexInformer;
import io.fabric8.kubernetes.client.informers.impl.DefaultSharedIndexInformer;
import io.fabric8.kubernetes.client.internal.readiness.Readiness;
import io.fabric8.kubernetes.client.utils.URLUtils;
import io.fabric8.kubernetes.client.utils.URLUtils.URLBuilder;
import io.fabric8.kubernetes.client.utils.Utils;
import io.fabric8.kubernetes.client.utils.WatcherToggle;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
public class BaseOperation, R extends Resource>
extends CreateOnlyResourceOperation
implements
OperationInfo,
MixedOperation,
Resource,
ListerWatcher {
private static final String WATCH = "watch";
private static final String READ_ONLY_UPDATE_EXCEPTION_MESSAGE = "Cannot update read-only resources";
private static final String READ_ONLY_EDIT_EXCEPTION_MESSAGE = "Cannot edit read-only resources";
private final boolean cascading;
private final T item;
private final String resourceVersion;
private final boolean reloadingFromServer;
private final long gracePeriodSeconds;
private final DeletionPropagation propagationPolicy;
protected String apiVersion;
protected Class listType;
// informable state
private Map>> indexers;
private Long limit;
protected BaseOperation(OperationContext ctx) {
super(ctx);
this.cascading = ctx.getCascading();
this.item = (T) ctx.getItem();
this.reloadingFromServer = ctx.isReloadingFromServer();
this.resourceVersion = ctx.getResourceVersion();
this.gracePeriodSeconds = ctx.getGracePeriodSeconds();
this.propagationPolicy = ctx.getPropagationPolicy();
}
public BaseOperation newInstance(OperationContext context) {
return new BaseOperation<>(context);
}
/**
* Helper method for list() and list(limit, continue) methods
*
* @param url
* @return list of corresponding Kubernetes Resources
*/
private L listRequestHelper(URL url) {
try {
HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().url(url);
L answer = handleResponse(requestBuilder, listType);
updateApiVersion(answer);
return answer;
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(forOperationType("list"), ie);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(forOperationType("list"), e);
}
}
protected URL fetchListUrl(URL url, ListOptions listOptions) {
return appendListOptionParams(url, listOptions);
}
@Override
public T get() {
try {
final T answer = getMandatory();
updateApiVersion(answer);
return answer;
} catch (KubernetesClientException e) {
if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw e;
}
return null;
}
}
@Override
public T require() {
try {
T answer = getMandatory();
if (answer == null) {
throw new ResourceNotFoundException("The resource you request doesn't exist or couldn't be fetched.");
}
return answer;
} catch (KubernetesClientException e) {
if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw e;
}
throw new ResourceNotFoundException("Resource not found : " + e.getMessage(), e);
}
}
public T getMandatory() {
if (item != null && !reloadingFromServer) {
return Serialization.clone(item);
}
try {
URL requestUrl = getCompleteResourceUrl();
return handleGet(requestUrl);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(forOperationType("get"), ie);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(forOperationType("get"), e);
}
}
@Override
public T edit(UnaryOperator function) {
throw new KubernetesClientException(READ_ONLY_EDIT_EXCEPTION_MESSAGE);
}
@Override
public T editStatus(UnaryOperator function) {
throw new KubernetesClientException(READ_ONLY_EDIT_EXCEPTION_MESSAGE);
}
@Override
public T edit(Visitor... visitors) {
throw new KubernetesClientException(READ_ONLY_EDIT_EXCEPTION_MESSAGE);
}
@Override
public T edit(final Class visitorType, final Visitor visitor) {
return edit(new TypedVisitor() {
@Override
public Class getType() {
return visitorType;
}
@Override
public void visit(V item) {
visitor.visit(item);
}
});
}
@Override
public T accept(Consumer consumer) {
throw new KubernetesClientException(READ_ONLY_EDIT_EXCEPTION_MESSAGE);
}
@Override
public R withName(String name) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Name must be provided.");
}
return (R) newInstance(context.withName(name));
}
@Override
public ReplaceDeletable lockResourceVersion(String resourceVersion) {
return newInstance(context.withResourceVersion(resourceVersion));
}
@Override
public NonNamespaceOperation inNamespace(String namespace) {
return newInstance(context.withNamespace(namespace));
}
@Override
public NonNamespaceOperation inAnyNamespace() {
Config updated = new ConfigBuilder(config).withNamespace(null).build();
return newInstance(context.withConfig(updated).withNamespace(null));
}
@Override
public EditReplacePatchDeletable cascading(boolean cascading) {
return newInstance(context.withCascading(cascading).withPropagationPolicy(null));
}
@Override
public R load(InputStream is) {
return (R) newInstance(context.withItem(unmarshal(is, type)));
}
@Override
public R load(URL url) {
try (InputStream inputStream = url.openStream()) {
return load(inputStream);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(e);
}
}
@Override
public R load(File file) {
try (FileInputStream fis = new FileInputStream(file)) {
return load(fis);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(e);
}
}
@Override
public R load(String path) {
return load(new File(path));
}
@Override
public Gettable fromServer() {
return newInstance(context.withReloadingFromServer(true));
}
@SafeVarargs
@Override
public final T createOrReplace(T... items) {
T itemToCreateOrReplace = getItem();
if (items.length > 1) {
throw new IllegalArgumentException("Too many items to create.");
} else if (items.length == 1) {
itemToCreateOrReplace = items[0];
}
if (itemToCreateOrReplace == null) {
throw new IllegalArgumentException("Nothing to create.");
}
if (Utils.isNullOrEmpty(name)) {
return withName(itemToCreateOrReplace.getMetadata().getName()).createOrReplace(itemToCreateOrReplace);
}
T finalItemToCreateOrReplace = itemToCreateOrReplace;
CreateOrReplaceHelper createOrReplaceHelper = new CreateOrReplaceHelper<>(
this::create,
this::replace,
m -> waitUntilCondition(Objects::nonNull, 1, TimeUnit.SECONDS),
m -> fromServer().get()
);
return createOrReplaceHelper.createOrReplace(finalItemToCreateOrReplace);
}
@Override
public FilterWatchListDeletable withLabels(Map labels) {
return withNewFilter().withLabels(labels).endFilter();
}
@Override
public FilterWatchListDeletable withLabelSelector(LabelSelector selector) {
return withNewFilter().withLabelSelector(selector).endFilter();
}
@Override
public FilterWatchListDeletable withoutLabels(Map labels) {
return withNewFilter().withoutLabels(labels).endFilter();
}
@Override
public FilterWatchListDeletable withLabelIn(String key, String... values) {
return withNewFilter().withLabelIn(key, values).endFilter();
}
@Override
public FilterWatchListDeletable withLabelNotIn(String key, String... values) {
return withNewFilter().withLabelNotIn(key, values).endFilter();
}
@Override
public FilterWatchListDeletable withLabel(String key, String value) {
return withNewFilter().withLabel(key, value).endFilter();
}
@Override
public FilterWatchListDeletable withoutLabel(String key, String value) {
return withNewFilter().withoutLabel(key, value).endFilter();
}
@Override
public FilterWatchListDeletable withLabelSelector(String selectorAsString) {
return withNewFilter().withLabelSelector(selectorAsString).endFilter();
}
@Override
public FilterWatchListDeletable withFields(Map fields) {
return withNewFilter().withFields(fields).endFilter();
}
@Override
public FilterWatchListDeletable withField(String key, String value) {
return withNewFilter().withField(key, value).endFilter();
}
@Override
public FilterWatchListDeletable withInvolvedObject(ObjectReference objectReference) {
if (objectReference != null) {
return withNewFilter().withInvolvedObject(objectReference).endFilter();
}
return this;
}
@Override
public FilterNested> withNewFilter() {
return new FilterNestedImpl<>(this);
}
@Override
public FilterWatchListDeletable withoutFields(Map fields) {
return withNewFilter().withoutFields(fields).endFilter();
}
@Override
public FilterWatchListDeletable withoutField(String key, String value) {
return withNewFilter().withoutField(key, value).endFilter();
}
public String getFieldQueryParam() {
return context.getFieldQueryParam();
}
public String getLabelQueryParam() {
return context.getLabelQueryParam();
}
@Override
public L list() {
return list(new ListOptions());
}
@Override
public L list(Integer limitVal, String continueVal) {
return list(new ListOptionsBuilder().withLimit(limitVal.longValue()).withContinue(continueVal).build());
}
@Override
public L list(ListOptions listOptions) {
try {
return listRequestHelper(
fetchListUrl(getNamespacedUrl(), defaultListOptions(listOptions, null)));
} catch (MalformedURLException e) {
throw KubernetesClientException.launderThrowable(forOperationType("list"), e);
}
}
/**
* Override the options based upon the context / call
*/
private ListOptions defaultListOptions(ListOptions options, Boolean watch) {
options.setWatch(watch);
String fieldQueryParam = context.getFieldQueryParam();
if (fieldQueryParam != null) {
options.setFieldSelector(fieldQueryParam);
}
String labelQueryParam = context.getLabelQueryParam();
if (labelQueryParam != null) {
options.setLabelSelector(labelQueryParam);
}
if (resourceVersion != null) {
options.setResourceVersion(resourceVersion);
}
return options;
}
@Override
public Boolean delete() {
if (item != null || (name != null && !name.isEmpty())) {
try {
deleteThis();
return true;
} catch (KubernetesClientException e) {
if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw e;
}
return false;
}
} else {
try {
deleteList();
return true;
} catch (KubernetesClientException e) {
if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw e;
}
return false;
}
}
}
@SafeVarargs
@Override
public final Boolean delete(T... items) {
return delete(Arrays.asList(items));
}
@Override
public Boolean delete(List items) {
boolean deleted = true;
if (items != null) {
for (T toDelete : items) {
if (toDelete == null) {
continue;
}
updateApiVersion(toDelete);
try {
if (toDelete.getMetadata() != null
&& toDelete.getMetadata().getName() != null
&& !toDelete.getMetadata().getName().isEmpty()) {
deleted &= inNamespace(checkNamespace(toDelete)).withName(toDelete.getMetadata().getName()).delete();
} else {
deleted &= withItem(toDelete).delete();
}
} catch (KubernetesClientException e) {
if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw e;
}
return false;
}
}
}
return deleted;
}
@Override
public T updateStatus(T item) {
try {
return handleUpdate(item, true);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(forOperationType("statusUpdate"), ie);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(forOperationType("statusUpdate"), e);
}
}
@Override
public T patchStatus(T item) {
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
}
public BaseOperation withItem(T item) {
return newInstance(context.withItem(item));
}
void deleteThis() {
try {
if (item != null) {
updateApiVersion(item);
handleDelete(item, gracePeriodSeconds, propagationPolicy, resourceVersion, cascading);
} else {
handleDelete(getResourceURLForWriteOperation(getResourceUrl()), gracePeriodSeconds, propagationPolicy, resourceVersion, cascading);
}
} catch (Exception e) {
throw KubernetesClientException.launderThrowable(forOperationType("delete"), e);
}
}
void deleteList() {
delete(list().getItems());
}
@Override
public BaseOperation withResourceVersion(String resourceVersion) {
return newInstance(context.withResourceVersion(resourceVersion));
}
@Override
public Watch watch(final Watcher watcher) {
return watch(new ListOptions(), watcher);
}
@Override
public Watch watch(String resourceVersion, Watcher watcher) {
return watch(new ListOptionsBuilder()
.withResourceVersion(resourceVersion)
.build(), watcher);
}
@Override
public Watch watch(ListOptions options, final Watcher watcher) {
WatcherToggle watcherToggle = new WatcherToggle<>(watcher, true);
options = defaultListOptions(options, true);
WatchConnectionManager watch = null;
try {
watch = new WatchConnectionManager<>(
httpClient,
this,
options,
watcherToggle,
config.getWatchReconnectInterval(),
config.getWatchReconnectLimit(),
config.getWebsocketTimeout()
);
watch.waitUntilReady();
return watch;
} catch (MalformedURLException e) {
throw KubernetesClientException.launderThrowable(forOperationType(WATCH), e);
} catch (KubernetesClientException ke) {
List furtherProcessedCodes = Arrays.asList(200, 503);
if (!furtherProcessedCodes.contains(ke.getCode())) {
if (watch != null) {
//release the watch
watch.close();
}
throw ke;
}
if (watch != null) {
//release the watch after disabling the watcher (to avoid premature call to onClose)
watcherToggle.disable();
watch.close();
}
// If the HTTP return code is 200 or 503, we retry the watch again using a persistent hanging
// HTTP GET. This is meant to handle cases like kubectl local proxy which does not support
// websockets. Issue: https://github.com/kubernetes/kubernetes/issues/25126
try {
return new WatchHTTPManager<>(
httpClient,
this,
options,
watcher,
config.getWatchReconnectInterval(),
config.getWatchReconnectLimit(),
config.getConnectionTimeout()
);
} catch (MalformedURLException e) {
throw KubernetesClientException.launderThrowable(forOperationType(WATCH), e);
}
}
}
@Override
public T replace(T item) {
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
}
@Override
public T replaceStatus(T item) {
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
}
@Override
public T patch(PatchContext patchContext, String patch) {
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
}
@Override
public T patch(PatchContext patchContext, T item) {
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
}
@Override
public boolean isResourceNamespaced() {
return Utils.isResourceNamespaced(getType());
}
protected T handleResponse(HttpRequest.Builder requestBuilder) throws InterruptedException, IOException {
return handleResponse(requestBuilder, getType());
}
@Override
protected T handleCreate(T resource) throws InterruptedException, IOException {
updateApiVersion(resource);
return handleCreate(resource, getType());
}
protected T handleUpdate(T updated, boolean status) throws InterruptedException, IOException {
updateApiVersion(updated);
return handleUpdate(updated, getType(), status);
}
protected T handlePatch(PatchContext context, T current, T updated, boolean status) throws InterruptedException, IOException {
updateApiVersion(updated);
return handlePatch(context, current, updated, getType(), status);
}
protected T handlePatch(T current, Map patchedUpdate) throws InterruptedException, IOException {
updateApiVersion(current);
return handlePatch(current, patchedUpdate, getType());
}
protected T sendPatchedObject(T oldObject, T updatedObject) {
try {
return handlePatch(null, oldObject, updatedObject, false);
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(interruptedException);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(e);
}
}
protected Scale handleScale(Scale scaleParam) {
try {
return handleScale(getCompleteResourceUrl().toString(), scaleParam);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(forOperationType("scale"), ie);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(forOperationType("scale"), e);
}
}
protected Status handleDeploymentRollback(DeploymentRollback deploymentRollback) {
try {
return handleDeploymentRollback(getCompleteResourceUrl().toString(), deploymentRollback);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw KubernetesClientException.launderThrowable(forOperationType("rollback"), ie);
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(forOperationType("rollback"), e);
}
}
protected T handleGet(URL resourceUrl) throws InterruptedException, IOException {
T answer = handleGet(resourceUrl, getType());
updateApiVersion(answer);
return answer;
}
private URL getCompleteResourceUrl() throws MalformedURLException {
URL requestUrl = null;
if (item != null) {
requestUrl = getNamespacedUrl(item);
} else {
requestUrl = getNamespacedUrl();
}
if (name != null) {
requestUrl = new URL(URLUtils.join(requestUrl.toString(), name));
} else if (item != null && reloadingFromServer) {
requestUrl = new URL(URLUtils.join(requestUrl.toString(), checkName(item)));
}
return requestUrl;
}
public Boolean isCascading() {
return cascading;
}
@Override
public T getItem() {
return item;
}
public String getResourceVersion() {
return resourceVersion;
}
public Boolean isReloadingFromServer() {
return reloadingFromServer;
}
public Long getGracePeriodSeconds() {
return gracePeriodSeconds;
}
public DeletionPropagation getPropagationPolicy() {
return propagationPolicy;
}
public Class getListType() {
return listType;
}
@Override
public String getKind() {
return type != null ? HasMetadata.getKind(type) : "Resource";
}
@Override
public String getGroup() {
return getAPIGroupName();
}
@Override
public String getPlural() {
return getResourceT();
}
@Override
public String getVersion() {
return getAPIGroupVersion();
}
@Override
public String getOperationType() {
return null;
}
@Override
public OperationInfo forOperationType(String type) {
return new DefaultOperationInfo(getKind(), type, name, namespace, getGroup(), getPlural(), getVersion());
}
@Override
public FilterWatchListDeletable withGracePeriod(long gracePeriodSeconds) {
return newInstance(context.withGracePeriodSeconds(gracePeriodSeconds));
}
@Override
public EditReplacePatchDeletable withPropagationPolicy(DeletionPropagation propagationPolicy) {
return newInstance(context.withPropagationPolicy(propagationPolicy));
}
@Override
public BaseOperation withWaitRetryBackoff(long initialBackoff, TimeUnit backoffUnit, double backoffMultiplier) {
return this;
}
protected Class extends Config> getConfigType() {
return Config.class;
}
/**
* Updates the list items if they have missing or default apiGroupVersion values and the resource is currently
* using API Groups with custom version strings
*
* @param list Kubernetes resource list
*/
protected void updateApiVersion(KubernetesResourceList list) {
String version = apiVersion;
if (list != null && version != null && version.length() > 0 && list.getItems() != null) {
list.getItems().forEach(this::updateApiVersion);
}
}
/**
* Updates the resource if it has missing or default apiGroupVersion values and the resource is currently
* using API Groups with custom version strings
*
* @param hasMetadata object whose api version needs to be updated
*/
protected void updateApiVersion(HasMetadata hasMetadata) {
String version = apiVersion;
if (hasMetadata != null && version != null && version.length() > 0) {
String current = hasMetadata.getApiVersion();
// lets overwrite the api version if its currently missing, the resource uses an API Group with '/'
// or we have the default of 'v1' when using an API group version like 'build.openshift.io/v1'
if (current == null || "v1".equals(current) || current.indexOf('/') < 0 && version.indexOf('/') > 0) {
hasMetadata.setApiVersion(version);
}
}
}
public Readiness getReadiness() {
return config.getReadiness();
}
@Override
public final boolean isReady() {
T item = fromServer().get();
if (item == null) {
return false;
}
return getReadiness().isReady(item);
}
@Override
public T waitUntilReady(long amount, TimeUnit timeUnit) {
return waitUntilCondition(resource -> Objects.nonNull(resource) && getReadiness().isReady(resource), amount, timeUnit);
}
@Override
public T waitUntilCondition(Predicate condition, long amount, TimeUnit timeUnit) {
CompletableFuture> futureCondition = informOnCondition(l -> {
if (l.isEmpty()) {
return condition.test(null);
}
return condition.test(l.get(0));
});
if (!Utils.waitUntilReady(futureCondition, amount, timeUnit)) {
futureCondition.cancel(true);
T i = getItem();
if (i != null) {
throw new KubernetesClientTimeoutException(i, amount, timeUnit);
}
throw new KubernetesClientTimeoutException(getKind(), getName(), getNamespace(), amount, timeUnit);
}
return futureCondition.thenApply(l -> l.isEmpty() ? null : l.get(0)).getNow(null);
}
@Override
public CompletableFuture> informOnCondition(Predicate> condition) {
CompletableFuture> future = new CompletableFuture<>();
AtomicReference tester = new AtomicReference<>();
// create an informer that supplies the tester with events and empty list handling
SharedIndexInformer informer = this.createInformer(0);
// prevent unnecessary watches and handle closure
future.whenComplete((r, t) -> informer.stop());
// use the cache to evaluate the list predicate, trapping any exceptions
Runnable test = () -> {
try {
// could skip if lastResourceVersion has not changed
List list = informer.getStore().list();
if (condition.test(list)) {
future.complete(list);
}
} catch (Exception e) {
future.completeExceptionally(e);
}
};
tester.set(test);
informer.addEventHandler(new ResourceEventHandler() {
@Override
public void onAdd(T obj) {
test.run();
}
@Override
public void onDelete(T obj, boolean deletedFinalStateUnknown) {
test.run();
}
@Override
public void onUpdate(T oldObj, T newObj) {
test.run();
}
@Override
public void onNothing() {
test.run();
}
});
informer.run();
return future;
}
public void setType(Class type) {
this.type = type;
}
public void setListType(Class listType) {
this.listType = listType;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
@Override
public WritableOperation dryRun(boolean isDryRun) {
return newInstance(context.withDryRun(isDryRun));
}
@Override
public Informable withIndexers(Map>> indexers) {
BaseOperation result = newInstance(context);
result.indexers = indexers;
result.limit = this.limit;
return result;
}
@Override
public BaseOperation withLimit(Long limit) {
BaseOperation result = newInstance(context);
result.indexers = this.indexers;
result.limit = limit;
return result;
}
@Override
public Long getLimit() {
return this.limit;
}
@Override
public SharedIndexInformer inform(ResourceEventHandler super T> handler, long resync) {
DefaultSharedIndexInformer result = createInformer(resync);
if (handler != null) {
result.addEventHandler(handler);
}
// synchronous start list/watch must succeed in the calling thread
// initial add events will be processed in the calling thread as well
result.run();
return result;
}
@Override
public SharedIndexInformer runnableInformer(long resync) {
return createInformer(resync);
}
private DefaultSharedIndexInformer createInformer(long resync) {
T i = getItem();
if (Utils.isNotNullOrEmpty(getName()) && i != null) {
checkName(i);
}
// use the local context / namespace but without a resourceVersion
DefaultSharedIndexInformer informer = new DefaultSharedIndexInformer<>(getType(), this.withResourceVersion(null).withLimit(this.limit), resync, Runnable::run); // just run the event notification in the websocket thread
if (indexers != null) {
informer.addIndexers(indexers);
}
return informer;
}
public static URL appendListOptionParams(URL base, ListOptions listOptions) {
if (listOptions == null) {
return base;
}
URLBuilder urlBuilder = new URLBuilder(base);
if (listOptions.getLimit() != null) {
urlBuilder.addQueryParameter("limit", listOptions.getLimit().toString());
}
if (listOptions.getContinue() != null) {
urlBuilder.addQueryParameter("continue", listOptions.getContinue());
}
if (listOptions.getFieldSelector() != null) {
urlBuilder.addQueryParameter("fieldSelector", listOptions.getFieldSelector());
}
if (listOptions.getLabelSelector() != null) {
urlBuilder.addQueryParameter("labelSelector", listOptions.getLabelSelector());
}
if (listOptions.getResourceVersion() != null) {
urlBuilder.addQueryParameter("resourceVersion", listOptions.getResourceVersion());
}
if (listOptions.getTimeoutSeconds() != null) {
urlBuilder.addQueryParameter("timeoutSeconds", listOptions.getTimeoutSeconds().toString());
}
if (listOptions.getAllowWatchBookmarks() != null) {
urlBuilder.addQueryParameter("allowWatchBookmarks", listOptions.getAllowWatchBookmarks().toString());
}
if (listOptions.getWatch() != null) {
urlBuilder.addQueryParameter(WATCH, listOptions.getWatch().toString());
}
return urlBuilder.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy