Skip to content

Commit 8262cfe

Browse files
authored
Merge pull request #250 from psycho-ir/inner-class-doneable
Fix issue with the generation of doneable classes for inner class custom resources
2 parents 8aa933b + d4015ff commit 8262cfe

File tree

6 files changed

+80
-53
lines changed

6 files changed

+80
-53
lines changed

Diff for: operator-framework/pom.xml

+11-14
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@
4545
<groupId>org.slf4j</groupId>
4646
<artifactId>slf4j-api</artifactId>
4747
</dependency>
48+
<dependency>
49+
<groupId>com.google.auto.service</groupId>
50+
<artifactId>auto-service</artifactId>
51+
<version>1.0-rc7</version>
52+
</dependency>
53+
54+
<dependency>
55+
<groupId>com.squareup</groupId>
56+
<artifactId>javapoet</artifactId>
57+
<version>1.13.0</version>
58+
</dependency>
4859
<dependency>
4960
<groupId>org.junit.jupiter</groupId>
5061
<artifactId>junit-jupiter-api</artifactId>
@@ -85,19 +96,5 @@
8596
<version>0.19</version>
8697
<scope>test</scope>
8798
</dependency>
88-
89-
<dependency>
90-
<groupId>com.google.auto.service</groupId>
91-
<artifactId>auto-service</artifactId>
92-
<version>1.0-rc2</version>
93-
<scope>compile</scope>
94-
</dependency>
95-
96-
<dependency>
97-
<groupId>com.squareup</groupId>
98-
<artifactId>javapoet</artifactId>
99-
<version>1.13.0</version>
100-
<scope>compile</scope>
101-
</dependency>
10299
</dependencies>
103100
</project>
+10-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package io.javaoperatorsdk.operator;
22

3-
import io.fabric8.kubernetes.client.CustomResource;
4-
import io.javaoperatorsdk.operator.api.ResourceController;
53
import org.apache.commons.lang3.ClassUtils;
64
import org.slf4j.Logger;
75
import org.slf4j.LoggerFactory;
@@ -14,11 +12,11 @@
1412
import java.util.stream.Collectors;
1513

1614

17-
class ControllerToCustomResourceMappingsProvider {
18-
private static final Logger log = LoggerFactory.getLogger(ControllerToCustomResourceMappingsProvider.class);
15+
class ClassMappingProvider {
16+
private static final Logger log = LoggerFactory.getLogger(ClassMappingProvider.class);
1917

20-
static Map<Class<? extends ResourceController>, Class<? extends CustomResource>> provide(final String resourcePath) {
21-
Map<Class<? extends ResourceController>, Class<? extends CustomResource>> controllerToCustomResourceMappings = new HashMap();
18+
static <T, V> Map<T, V> provide(final String resourcePath, T key, V value) {
19+
Map<T, V> result = new HashMap();
2220
try {
2321
final var classLoader = Thread.currentThread().getContextClassLoader();
2422
final Enumeration<URL> customResourcesMetadataList = classLoader.getResources(resourcePath);
@@ -30,20 +28,20 @@ static Map<Class<? extends ResourceController>, Class<? extends CustomResource>>
3028
try {
3129
final String[] classNames = clazzPair.split(",");
3230
if (classNames.length != 2) {
33-
throw new IllegalStateException(String.format("%s is not valid CustomResource metadata defined in %s", clazzPair, url.toString()));
31+
throw new IllegalStateException(String.format("%s is not valid Mapping metadata, defined in %s", clazzPair, url.toString()));
3432
}
3533

36-
controllerToCustomResourceMappings.put(
37-
(Class<? extends ResourceController>) ClassUtils.getClass(classNames[0]),
38-
(Class<? extends CustomResource>) ClassUtils.getClass(classNames[1])
34+
result.put(
35+
(T) ClassUtils.getClass(classNames[0]),
36+
(V) ClassUtils.getClass(classNames[1])
3937
);
4038
} catch (ClassNotFoundException e) {
4139
throw new RuntimeException(e);
4240
}
4341
});
4442
}
45-
log.debug("Loaded Controller to CustomResource mappings {}", controllerToCustomResourceMappings);
46-
return controllerToCustomResourceMappings;
43+
log.debug("Loaded Controller to CustomResource mappings {}", result);
44+
return result;
4745
} catch (IOException e) {
4846
throw new RuntimeException(e);
4947
}

Diff for: operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerUtils.java

+17-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import io.fabric8.kubernetes.client.CustomResourceDoneable;
55
import io.javaoperatorsdk.operator.api.Controller;
66
import io.javaoperatorsdk.operator.api.ResourceController;
7-
import org.apache.commons.lang3.ClassUtils;
87

98
import java.util.Map;
109

@@ -13,12 +12,21 @@ public class ControllerUtils {
1312

1413
private static final String FINALIZER_NAME_SUFFIX = "/finalizer";
1514
public static final String CONTROLLERS_RESOURCE_PATH = "javaoperatorsdk/controllers";
15+
public static final String DONEABLES_RESOURCE_PATH = "javaoperatorsdk/doneables";
1616
private static Map<Class<? extends ResourceController>, Class<? extends CustomResource>> controllerToCustomResourceMappings;
17+
private static Map<Class<? extends CustomResource>, Class<? extends CustomResourceDoneable>> resourceToDoneableMappings;
18+
1719

1820
static {
19-
controllerToCustomResourceMappings =
20-
ControllerToCustomResourceMappingsProvider
21-
.provide(CONTROLLERS_RESOURCE_PATH);
21+
controllerToCustomResourceMappings = ClassMappingProvider
22+
.provide(CONTROLLERS_RESOURCE_PATH,
23+
ResourceController.class,
24+
CustomResource.class);
25+
resourceToDoneableMappings = ClassMappingProvider
26+
.provide(DONEABLES_RESOURCE_PATH,
27+
CustomResource.class,
28+
CustomResourceDoneable.class
29+
);
2230
}
2331

2432
static String getFinalizer(ResourceController controller) {
@@ -53,13 +61,12 @@ static String getCrdName(ResourceController controller) {
5361

5462
public static <T extends CustomResource> Class<? extends CustomResourceDoneable<T>>
5563
getCustomResourceDoneableClass(ResourceController<T> controller) {
56-
try {
57-
final Class<T> customResourceClass = getCustomResourceClass(controller);
58-
return (Class<? extends CustomResourceDoneable<T>>) ClassUtils.getClass(customResourceClass.getCanonicalName() + "Doneable");
59-
} catch (ClassNotFoundException e) {
60-
e.printStackTrace();
61-
return null;
64+
final Class<T> customResourceClass = getCustomResourceClass(controller);
65+
final Class<? extends CustomResourceDoneable<T>> doneableClass = (Class<? extends CustomResourceDoneable<T>>) resourceToDoneableMappings.get(customResourceClass);
66+
if (doneableClass == null) {
67+
throw new RuntimeException(String.format("No matching Doneable class found for %s", customResourceClass));
6268
}
69+
return doneableClass;
6370
}
6471

6572
private static Controller getAnnotation(ResourceController<?> controller) {

Diff for: operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllersResourceWriter.java renamed to operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/AccumulativeMappingWriter.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@
1010
import java.util.concurrent.ConcurrentHashMap;
1111
import java.util.stream.Collectors;
1212

13-
import static io.javaoperatorsdk.operator.ControllerUtils.CONTROLLERS_RESOURCE_PATH;
14-
15-
class ControllersResourceWriter {
13+
class AccumulativeMappingWriter {
1614
private Map<String, String> mappings = new ConcurrentHashMap<>();
15+
private final String resourcePath;
1716
private final ProcessingEnvironment processingEnvironment;
1817

19-
public ControllersResourceWriter(ProcessingEnvironment processingEnvironment) {
18+
public AccumulativeMappingWriter(String resourcePath, ProcessingEnvironment processingEnvironment) {
19+
this.resourcePath = resourcePath;
2020
this.processingEnvironment = processingEnvironment;
2121
}
2222

23-
public ControllersResourceWriter loadExistingMappings() {
23+
public AccumulativeMappingWriter loadExistingMappings() {
2424
try {
2525
final var readonlyResource = processingEnvironment
2626
.getFiler()
27-
.getResource(StandardLocation.CLASS_OUTPUT, "", CONTROLLERS_RESOURCE_PATH);
27+
.getResource(StandardLocation.CLASS_OUTPUT, "", resourcePath);
2828

2929
final var bufferedReader = new BufferedReader(new InputStreamReader(readonlyResource.openInputStream()));
3030
final var existingLines = bufferedReader
@@ -37,8 +37,8 @@ public ControllersResourceWriter loadExistingMappings() {
3737
return this;
3838
}
3939

40-
public ControllersResourceWriter add(String controllerClassName, String customResourceTypeName) {
41-
this.mappings.put(controllerClassName, customResourceTypeName);
40+
public AccumulativeMappingWriter add(String key, String value) {
41+
this.mappings.put(key, value);
4242
return this;
4343
}
4444

@@ -47,7 +47,7 @@ public void flush() {
4747
try {
4848
final var resource = processingEnvironment
4949
.getFiler()
50-
.createResource(StandardLocation.CLASS_OUTPUT, "", CONTROLLERS_RESOURCE_PATH);
50+
.createResource(StandardLocation.CLASS_OUTPUT, "", resourcePath);
5151
printWriter = new PrintWriter(resource.openOutputStream());
5252

5353

Diff for: operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessor.java

+19-5
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,24 @@
2222
import java.util.stream.Collectors;
2323

2424
import static io.javaoperatorsdk.operator.ControllerUtils.CONTROLLERS_RESOURCE_PATH;
25+
import static io.javaoperatorsdk.operator.ControllerUtils.DONEABLES_RESOURCE_PATH;
2526

2627
@SupportedAnnotationTypes(
2728
"io.javaoperatorsdk.operator.api.Controller")
2829
@SupportedSourceVersion(SourceVersion.RELEASE_8)
2930
@AutoService(Processor.class)
3031
public class ControllerAnnotationProcessor extends AbstractProcessor {
31-
private ControllersResourceWriter controllersResourceWriter;
32+
private AccumulativeMappingWriter controllersResourceWriter;
33+
private AccumulativeMappingWriter doneablesResourceWriter;
3234
private Set<String> generatedDoneableClassFiles = new HashSet<>();
3335

3436
@Override
3537
public synchronized void init(ProcessingEnvironment processingEnv) {
3638
super.init(processingEnv);
37-
controllersResourceWriter = new ControllersResourceWriter(processingEnv);
38-
controllersResourceWriter.loadExistingMappings();
39+
controllersResourceWriter = new AccumulativeMappingWriter(CONTROLLERS_RESOURCE_PATH, processingEnv)
40+
.loadExistingMappings();
41+
doneablesResourceWriter = new AccumulativeMappingWriter(DONEABLES_RESOURCE_PATH, processingEnv)
42+
.loadExistingMappings();
3943
}
4044

4145
@Override
@@ -51,6 +55,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
5155
} finally {
5256
if (roundEnv.processingOver()) {
5357
controllersResourceWriter.flush();
58+
doneablesResourceWriter.flush();
5459
}
5560
}
5661
return true;
@@ -71,7 +76,7 @@ private void generateDoneableClass(TypeElement controllerClassSymbol) {
7176
if (!generatedDoneableClassFiles.add(destinationClassFileName)) {
7277
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
7378
String.format(
74-
"%s already exist! adding the mapping to the %s",
79+
"%s already exists! adding the mapping to the %s",
7580
destinationClassFileName,
7681
CONTROLLERS_RESOURCE_PATH)
7782
);
@@ -98,9 +103,11 @@ private void generateDoneableClass(TypeElement controllerClassSymbol) {
98103
.build();
99104

100105
final PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(customerResourceTypeElement);
101-
JavaFile file = JavaFile.builder(packageElement.getQualifiedName().toString(), typeSpec)
106+
final var packageQualifiedName = packageElement.getQualifiedName().toString();
107+
JavaFile file = JavaFile.builder(packageQualifiedName, typeSpec)
102108
.build();
103109
file.writeTo(out);
110+
doneablesResourceWriter.add(customResourceType.toString(), makeQualifiedClassName(packageQualifiedName, doneableClassName));
104111
}
105112
} catch (Exception ioException) {
106113
ioException.printStackTrace();
@@ -137,4 +144,11 @@ private List<DeclaredType> collectAllInterfaces(TypeElement element) {
137144
return null;
138145
}
139146
}
147+
148+
private String makeQualifiedClassName(String packageName, String className) {
149+
if (packageName.equals("")) {
150+
return className;
151+
}
152+
return packageName + "." + className;
153+
}
140154
}

Diff for: operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerUtilsTest.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.javaoperatorsdk.operator;
22

3+
import io.fabric8.kubernetes.client.CustomResource;
34
import io.fabric8.kubernetes.client.CustomResourceDoneable;
45
import io.javaoperatorsdk.operator.api.*;
56
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
@@ -23,15 +24,18 @@ public void returnsValuesFromControllerAnnotationFinalizer() {
2324
}
2425

2526
@Controller(crdName = "test.crd", finalizerName = CUSTOM_FINALIZER_NAME)
26-
static class TestCustomFinalizerController implements ResourceController<TestCustomResource> {
27+
static class TestCustomFinalizerController implements ResourceController<TestCustomFinalizerController.InnerCustomResource> {
28+
public class InnerCustomResource extends CustomResource {
29+
}
2730

2831
@Override
29-
public DeleteControl deleteResource(TestCustomResource resource, Context<TestCustomResource> context) {
32+
public DeleteControl deleteResource(TestCustomFinalizerController.InnerCustomResource resource, Context<InnerCustomResource> context) {
3033
return DeleteControl.DEFAULT_DELETE;
3134
}
3235

3336
@Override
34-
public UpdateControl<TestCustomResource> createOrUpdateResource(TestCustomResource resource, Context<TestCustomResource> context) {
37+
public UpdateControl<TestCustomFinalizerController.InnerCustomResource> createOrUpdateResource(InnerCustomResource resource,
38+
Context<InnerCustomResource> context) {
3539
return null;
3640
}
3741
}
@@ -40,4 +44,11 @@ public UpdateControl<TestCustomResource> createOrUpdateResource(TestCustomResour
4044
public void returnCustomerFinalizerNameIfSet() {
4145
assertEquals(CUSTOM_FINALIZER_NAME, ControllerUtils.getFinalizer(new TestCustomFinalizerController()));
4246
}
47+
48+
@Test
49+
public void supportsInnerClassCustomResources() {
50+
assertDoesNotThrow(() -> {
51+
ControllerUtils.getCustomResourceDoneableClass(new TestCustomFinalizerController());
52+
});
53+
}
4354
}

0 commit comments

Comments
 (0)