io.kubernetes.client.informer.impl.DefaultSharedIndexInformer Maven / Gradle / Ivy
/*
Copyright 2020 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.informer.impl;
import io.kubernetes.client.common.KubernetesListObject;
import io.kubernetes.client.common.KubernetesObject;
import io.kubernetes.client.informer.ListerWatcher;
import io.kubernetes.client.informer.ResourceEventHandler;
import io.kubernetes.client.informer.SharedIndexInformer;
import io.kubernetes.client.informer.TransformFunc;
import io.kubernetes.client.informer.cache.Cache;
import io.kubernetes.client.informer.cache.Controller;
import io.kubernetes.client.informer.cache.DeltaFIFO;
import io.kubernetes.client.informer.cache.Indexer;
import io.kubernetes.client.informer.cache.ProcessorListener;
import io.kubernetes.client.informer.cache.SharedProcessor;
import io.kubernetes.client.util.Threads;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DefaultSharedIndexInformer<
ApiType extends KubernetesObject, ApiListType extends KubernetesListObject>
implements SharedIndexInformer {
private static final Logger log = LoggerFactory.getLogger(DefaultSharedIndexInformer.class);
private static final long MINIMUM_RESYNC_PERIOD_MILLIS = 1000L;
// resyncCheckPeriod is how often we want the reflector's resync timer to fire so it can call
// shouldResync to check if any of our listeners need a resync.
private long resyncCheckPeriodMillis;
// defaultEventHandlerResyncPeriod is the default resync period for any handlers added via
// AddEventHandler (i.e. they don't specify one and just want to use the shared informer's
// default
// value).
private long defaultEventHandlerResyncPeriod;
private Indexer indexer;
private SharedProcessor processor;
private Controller controller;
private Thread controllerThread;
private TransformFunc transform;
private volatile boolean started = false;
private volatile boolean stopped = false;
public DefaultSharedIndexInformer(
Class apiTypeClass,
ListerWatcher listerWatcher,
long resyncPeriod) {
this(apiTypeClass, listerWatcher, resyncPeriod, new Cache<>());
}
public DefaultSharedIndexInformer(
Class apiTypeClass,
ListerWatcher listerWatcher,
long resyncPeriod,
Cache cache) {
this(
apiTypeClass,
listerWatcher,
resyncPeriod,
// down-casting should be safe here because one delta FIFO instance only serves one
// resource
// type
new DeltaFIFO(
(Function) cache.getKeyFunc(),
(Cache) cache),
cache,
null);
}
public DefaultSharedIndexInformer(
Class apiTypeClass,
ListerWatcher listerWatcher,
long resyncPeriod,
Cache cache,
BiConsumer, Throwable> exceptionHandler) {
this(
apiTypeClass,
listerWatcher,
resyncPeriod,
// down-casting should be safe here because one delta FIFO instance only serves one
// resource
// type
new DeltaFIFO(
(Function) cache.getKeyFunc(),
(Cache) cache),
cache,
exceptionHandler);
}
public DefaultSharedIndexInformer(
Class apiTypeClass,
ListerWatcher listerWatcher,
long resyncPeriod,
DeltaFIFO deltaFIFO,
Indexer indexer) {
this(apiTypeClass, listerWatcher, resyncPeriod, deltaFIFO, indexer, null);
}
public DefaultSharedIndexInformer(
Class apiTypeClass,
ListerWatcher listerWatcher,
long resyncPeriod,
DeltaFIFO deltaFIFO,
Indexer indexer,
BiConsumer, Throwable> exceptionHandler) {
this.resyncCheckPeriodMillis = resyncPeriod;
this.defaultEventHandlerResyncPeriod = resyncPeriod;
this.processor = new SharedProcessor<>();
this.indexer = indexer;
this.controller =
new Controller<>(
apiTypeClass,
deltaFIFO,
listerWatcher,
this::handleDeltas,
processor::shouldResync,
resyncCheckPeriodMillis,
exceptionHandler);
ThreadFactory threadFactory =
Threads.threadFactory("informer-controller-" + apiTypeClass.getSimpleName());
controllerThread = threadFactory.newThread(controller::run);
}
/** add event callback */
@Override
public void addEventHandler(ResourceEventHandler handler) {
addEventHandlerWithResyncPeriod(handler, defaultEventHandlerResyncPeriod);
}
/** add event callback with a resync period */
@Override
public void addEventHandlerWithResyncPeriod(
ResourceEventHandler handler, long resyncPeriodMillis) {
if (stopped) {
log.info(
"DefaultSharedIndexInformer#Handler was not added to shared informer because it has stopped already");
return;
}
if (resyncPeriodMillis > 0) {
if (resyncPeriodMillis < MINIMUM_RESYNC_PERIOD_MILLIS) {
log.warn(
"DefaultSharedIndexInformer#resyncPeriod {} is too small. Changing it to the minimum allowed rule of {}",
resyncPeriodMillis,
MINIMUM_RESYNC_PERIOD_MILLIS);
resyncPeriodMillis = MINIMUM_RESYNC_PERIOD_MILLIS;
}
if (resyncPeriodMillis < this.resyncCheckPeriodMillis) {
if (started) {
log.warn(
"DefaultSharedIndexInformer#resyncPeriod {} is smaller than resyncCheckPeriod {} and the informer has already started. Changing it to {}",
resyncPeriodMillis,
resyncCheckPeriodMillis,
resyncCheckPeriodMillis);
resyncPeriodMillis = resyncCheckPeriodMillis;
} else {
// if the event handler's resyncPeriod is smaller than the current
// resyncCheckPeriod,
// update resyncCheckPeriod to match resyncPeriod and adjust the resync periods
// of all
// the listeners accordingly
this.resyncCheckPeriodMillis = resyncPeriodMillis;
}
}
}
ProcessorListener listener =
new ProcessorListener(
handler, determineResyncPeriod(resyncCheckPeriodMillis, this.resyncCheckPeriodMillis));
if (!started) {
this.processor.addListener(listener);
return;
}
this.processor.addAndStartListener(listener);
List objectList = this.indexer.list();
for (Object item : objectList) {
listener.add(new ProcessorListener.AddNotification(item));
}
}
@Override
public String lastSyncResourceVersion() {
if (!started) {
return "";
}
return this.controller.lastSyncResourceVersion();
}
@Override
public void setTransform(TransformFunc transformFunc) {
if (started) {
throw new IllegalStateException("cannot set transform func to a running informer");
}
this.transform = transformFunc;
}
@Override
public void run() {
if (started) {
return;
}
started = true;
this.processor.run();
controllerThread.start();
}
@Override
public void stop() {
if (!started) {
return;
}
stopped = true;
controller.stop();
controllerThread.interrupt();
processor.stop();
}
@Override
public boolean hasSynced() {
// Waiting for controller initialize.
// Because informer.run() and hasSynced() are called by different threads.
return controller != null && this.controller.hasSynced();
}
/**
* handleDeltas handles deltas and call processor distribute.
*
* @param deltas deltas
*/
public void handleDeltas(Deque> deltas) {
if (CollectionUtils.isEmpty(deltas)) {
return;
}
// from oldest to newest
for (MutablePair delta : deltas) {
DeltaFIFO.DeltaType deltaType = delta.getLeft();
KubernetesObject obj = delta.getRight();
if (transform != null) {
obj = transform.transform(obj);
}
switch (deltaType) {
case Sync:
case Added:
case Updated:
boolean isSync = deltaType == DeltaFIFO.DeltaType.Sync;
Object oldObj = this.indexer.get((ApiType) obj);
if (oldObj != null) {
this.indexer.update((ApiType) obj);
this.processor.distribute(
new ProcessorListener.UpdateNotification(oldObj, obj), isSync);
} else {
this.indexer.add((ApiType) obj);
this.processor.distribute(new ProcessorListener.AddNotification(obj), isSync);
}
break;
case Deleted:
this.indexer.delete((ApiType) obj);
this.processor.distribute(new ProcessorListener.DeleteNotification(obj), false);
break;
}
}
}
@Override
public void addIndexers(Map>> indexers) {
if (started) {
throw new IllegalStateException("cannot add indexers to a running informer");
}
indexer.addIndexers(indexers);
}
@Override
public Indexer getIndexer() {
return this.indexer;
}
private long determineResyncPeriod(long desired, long check) {
if (desired == 0) {
return desired;
}
if (check == 0) {
return 0;
}
return Math.max(desired, check);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy