Skip to content

Commit 31f8645

Browse files
committed
Remove need for doneable and list classes
Replace with default dummy classes, adapt tests
1 parent 0059029 commit 31f8645

File tree

21 files changed

+134
-107
lines changed

21 files changed

+134
-107
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@ The Controller implements the business logic and describes all the classes neede
4848

4949
```java
5050
@Controller(customResourceClass = WebServer.class,
51-
crdName = "webservers.sample.javaoperatorsdk",
52-
customResourceListClass = WebServerList.class,
53-
customResourceDonebaleClass = WebServerDoneable.class)
51+
crdName = "webservers.sample.javaoperatorsdk")
5452
public class WebServerController implements ResourceController<WebServer> {
5553

5654
@Override

operator-framework/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,10 @@
7373
<version>4.0.1</version>
7474
<scope>test</scope>
7575
</dependency>
76+
<dependency>
77+
<groupId>jboss</groupId>
78+
<artifactId>javassist</artifactId>
79+
<version>3.8.0.GA</version>
80+
</dependency>
7681
</dependencies>
7782
</project>

operator-framework/src/main/java/com/github/containersolutions/operator/ControllerUtils.java

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@
55
import io.fabric8.kubernetes.client.CustomResource;
66
import io.fabric8.kubernetes.client.CustomResourceDoneable;
77
import io.fabric8.kubernetes.client.CustomResourceList;
8+
import javassist.*;
9+
import org.apache.commons.lang3.StringUtils;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
813

914
class ControllerUtils {
1015

16+
private final static Logger log = LoggerFactory.getLogger(Operator.class);
17+
private static ClassPool pool;
18+
private static Class generatedCustomResourceDoneable;
19+
1120
static String getDefaultFinalizer(ResourceController controller) {
1221
return getAnnotation(controller).finalizerName();
1322
}
@@ -20,16 +29,94 @@ static String getCrdName(ResourceController controller) {
2029
return getAnnotation(controller).crdName();
2130
}
2231

23-
static <R extends CustomResource> Class<? extends CustomResourceList<R>> getCustomResourceListClass(ResourceController controller) {
24-
return (Class<? extends CustomResourceList<R>>) getAnnotation(controller).customResourceListClass();
32+
public static Class<? extends CustomResourceList> getCustomResourceListClass() {
33+
return CustomResourceList.class;
34+
}
35+
36+
public static <R extends CustomResource> Class<? extends CustomResourceDoneable<R>> getCustomResourceDoneableClass(ResourceController<R> controller) {
37+
return createCustomResourceDoneableClass(controller);
38+
}
39+
40+
private static <R extends CustomResource> Class<? extends CustomResourceDoneable<R>> createCustomResourceDoneableClass(ResourceController<R> controller) {
41+
pool = ClassPool.getDefault();
42+
pool.appendClassPath(new ClassClassPath(ControllerUtils.class));
43+
44+
String controllerName = StringUtils.substringAfterLast(controller.getClass().toString(), ".");
45+
Class<R> customResourceClass = (Class<R>) getAnnotation(controller).customResourceClass();
46+
47+
String className = getPackageName(customResourceClass.getName(), controllerName + "CustomResourceDoneable");
48+
if (isClassInPool(className)) {
49+
return generatedCustomResourceDoneable;
50+
}
51+
String superClassName = "io.fabric8.kubernetes.client.CustomResourceDoneable";
52+
CtClass customDoneable = getOrCreateClass(className, superClassName);
53+
54+
try {
55+
CtClass customResource = pool.get(customResourceClass.getName());
56+
CtClass function = pool.get("io.fabric8.kubernetes.api.builder.Function");
57+
CtClass[] argTypes = {customResource, function};
58+
CtConstructor ctConstructor = CtNewConstructor.make(argTypes, null, "super($1, $2);", customDoneable);
59+
customDoneable.addConstructor(ctConstructor);
60+
61+
} catch (CannotCompileException | NotFoundException e) {
62+
log.error("Error compiling constructor for CustomResourceDoneable class: {}", e);
63+
}
64+
65+
Class<? extends CustomResourceDoneable<R>> doneableClass = getClassFromCtClass(customDoneable);
66+
generatedCustomResourceDoneable = doneableClass;
67+
return doneableClass;
68+
}
69+
70+
private static boolean isClassInPool(String className) {
71+
try {
72+
pool.get(className);
73+
return true;
74+
} catch (NotFoundException e) {
75+
log.debug("Class {} not in pool", className);
76+
return false;
77+
}
2578
}
2679

27-
static <R extends CustomResource> Class<? extends CustomResourceDoneable<R>> getCustomResourceDonebaleClass(ResourceController controller) {
28-
return (Class<? extends CustomResourceDoneable<R>>) getAnnotation(controller).customResourceDoneableClass();
80+
private static CtClass getOrCreateClass(String className, String superClassName){
81+
CtClass customClass;
82+
try {
83+
customClass = pool.get(className);
84+
customClass.defrost();
85+
} catch (NotFoundException ce) {
86+
log.info("Class not found, creating new: {}", className);
87+
CtClass superClass = null;
88+
try {
89+
superClass = pool.get(superClassName);
90+
} catch (NotFoundException sce) {
91+
log.error("Error getting superClass: {}", sce);
92+
}
93+
customClass = pool.makeClass(className, superClass);
94+
}
95+
return customClass;
2996
}
3097

3198
private static Controller getAnnotation(ResourceController controller) {
3299
return controller.getClass().getAnnotation(Controller.class);
33100
}
34101

35-
}
102+
private static String getPackageName(String customResourceName, String newClassName) {
103+
CtClass customResource = null;
104+
try {
105+
customResource = pool.get(customResourceName);
106+
} catch (NotFoundException e) {
107+
log.error("Error getting class: {}", e);
108+
}
109+
String packageName = customResource != null ? customResource.getPackageName() : "";
110+
return packageName + "." + newClassName;
111+
}
112+
113+
private static Class getClassFromCtClass(CtClass customCtClass) {
114+
Class customClass = null;
115+
try {
116+
customClass = customCtClass.toClass();
117+
} catch (CannotCompileException e) {
118+
log.error("Error transforming CtClass to Class: {}", e);
119+
}
120+
return customClass;
121+
}
122+
}

operator-framework/src/main/java/com/github/containersolutions/operator/Operator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ private <R extends CustomResource> void registerController(ResourceController<R>
5959
CustomResourceDefinition crd = getCustomResourceDefinitionForController(controller);
6060
KubernetesDeserializer.registerCustomKind(getApiVersion(crd), getKind(crd), resClass);
6161

62-
Class<? extends CustomResourceList<R>> list = getCustomResourceListClass(controller);
63-
Class<? extends CustomResourceDoneable<R>> doneable = getCustomResourceDonebaleClass(controller);
62+
Class<? extends CustomResourceList<R>> list = (Class<? extends CustomResourceList<R>>) getCustomResourceListClass();
63+
64+
Class<? extends CustomResourceDoneable<R>> doneable = getCustomResourceDoneableClass(controller);
6465
MixedOperation client = k8sClient.customResources(crd, resClass, list, doneable);
6566
EventDispatcher eventDispatcher = new EventDispatcher(controller,
6667
getDefaultFinalizer(controller), new EventDispatcher.CustomResourceReplaceFacade(client));

operator-framework/src/main/java/com/github/containersolutions/operator/api/Controller.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@
1919

2020
Class<? extends CustomResource> customResourceClass();
2121

22-
Class<? extends CustomResourceList<? extends CustomResource>> customResourceListClass();
22+
Class<? extends CustomResourceDoneable> customResourceDoneableClass() default CustomResourceDoneable.class;
2323

24-
Class<? extends CustomResourceDoneable<? extends CustomResource>> customResourceDoneableClass();
24+
Class<? extends CustomResourceList> customResourceListClass() default CustomResourceList.class;
2525

2626
String finalizerName() default DEFAULT_FINALIZER;
27-
2827
}

operator-framework/src/test/java/com/github/containersolutions/operator/ConcurrencyIT.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.containersolutions.operator;
22

33
import com.github.containersolutions.operator.sample.TestCustomResource;
4+
import com.github.containersolutions.operator.sample.TestCustomResourceController;
45
import io.fabric8.kubernetes.api.model.ConfigMap;
56
import org.awaitility.Awaitility;
67
import org.junit.jupiter.api.*;
@@ -50,6 +51,7 @@ public void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedExcepti
5051
.untilAsserted(() -> {
5152
List<ConfigMap> items = integrationTest.getK8sClient().configMaps()
5253
.inNamespace(TEST_NAMESPACE)
54+
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
5355
.list().getItems();
5456
assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED);
5557
});
@@ -75,6 +77,7 @@ public void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedExcepti
7577
.untilAsserted(() -> {
7678
List<ConfigMap> items = integrationTest.getK8sClient().configMaps()
7779
.inNamespace(TEST_NAMESPACE)
80+
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
7881
.list().getItems();
7982
assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED);
8083

operator-framework/src/test/java/com/github/containersolutions/operator/ControllerUtilsTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
import com.github.containersolutions.operator.sample.TestCustomResource;
44
import com.github.containersolutions.operator.sample.TestCustomResourceController;
5-
import com.github.containersolutions.operator.sample.TestCustomResourceDoneable;
6-
import com.github.containersolutions.operator.sample.TestCustomResourceList;
5+
import io.fabric8.kubernetes.client.CustomResourceDoneable;
6+
import io.fabric8.kubernetes.client.CustomResourceList;
77
import org.junit.jupiter.api.Test;
88

99
import static com.github.containersolutions.operator.api.Controller.DEFAULT_FINALIZER;
1010
import static com.github.containersolutions.operator.sample.TestCustomResourceController.CRD_NAME;
1111
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
import static org.junit.jupiter.api.Assertions.assertTrue;
1213

1314
class ControllerUtilsTest {
1415

1516
@Test
1617
public void returnsValuesFromControllerAnnotationFinalizer() {
1718
assertEquals(DEFAULT_FINALIZER, ControllerUtils.getDefaultFinalizer(new TestCustomResourceController(null)));
1819
assertEquals(TestCustomResource.class, ControllerUtils.getCustomResourceClass(new TestCustomResourceController(null)));
19-
assertEquals(TestCustomResourceDoneable.class, ControllerUtils.getCustomResourceDonebaleClass(new TestCustomResourceController(null)));
20-
assertEquals(TestCustomResourceList.class, ControllerUtils.getCustomResourceListClass(new TestCustomResourceController(null)));
2120
assertEquals(CRD_NAME, ControllerUtils.getCrdName(new TestCustomResourceController(null)));
21+
assertTrue(CustomResourceDoneable.class.isAssignableFrom(ControllerUtils.getCustomResourceDoneableClass(new TestCustomResourceController(null))));
22+
assertEquals(CustomResourceList.class, ControllerUtils.getCustomResourceListClass());
2223
}
23-
2424
}

operator-framework/src/test/java/com/github/containersolutions/operator/IntegrationTestSupport.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.github.containersolutions.operator;
22

3+
import com.github.containersolutions.operator.api.ResourceController;
34
import com.github.containersolutions.operator.sample.*;
45
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
56
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
67
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
7-
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
8-
import io.fabric8.kubernetes.client.KubernetesClient;
8+
import io.fabric8.kubernetes.client.*;
9+
import io.fabric8.kubernetes.client.dsl.CreateOrReplaceable;
10+
import io.fabric8.kubernetes.client.dsl.Deletable;
911
import io.fabric8.kubernetes.client.dsl.MixedOperation;
1012
import io.fabric8.kubernetes.client.dsl.Resource;
1113
import io.fabric8.kubernetes.client.utils.Serialization;
@@ -17,6 +19,8 @@
1719
import java.io.InputStream;
1820
import java.util.concurrent.TimeUnit;
1921

22+
import static com.github.containersolutions.operator.ControllerUtils.getCustomResourceDoneableClass;
23+
import static com.github.containersolutions.operator.ControllerUtils.getCustomResourceListClass;
2024
import static org.assertj.core.api.Assertions.assertThat;
2125
import static org.awaitility.Awaitility.await;
2226

@@ -26,8 +30,10 @@ public class IntegrationTestSupport {
2630
public static final String TEST_CUSTOM_RESOURCE_PREFIX = "test-custom-resource-";
2731
private final static Logger log = LoggerFactory.getLogger(IntegrationTestSupport.class);
2832
private KubernetesClient k8sClient;
29-
private MixedOperation<TestCustomResource, TestCustomResourceList, TestCustomResourceDoneable,
30-
Resource<TestCustomResource, TestCustomResourceDoneable>> crOperations;
33+
private MixedOperation<TestCustomResource, CustomResourceList, CustomResourceDoneable,
34+
Resource<TestCustomResource, CustomResourceDoneable>> crOperations;
35+
private CreateOrReplaceable<TestCustomResource, TestCustomResource, CustomResourceDoneable> createOrReplaceable;
36+
private Deletable<TestCustomResource> deleteable;
3137
private Operator operator;
3238

3339

@@ -52,7 +58,10 @@ public void cleanup() {
5258
k8sClient.customResourceDefinitions().createOrReplace(crd);
5359
KubernetesDeserializer.registerCustomKind(crd.getApiVersion(), crd.getKind(), TestCustomResource.class);
5460

55-
crOperations = k8sClient.customResources(crd, TestCustomResource.class, TestCustomResourceList.class, TestCustomResourceDoneable.class);
61+
ResourceController<TestCustomResource> controller = new TestCustomResourceController(k8sClient);
62+
Class listClass = getCustomResourceListClass();
63+
Class doneableClass = getCustomResourceDoneableClass(controller);
64+
crOperations = k8sClient.customResources(crd, TestCustomResource.class, listClass, doneableClass);
5665
crOperations.inNamespace(TEST_NAMESPACE).delete(crOperations.list().getItems());
5766
//we depend on the actual operator from the startup to handle the finalizers and clean up
5867
//resources from previous test runs
@@ -69,7 +78,9 @@ public void cleanup() {
6978

7079
await("all config maps cleaned up").atMost(60, TimeUnit.SECONDS)
7180
.untilAsserted(() -> {
72-
assertThat(k8sClient.configMaps().inNamespace(TEST_NAMESPACE).list().getItems()).isEmpty();
81+
assertThat(k8sClient.configMaps().inNamespace(TEST_NAMESPACE)
82+
.withLabel("managedBy", TestCustomResourceController.class.getSimpleName())
83+
.list().getItems().isEmpty());
7384
});
7485

7586
log.info("Cleaned up namespace " + TEST_NAMESPACE);
@@ -105,7 +116,7 @@ public KubernetesClient getK8sClient() {
105116
return k8sClient;
106117
}
107118

108-
public MixedOperation<TestCustomResource, TestCustomResourceList, TestCustomResourceDoneable, Resource<TestCustomResource, TestCustomResourceDoneable>> getCrOperations() {
119+
public MixedOperation<TestCustomResource, CustomResourceList, CustomResourceDoneable, Resource<TestCustomResource, CustomResourceDoneable>> getCrOperations() {
109120
return crOperations;
110121
}
111122

operator-framework/src/test/java/com/github/containersolutions/operator/sample/TestCustomResourceController.java

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

1616
@Controller(
1717
crdName = TestCustomResourceController.CRD_NAME,
18-
customResourceClass = TestCustomResource.class,
19-
customResourceListClass = TestCustomResourceList.class,
20-
customResourceDoneableClass = TestCustomResourceDoneable.class)
18+
customResourceClass = TestCustomResource.class)
2119
public class TestCustomResourceController implements ResourceController<TestCustomResource> {
2220

2321
private static final Logger log = LoggerFactory.getLogger(TestCustomResourceController.class);

operator-framework/src/test/java/com/github/containersolutions/operator/sample/TestCustomResourceDoneable.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)