Skip to content

Commit 79d729d

Browse files
committed
first working version of operator sdk
1 parent 403f96c commit 79d729d

File tree

6 files changed

+218
-0
lines changed

6 files changed

+218
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target/

pom.xml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>jkube</groupId>
7+
<artifactId>operator-sdk</artifactId>
8+
<version>0.0.1-SNAPSHOT</version>
9+
<name>operator-sdk</name>
10+
<description>Framework for implementing Kubernetes operators</description>
11+
12+
<properties>
13+
<java.version>11</java.version>
14+
</properties>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>io.fabric8</groupId>
19+
<artifactId>openshift-client</artifactId>
20+
<version>4.2.2</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.apache.commons</groupId>
24+
<artifactId>commons-lang3</artifactId>
25+
<version>3.8.1</version>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.slf4j</groupId>
29+
<artifactId>slf4j-api</artifactId>
30+
<version>1.7.26</version>
31+
</dependency>
32+
</dependencies>
33+
34+
<build>
35+
<plugins>
36+
<plugin>
37+
<groupId>org.apache.maven.plugins</groupId>
38+
<artifactId>maven-compiler-plugin</artifactId>
39+
<version>3.8.1</version>
40+
<configuration>
41+
<source>11</source>
42+
<target>11</target>
43+
</configuration>
44+
</plugin>
45+
</plugins>
46+
</build>
47+
48+
</project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package jkube.operator;
2+
3+
import io.fabric8.kubernetes.api.model.Doneable;
4+
import io.fabric8.kubernetes.client.CustomResource;
5+
import io.fabric8.kubernetes.client.CustomResourceDoneable;
6+
import io.fabric8.kubernetes.client.CustomResourceList;
7+
8+
public interface CustomResourceController<R extends CustomResource, L extends CustomResourceList<R>, D extends CustomResourceDoneable<R>> {
9+
10+
void deleteResource(R resource);
11+
12+
R createOrUpdateResource(R resource);
13+
14+
Class<R> getCustomResourceClass();
15+
16+
Class<L> getCustomResourceListClass();
17+
18+
Class<D> getCustomResourceDoneableClass();
19+
20+
String getApiVersion();
21+
22+
String getCrdVersion();
23+
24+
25+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package jkube.operator;
2+
3+
import io.fabric8.kubernetes.client.*;
4+
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
5+
import io.fabric8.kubernetes.client.dsl.Resource;
6+
import io.fabric8.kubernetes.client.dsl.internal.CustomResourceOperationsImpl;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
public class EventDispatcher<R extends CustomResource,
11+
L extends CustomResourceList<R>,
12+
D extends CustomResourceDoneable<R>> implements Watcher<R> {
13+
14+
private final static Logger log = LoggerFactory.getLogger(EventDispatcher.class);
15+
16+
private final CustomResourceController<R, L, D> controller;
17+
private final NonNamespaceOperation<R, L, D, Resource<R, D>> resourceClient;
18+
19+
public EventDispatcher(CustomResourceController<R, L, D> controller,
20+
NonNamespaceOperation<R, L, D, Resource<R, D>> resourceClient) {
21+
this.controller = controller;
22+
this.resourceClient = resourceClient;
23+
}
24+
25+
public void eventReceived(Action action, R resource) {
26+
log.info("Action: {}, Event: {}", action, resource);
27+
28+
if (action == Action.MODIFIED || action == Action.ADDED) {
29+
if (markedForDeletion(resource)) {
30+
controller.deleteResource(resource);
31+
} else {
32+
R updatedResource = controller.createOrUpdateResource(resource);
33+
replace(updatedResource);
34+
}
35+
}
36+
37+
if (Action.ERROR == action) {
38+
log.error("Received error for resource: {}", resource.getMetadata().getName());
39+
}
40+
}
41+
42+
private void replace(R resource) {
43+
var resourceOperation = (CustomResourceOperationsImpl<R, L, D>) resourceClient;
44+
resourceOperation.lockResourceVersion(resource.getMetadata().getResourceVersion()).replace(resource);
45+
}
46+
47+
private boolean markedForDeletion(R resource) {
48+
return resource.getMetadata().getDeletionTimestamp() != null && !resource.getMetadata().getDeletionTimestamp().isEmpty();
49+
}
50+
51+
@Override
52+
public void onClose(KubernetesClientException e) {
53+
if (e != null) {
54+
log.error("Error: ", e);
55+
}
56+
}
57+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package jkube.operator;
2+
3+
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinitionList;
4+
import io.fabric8.kubernetes.client.*;
5+
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
6+
import io.fabric8.openshift.client.DefaultOpenShiftClient;
7+
import org.apache.commons.lang3.StringUtils;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
public class Operator {
15+
16+
private KubernetesClient k8sClient;
17+
18+
private Map<CustomResourceController, EventDispatcher> controllers = new HashMap<>();
19+
20+
private final static Logger log = LoggerFactory.getLogger(Operator.class);
21+
22+
public static Operator initializeFromEnvironment() {
23+
ConfigBuilder config = new ConfigBuilder().withTrustCerts(true);
24+
if (StringUtils.isNotBlank(System.getenv("K8S_MASTER_URL"))) {
25+
config.withMasterUrl(System.getenv("K8S_MASTER_URL"));
26+
}
27+
if (StringUtils.isNoneBlank(System.getenv("K8S_USERNAME"), System.getenv("K8S_PASSWORD"))) {
28+
config.withUsername(System.getenv("K8S_USERNAME")).withPassword(System.getenv("K8S_PASSWORD"));
29+
}
30+
var client = new DefaultOpenShiftClient(config.build());
31+
32+
33+
Operator operator = new Operator();
34+
operator.k8sClient = client;
35+
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
36+
log.error("Error", e);
37+
operator.stop();
38+
});
39+
return operator;
40+
}
41+
42+
private Operator() {
43+
}
44+
45+
public <R extends CustomResource, L extends CustomResourceList<R>, D extends CustomResourceDoneable<R>>
46+
void registerController(CustomResourceController<R, L, D> controller) throws OperatorException {
47+
var resClass = controller.getCustomResourceClass();
48+
KubernetesDeserializer.registerCustomKind(controller.getApiVersion(),
49+
resClass.getSimpleName(), resClass);
50+
51+
CustomResourceDefinitionList crdList = k8sClient.customResourceDefinitions().list();
52+
var crd = crdList.getItems().stream()
53+
.filter(c -> resClass.getSimpleName().equals(c.getSpec().getNames().getKind()) &&
54+
controller.getCrdVersion().equals(c.getSpec().getVersion()))
55+
.findFirst();
56+
57+
if (crd.isPresent()) {
58+
var client = k8sClient.customResources(crd.get(), resClass, controller.getCustomResourceListClass(),
59+
controller.getCustomResourceDoneableClass());
60+
var eventDispatcher = new EventDispatcher<>(controller, client);
61+
client.watch(eventDispatcher);
62+
controllers.put(controller, eventDispatcher);
63+
log.info("Registered Controller '" + controller.getClass().getSimpleName() + "' for CRD '" + controller.getCustomResourceClass().getName() + "'");
64+
} else {
65+
throw new OperatorException("CRD '" + resClass.getSimpleName() + "' with version '" + controller.getCrdVersion() + "' not found");
66+
}
67+
}
68+
69+
public void stop() {
70+
k8sClient.close();
71+
}
72+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package jkube.operator;
2+
3+
public class OperatorException extends RuntimeException {
4+
5+
public OperatorException() {
6+
}
7+
8+
public OperatorException(String message) {
9+
super(message);
10+
}
11+
12+
public OperatorException(String message, Throwable cause) {
13+
super(message, cause);
14+
}
15+
}

0 commit comments

Comments
 (0)