Skip to content

Commit 8983918

Browse files
authored
Merge pull request #235 from java-operator-sdk/event-sources
Event sources M1
2 parents f28a8cd + d4cda3d commit 8983918

File tree

70 files changed

+1861
-914
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1861
-914
lines changed

DECISION_LOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Decision Log
2+
3+
4+
## Event Sources
5+
6+
### 1. Move Retries to an Abstract Controller.
7+
8+
The original idea was to explicitly support retry in the scheduler. However, this turned out to complicate the algorithm
9+
in case of event sources. Mostly it would be harder to manage the buffer, and the other event sources, thus what
10+
does it mean for other event sources that there was a failed controller execution? Probably it would be better to
11+
manage this in an abstract controller, and just use the "reprocess event-source" source in case of an error.
12+

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ The Controller implements the business logic and describes all the classes neede
8282
public class WebServerController implements ResourceController<WebServer> {
8383

8484
@Override
85-
public boolean deleteResource(CustomService resource, Context<WebServer> context) {
85+
public DeleteControl deleteResource(CustomService resource, Context<WebServer> context) {
8686
// ... your logic ...
87-
return true;
87+
return DeleteControl.DEFAULT_DELETE;
8888
}
8989

9090
// Return the changed resource, so it gets updated. See javadoc for details.

docs/event-design.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<mxfile host="app.diagrams.net" modified="2020-11-11T11:37:34.373Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36" etag="RN6pgE0b8M6KAgj9wCQV" version="13.8.8" type="device"><diagram id="YHX19W9oFLqZG9bJU0nv" name="Page-1">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</diagram></mxfile>

operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerToCustomResourceMappingsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717
class ControllerToCustomResourceMappingsProvider {
18-
private static final Logger log = LoggerFactory.getLogger(ControllerUtils.class);
18+
private static final Logger log = LoggerFactory.getLogger(ControllerToCustomResourceMappingsProvider.class);
1919

2020
static Map<Class<? extends ResourceController>, Class<? extends CustomResource>> provide(final String resourcePath) {
2121
Map<Class<? extends ResourceController>, Class<? extends CustomResource>> controllerToCustomResourceMappings = new HashMap();

operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerUtils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ static String getCrdName(ResourceController controller) {
5151
return getAnnotation(controller).crdName();
5252
}
5353

54-
5554
public static <T extends CustomResource> Class<? extends CustomResourceDoneable<T>>
5655
getCustomResourceDoneableClass(ResourceController<T> controller) {
5756
try {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.fabric8.kubernetes.client.Watcher;
5+
import io.javaoperatorsdk.operator.processing.event.Event;
6+
import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent;
7+
8+
import java.util.List;
9+
10+
public class EventListUtils {
11+
12+
public static boolean containsCustomResourceDeletedEvent(List<Event> events) {
13+
return events.stream().anyMatch(e -> {
14+
if (e instanceof CustomResourceEvent) {
15+
return ((CustomResourceEvent) e).getAction() == Watcher.Action.DELETED;
16+
} else {
17+
return false;
18+
}
19+
});
20+
}
21+
}
Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package io.javaoperatorsdk.operator;
22

3-
import io.javaoperatorsdk.operator.api.ResourceController;
4-
import io.javaoperatorsdk.operator.processing.EventDispatcher;
5-
import io.javaoperatorsdk.operator.processing.EventScheduler;
6-
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;
7-
import io.javaoperatorsdk.operator.processing.retry.Retry;
83
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;
94
import io.fabric8.kubernetes.client.CustomResource;
105
import io.fabric8.kubernetes.client.CustomResourceDoneable;
@@ -14,13 +9,22 @@
149
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
1510
import io.fabric8.kubernetes.client.dsl.internal.CustomResourceOperationsImpl;
1611
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
12+
import io.javaoperatorsdk.operator.api.ResourceController;
13+
import io.javaoperatorsdk.operator.processing.EventDispatcher;
14+
import io.javaoperatorsdk.operator.processing.DefaultEventHandler;
15+
import io.javaoperatorsdk.operator.processing.CustomResourceCache;
16+
import io.javaoperatorsdk.operator.processing.event.DefaultEventSourceManager;
17+
import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEventSource;
1718
import org.slf4j.Logger;
1819
import org.slf4j.LoggerFactory;
1920

2021
import java.util.Arrays;
2122
import java.util.HashMap;
2223
import java.util.Map;
2324

25+
import static io.javaoperatorsdk.operator.ControllerUtils.*;
26+
27+
2428
@SuppressWarnings("rawtypes")
2529
public class Operator {
2630

@@ -34,58 +38,61 @@ public Operator(KubernetesClient k8sClient) {
3438

3539

3640
public <R extends CustomResource> void registerControllerForAllNamespaces(ResourceController<R> controller) throws OperatorException {
37-
registerController(controller, true, GenericRetry.defaultLimitedExponentialRetry());
38-
}
39-
40-
public <R extends CustomResource> void registerControllerForAllNamespaces(ResourceController<R> controller, Retry retry) throws OperatorException {
41-
registerController(controller, true, retry);
41+
registerController(controller, true);
4242
}
4343

4444
public <R extends CustomResource> void registerController(ResourceController<R> controller, String... targetNamespaces) throws OperatorException {
45-
registerController(controller, false, GenericRetry.defaultLimitedExponentialRetry(), targetNamespaces);
46-
}
47-
48-
public <R extends CustomResource> void registerController(ResourceController<R> controller, Retry retry, String... targetNamespaces) throws OperatorException {
49-
registerController(controller, false, retry, targetNamespaces);
45+
registerController(controller, false, targetNamespaces);
5046
}
5147

5248
@SuppressWarnings("rawtypes")
5349
private <R extends CustomResource> void registerController(ResourceController<R> controller,
54-
boolean watchAllNamespaces, Retry retry, String... targetNamespaces) throws OperatorException {
55-
Class<R> resClass = ControllerUtils.getCustomResourceClass(controller);
50+
boolean watchAllNamespaces, String... targetNamespaces) throws OperatorException {
51+
Class<R> resClass = getCustomResourceClass(controller);
5652
CustomResourceDefinitionContext crd = getCustomResourceDefinitionForController(controller);
5753
KubernetesDeserializer.registerCustomKind(crd.getVersion(), crd.getKind(), resClass);
5854
String finalizer = ControllerUtils.getFinalizer(controller);
5955
MixedOperation client = k8sClient.customResources(crd, resClass, CustomResourceList.class, ControllerUtils.getCustomResourceDoneableClass(controller));
6056
EventDispatcher eventDispatcher = new EventDispatcher(controller,
61-
finalizer, new EventDispatcher.CustomResourceFacade(client), ControllerUtils.getGenerationEventProcessing(controller));
62-
EventScheduler eventScheduler = new EventScheduler(eventDispatcher, retry);
63-
registerWatches(controller, client, resClass, watchAllNamespaces, targetNamespaces, eventScheduler);
64-
}
57+
finalizer, new EventDispatcher.CustomResourceFacade(client));
6558

6659

67-
private <R extends CustomResource> void registerWatches(ResourceController<R> controller, MixedOperation client,
68-
Class<R> resClass,
69-
boolean watchAllNamespaces, String[] targetNamespaces, EventScheduler eventScheduler) {
70-
71-
CustomResourceOperationsImpl crClient = (CustomResourceOperationsImpl) client;
72-
if (watchAllNamespaces) {
73-
crClient.inAnyNamespace().watch(eventScheduler);
74-
} else if (targetNamespaces.length == 0) {
75-
client.watch(eventScheduler);
76-
} else {
77-
for (String targetNamespace : targetNamespaces) {
78-
crClient.inNamespace(targetNamespace).watch(eventScheduler);
79-
log.debug("Registered controller for namespace: {}", targetNamespace);
80-
}
81-
}
60+
CustomResourceCache customResourceCache = new CustomResourceCache();
61+
DefaultEventHandler defaultEventHandler = new DefaultEventHandler(customResourceCache, eventDispatcher, controller.getClass().getName());
62+
DefaultEventSourceManager eventSourceManager = new DefaultEventSourceManager(defaultEventHandler);
63+
defaultEventHandler.setDefaultEventSourceManager(eventSourceManager);
64+
eventDispatcher.setEventSourceManager(eventSourceManager);
65+
8266
customResourceClients.put(resClass, (CustomResourceOperationsImpl) client);
67+
68+
controller.init(eventSourceManager);
69+
CustomResourceEventSource customResourceEventSource
70+
= createCustomResourceEventSource(client, customResourceCache, watchAllNamespaces, targetNamespaces,
71+
defaultEventHandler, ControllerUtils.getGenerationEventProcessing(controller));
72+
eventSourceManager.registerCustomResourceEventSource(customResourceEventSource);
73+
74+
8375
log.info("Registered Controller: '{}' for CRD: '{}' for namespaces: {}", controller.getClass().getSimpleName(),
8476
resClass, targetNamespaces.length == 0 ? "[all/client namespace]" : Arrays.toString(targetNamespaces));
8577
}
8678

79+
private CustomResourceEventSource createCustomResourceEventSource(MixedOperation client,
80+
CustomResourceCache customResourceCache,
81+
boolean watchAllNamespaces,
82+
String[] targetNamespaces,
83+
DefaultEventHandler defaultEventHandler,
84+
boolean generationAware) {
85+
CustomResourceEventSource customResourceEventSource = watchAllNamespaces ?
86+
CustomResourceEventSource.customResourceEventSourceForAllNamespaces(customResourceCache, client, generationAware) :
87+
CustomResourceEventSource.customResourceEventSourceForTargetNamespaces(customResourceCache, client, targetNamespaces, generationAware);
88+
89+
customResourceEventSource.setEventHandler(defaultEventHandler);
90+
91+
return customResourceEventSource;
92+
}
93+
8794
private CustomResourceDefinitionContext getCustomResourceDefinitionForController(ResourceController controller) {
88-
String crdName = ControllerUtils.getCrdName(controller);
95+
String crdName = getCrdName(controller);
8996
CustomResourceDefinition customResourceDefinition = k8sClient.customResourceDefinitions().withName(crdName).get();
9097
if (customResourceDefinition == null) {
9198
throw new OperatorException("Cannot find Custom Resource Definition with name: " + crdName);
@@ -103,11 +110,4 @@ public Map<Class<? extends CustomResource>, CustomResourceOperationsImpl> getCus
103110
return customResourceClients.get(customResourceClass);
104111
}
105112

106-
private String getKind(CustomResourceDefinition crd) {
107-
return crd.getSpec().getNames().getKind();
108-
}
109-
110-
private String getApiVersion(CustomResourceDefinition crd) {
111-
return crd.getSpec().getGroup() + "/" + crd.getSpec().getVersion();
112-
}
113113
}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
package io.javaoperatorsdk.operator.api;
22

33
import io.fabric8.kubernetes.client.CustomResource;
4+
import io.javaoperatorsdk.operator.processing.event.Event;
5+
import io.javaoperatorsdk.operator.processing.event.EventList;
6+
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
7+
8+
import java.util.List;
49

510
public interface Context<T extends CustomResource> {
611

7-
RetryInfo retryInfo();
12+
EventSourceManager getEventSourceManager();
13+
14+
EventList getEvents();
815

916
}
Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
package io.javaoperatorsdk.operator.api;
22

33
import io.fabric8.kubernetes.client.CustomResource;
4+
import io.javaoperatorsdk.operator.processing.event.Event;
5+
import io.javaoperatorsdk.operator.processing.event.EventList;
6+
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
7+
8+
import java.util.List;
49

510
public class DefaultContext<T extends CustomResource> implements Context<T> {
611

7-
private final RetryInfo retryInfo;
12+
private final EventList events;
13+
private final EventSourceManager eventSourceManager;
814

9-
public DefaultContext(RetryInfo retryInfo) {
10-
this.retryInfo = retryInfo;
15+
public DefaultContext(EventSourceManager eventSourceManager, EventList events) {
16+
this.events = events;
17+
this.eventSourceManager = eventSourceManager;
1118
}
1219

1320
@Override
14-
public RetryInfo retryInfo() {
15-
return retryInfo;
21+
public EventSourceManager getEventSourceManager() {
22+
return eventSourceManager;
1623
}
24+
25+
@Override
26+
public EventList getEvents() {
27+
return events;
28+
}
29+
1730
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.javaoperatorsdk.operator.api;
2+
3+
public enum DeleteControl {
4+
5+
DEFAULT_DELETE,
6+
NO_FINALIZER_REMOVAL
7+
8+
}

0 commit comments

Comments
 (0)