Skip to content

Commit 2385ed2

Browse files
authored
Merge pull request #288 from java-operator-sdk/client-v5
Upgrade to Fabric8's 5.0 client and Quarkus 1.11.0.Final
2 parents e26c31f + 22fcef3 commit 2385ed2

File tree

76 files changed

+356
-835
lines changed

Some content is hidden

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

76 files changed

+356
-835
lines changed

README.md

+38-26
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ Build Kubernetes Operators in Java without hassle. Inspired by [operator-sdk](ht
1515

1616
#### Features
1717
* Framework for handling Kubernetes API events
18-
* Registering Custom Resource watches
18+
* Automatic registration of Custom Resource watches
1919
* Retry action on failure
20-
* Smart event scheduling (only handle latest event for the same resource)
20+
* Smart event scheduling (only handle the latest event for the same resource)
2121

2222
Check out this [blog post](https://blog.container-solutions.com/a-deep-dive-into-the-java-operator-sdk)
2323
about the non-trivial yet common problems needed to be solved for every operator.
@@ -44,6 +44,37 @@ about the non-trivial yet common problems needed to be solved for every operator
4444
You can (will) find detailed documentation [here](docs/DOCS.md).
4545
Note that these docs are currently in progress.
4646

47+
> :warning: 1.7.0 Upgrade
48+
> The 1.7.0 upgrade comes with big changes due to the update to the 5.0.0 version of the fabric8
49+
> Kubernetes client. While this should improve the user experience quite nicely, there are a couple
50+
> of things to be aware of when upgrading from a previous version as detailed below.
51+
52+
##### Overview of the 1.7.0 changes
53+
54+
- `Doneable` classes have been removed along with all the involved complexity
55+
- `Controller` annotation has been simplified: the `crdName` field has been removed as that value is
56+
computed from the associated custom resource implementation
57+
- Custom Resource implementation classes now need to be annotated with `Group` and `Version`
58+
annotations so that they can be identified properly. Optionally, they can also be annotated with
59+
`Kind` (if the name of the implementation class doesn't match the desired kind) and `Plural` if
60+
the plural version cannot be automatically computed (or the default computed version doesn't match
61+
your expectations).
62+
- The `CustomResource` class that needs to be extended is now parameterized with spec and status
63+
types, so you can have an empty default implementation that does what you'd expect. If you don't
64+
need a status, using `Void` for the associated type should work.
65+
- Custom Resources that are namespace-scoped need to implement the `Namespaced` interface so that
66+
the client can generate the proper URLs. This means, in particular, that `CustomResource`
67+
implementations that do **not** implement `Namespaced` are considered cluster-scoped. As a
68+
consequence, the `isClusterScoped` method/field has been removed from the appropriate
69+
classes (`Controller` annotation, in particular) as this is now inferred from the `CustomResource`
70+
type associated with your `Controller`.
71+
72+
Many of these changes might not be immediately apparent but will result in `404` errors when
73+
connecting to the cluster. Please check that the Custom Resource implementations are properly
74+
annotated and that the value corresponds to your CRD manifest. If the namespace appear to be missing
75+
in your request URL, don't forget that namespace-scoped Custom Resources need to implement
76+
the `Namescaped` interface.
77+
4778
#### Usage
4879

4980
We have several sample Operators under the [samples](samples) directory:
@@ -52,6 +83,7 @@ Implemented with and without Spring Boot support. The two samples share the comm
5283
* *webserver*: More realistic example creating an nginx webserver from a Custom Resource containing html code.
5384
* *mysql-schema*: Operator managing schemas in a MySQL database
5485
* *spring-boot-plain/auto-config*: Samples showing integration with Spring Boot.
86+
* *quarkus*: Minimal application showing automatic configuration / injection of Operator / Controllers.
5587

5688
Add [dependency](https://search.maven.org/search?q=a:operator-framework) to your project with Maven:
5789

@@ -89,7 +121,7 @@ public class Runner {
89121
The Controller implements the business logic and describes all the classes needed to handle the CRD.
90122

91123
```java
92-
@Controller(crdName = "webservers.sample.javaoperatorsdk")
124+
@Controller
93125
public class WebServerController implements ResourceController<WebServer> {
94126

95127
@Override
@@ -110,28 +142,9 @@ public class WebServerController implements ResourceController<WebServer> {
110142
A sample custom resource POJO representation
111143

112144
```java
113-
public class WebServer extends CustomResource {
114-
115-
private WebServerSpec spec;
116-
117-
private WebServerStatus status;
118-
119-
public WebServerSpec getSpec() {
120-
return spec;
121-
}
122-
123-
public void setSpec(WebServerSpec spec) {
124-
this.spec = spec;
125-
}
126-
127-
public WebServerStatus getStatus() {
128-
return status;
129-
}
130-
131-
public void setStatus(WebServerStatus status) {
132-
this.status = status;
133-
}
134-
}
145+
@Group("sample.javaoperatorsdk")
146+
@Version("v1")
147+
public class WebServer extends CustomResource<WebServerSpec, WebServerStatus> implements Namespaced {}
135148

136149
public class WebServerSpec {
137150

@@ -183,7 +196,6 @@ public class QuarkusOperator implements QuarkusApplication {
183196
public int run(String... args) throws Exception {
184197
final var config = configuration.getConfigurationFor(new CustomServiceController(client));
185198
System.out.println("CR class: " + config.getCustomResourceClass());
186-
System.out.println("Doneable class = " + config.getDoneableClass());
187199

188200
Quarkus.waitForExit();
189201
return 0;

operator-framework-core/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>io.javaoperatorsdk</groupId>
88
<artifactId>java-operator-sdk</artifactId>
9-
<version>1.6.4-SNAPSHOT</version>
9+
<version>1.7.0-SNAPSHOT</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java

+13-46
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
package io.javaoperatorsdk.operator;
22

3-
import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition;
43
import io.fabric8.kubernetes.client.CustomResource;
5-
import io.fabric8.kubernetes.client.CustomResourceDoneable;
6-
import io.fabric8.kubernetes.client.CustomResourceList;
74
import io.fabric8.kubernetes.client.KubernetesClient;
85
import io.fabric8.kubernetes.client.dsl.MixedOperation;
9-
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
10-
import io.fabric8.kubernetes.client.dsl.internal.CustomResourceOperationsImpl;
11-
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
126
import io.javaoperatorsdk.operator.api.ResourceController;
137
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
148
import io.javaoperatorsdk.operator.processing.CustomResourceCache;
@@ -19,8 +13,6 @@
1913
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;
2014
import io.javaoperatorsdk.operator.processing.retry.Retry;
2115
import java.util.Arrays;
22-
import java.util.HashMap;
23-
import java.util.Map;
2416
import org.slf4j.Logger;
2517
import org.slf4j.LoggerFactory;
2618

@@ -30,8 +22,6 @@ public class Operator {
3022
private static final Logger log = LoggerFactory.getLogger(Operator.class);
3123
private final KubernetesClient k8sClient;
3224
private final ConfigurationService configurationService;
33-
private Map<Class<? extends CustomResource>, CustomResourceOperationsImpl> customResourceClients =
34-
new HashMap<>();
3525

3626
public Operator(KubernetesClient k8sClient, ConfigurationService configurationService) {
3727
this.k8sClient = k8sClient;
@@ -41,9 +31,18 @@ public Operator(KubernetesClient k8sClient, ConfigurationService configurationSe
4131
public <R extends CustomResource> void register(ResourceController<R> controller)
4232
throws OperatorException {
4333
final var configuration = configurationService.getConfigurationFor(controller);
44-
final var retry = GenericRetry.fromConfiguration(configuration.getRetryConfiguration());
45-
final var targetNamespaces = configuration.getNamespaces().toArray(new String[] {});
46-
registerController(controller, configuration.watchAllNamespaces(), retry, targetNamespaces);
34+
if (configuration == null) {
35+
log.warn(
36+
"Skipping registration of {} controller named {} because its configuration cannot be found.\n"
37+
+ "Known controllers are: {}",
38+
controller.getClass().getCanonicalName(),
39+
ControllerUtils.getNameFor(controller),
40+
configurationService.getKnownControllerNames());
41+
} else {
42+
final var retry = GenericRetry.fromConfiguration(configuration.getRetryConfiguration());
43+
final var targetNamespaces = configuration.getNamespaces().toArray(new String[] {});
44+
registerController(controller, configuration.watchAllNamespaces(), retry, targetNamespaces);
45+
}
4746
}
4847

4948
public <R extends CustomResource> void registerControllerForAllNamespaces(
@@ -76,12 +75,8 @@ private <R extends CustomResource> void registerController(
7675
throws OperatorException {
7776
final var configuration = configurationService.getConfigurationFor(controller);
7877
Class<R> resClass = configuration.getCustomResourceClass();
79-
CustomResourceDefinitionContext crd = getCustomResourceDefinitionForController(controller);
80-
KubernetesDeserializer.registerCustomKind(crd.getVersion(), crd.getKind(), resClass);
8178
String finalizer = configuration.getFinalizer();
82-
MixedOperation client =
83-
k8sClient.customResources(
84-
crd, resClass, CustomResourceList.class, configuration.getDoneableClass());
79+
MixedOperation client = k8sClient.customResources(resClass);
8580
EventDispatcher eventDispatcher =
8681
new EventDispatcher(
8782
controller, finalizer, new EventDispatcher.CustomResourceFacade(client));
@@ -95,8 +90,6 @@ private <R extends CustomResource> void registerController(
9590
defaultEventHandler.setEventSourceManager(eventSourceManager);
9691
eventDispatcher.setEventSourceManager(eventSourceManager);
9792

98-
customResourceClients.put(resClass, (CustomResourceOperationsImpl) client);
99-
10093
controller.init(eventSourceManager);
10194
CustomResourceEventSource customResourceEventSource =
10295
createCustomResourceEventSource(
@@ -137,30 +130,4 @@ private CustomResourceEventSource createCustomResourceEventSource(
137130

138131
return customResourceEventSource;
139132
}
140-
141-
private CustomResourceDefinitionContext getCustomResourceDefinitionForController(
142-
ResourceController controller) {
143-
final var crdName = configurationService.getConfigurationFor(controller).getCRDName();
144-
CustomResourceDefinition customResourceDefinition =
145-
k8sClient.customResourceDefinitions().withName(crdName).get();
146-
if (customResourceDefinition == null) {
147-
throw new OperatorException("Cannot find Custom Resource Definition with name: " + crdName);
148-
}
149-
CustomResourceDefinitionContext context =
150-
CustomResourceDefinitionContext.fromCrd(customResourceDefinition);
151-
return context;
152-
}
153-
154-
public Map<Class<? extends CustomResource>, CustomResourceOperationsImpl>
155-
getCustomResourceClients() {
156-
return customResourceClients;
157-
}
158-
159-
public <
160-
T extends CustomResource,
161-
L extends CustomResourceList<T>,
162-
D extends CustomResourceDoneable<T>>
163-
CustomResourceOperationsImpl<T, L, D> getCustomResourceClients(Class<T> customResourceClass) {
164-
return customResourceClients.get(customResourceClass);
165-
}
166133
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/Controller.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
String NULL = "";
1313

14-
String crdName();
15-
1614
String name() default NULL;
1715

1816
/**
@@ -28,7 +26,15 @@
2826
*/
2927
boolean generationAwareEventProcessing() default true;
3028

31-
boolean isClusterScoped() default false;
32-
29+
/**
30+
* Specified which namespaces this Controller monitors for custom resources events. If no
31+
* namespace is specified then the controller will monitor the namespace it is deployed in (or the
32+
* namespace to which the Kubernetes client is connected to). To specify that the controller needs
33+
* to monitor all namespaces, add {@link
34+
* io.javaoperatorsdk.operator.api.config.ControllerConfiguration#WATCH_ALL_NAMESPACES_MARKER} to
35+
* this field.
36+
*
37+
* @return the list of namespaces this controller monitors
38+
*/
3339
String[] namespaces() default {};
3440
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.javaoperatorsdk.operator.ControllerUtils;
55
import io.javaoperatorsdk.operator.api.ResourceController;
66
import java.util.Map;
7+
import java.util.Set;
78
import java.util.concurrent.ConcurrentHashMap;
89

910
public abstract class AbstractConfigurationService implements ConfigurationService {
@@ -35,4 +36,9 @@ public <R extends CustomResource> ControllerConfiguration<R> getConfigurationFor
3536
ResourceController<R> controller) {
3637
return configurations.get(ControllerUtils.getNameFor(controller));
3738
}
39+
40+
@Override
41+
public Set<String> getKnownControllerNames() {
42+
return configurations.keySet();
43+
}
3844
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.fabric8.kubernetes.client.Config;
44
import io.fabric8.kubernetes.client.CustomResource;
55
import io.javaoperatorsdk.operator.api.ResourceController;
6+
import java.util.Set;
67

78
public interface ConfigurationService {
89

@@ -12,4 +13,6 @@ <R extends CustomResource> ControllerConfiguration<R> getConfigurationFor(
1213
default Config getClientConfiguration() {
1314
return Config.autoConfigure(null);
1415
}
16+
17+
Set<String> getKnownControllerNames();
1518
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java

-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.javaoperatorsdk.operator.api.config;
22

33
import io.fabric8.kubernetes.client.CustomResource;
4-
import io.fabric8.kubernetes.client.CustomResourceDoneable;
54
import java.util.Collections;
65
import java.util.Set;
76

@@ -19,14 +18,8 @@ public interface ControllerConfiguration<R extends CustomResource> {
1918

2019
Class<R> getCustomResourceClass();
2120

22-
Class<? extends CustomResourceDoneable<R>> getDoneableClass();
23-
2421
String getAssociatedControllerClassName();
2522

26-
default boolean isClusterScoped() {
27-
return false;
28-
}
29-
3023
default Set<String> getNamespaces() {
3124
return Collections.emptySet();
3225
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/EventDispatcher.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,9 @@ private void addFinalizerIfNotPresent(CustomResource resource) {
191191
// created to support unit testing
192192
public static class CustomResourceFacade {
193193

194-
private final MixedOperation<?, ?, ?, Resource<CustomResource, ?>> resourceOperation;
194+
private final MixedOperation<?, ?, Resource<CustomResource>> resourceOperation;
195195

196-
public CustomResourceFacade(
197-
MixedOperation<?, ?, ?, Resource<CustomResource, ?>> resourceOperation) {
196+
public CustomResourceFacade(MixedOperation<?, ?, Resource<CustomResource>> resourceOperation) {
198197
this.resourceOperation = resourceOperation;
199198
}
200199

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID;
44
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion;
55
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.markedForDeletion;
6-
import static java.net.HttpURLConnection.HTTP_GONE;
76

87
import io.fabric8.kubernetes.client.CustomResource;
9-
import io.fabric8.kubernetes.client.KubernetesClientException;
108
import io.fabric8.kubernetes.client.Watcher;
9+
import io.fabric8.kubernetes.client.WatcherException;
1110
import io.fabric8.kubernetes.client.dsl.MixedOperation;
1211
import io.fabric8.kubernetes.client.dsl.internal.CustomResourceOperationsImpl;
1312
import io.javaoperatorsdk.operator.ControllerUtils;
@@ -151,11 +150,11 @@ public void eventSourceDeRegisteredForResource(String customResourceUid) {
151150
}
152151

153152
@Override
154-
public void onClose(KubernetesClientException e) {
153+
public void onClose(WatcherException e) {
155154
if (e == null) {
156155
return;
157156
}
158-
if (e.getCode() == HTTP_GONE) {
157+
if (e.isHttpGone()) {
159158
log.warn("Received error for watch, will try to reconnect.", e);
160159
registerWatch();
161160
} else {
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,10 @@
11
package io.javaoperatorsdk.operator.sample.simple;
22

33
import io.fabric8.kubernetes.client.CustomResource;
4+
import io.fabric8.kubernetes.model.annotation.Group;
5+
import io.fabric8.kubernetes.model.annotation.Version;
46

5-
public class TestCustomResource extends CustomResource {
6-
7-
private TestCustomResourceSpec spec;
8-
9-
private TestCustomResourceStatus status;
10-
11-
public TestCustomResourceSpec getSpec() {
12-
return spec;
13-
}
14-
15-
public void setSpec(TestCustomResourceSpec spec) {
16-
this.spec = spec;
17-
}
18-
19-
public TestCustomResourceStatus getStatus() {
20-
return status;
21-
}
22-
23-
public void setStatus(TestCustomResourceStatus status) {
24-
this.status = status;
25-
}
26-
27-
@Override
28-
public String toString() {
29-
return "TestCustomResource{"
30-
+ "spec="
31-
+ spec
32-
+ ", status="
33-
+ status
34-
+ ", extendedFrom="
35-
+ super.toString()
36-
+ '}';
37-
}
38-
}
7+
@Group("sample.javaoperatorsdk.io")
8+
@Version("v1")
9+
public class TestCustomResource
10+
extends CustomResource<TestCustomResourceSpec, TestCustomResourceStatus> {}

0 commit comments

Comments
 (0)