Skip to content

Commit fdefd56

Browse files
authored
fix: generate proper resource name & finalizer for group-less resources (#814)
Fixes #812
1 parent 9c3f187 commit fdefd56

File tree

9 files changed

+87
-26
lines changed

9 files changed

+87
-26
lines changed

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

+45-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.Locale;
44

5+
import io.fabric8.kubernetes.api.model.HasMetadata;
6+
import io.fabric8.kubernetes.api.model.ObjectMeta;
57
import io.javaoperatorsdk.operator.api.reconciler.Constants;
68
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
79
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
@@ -10,9 +12,50 @@
1012
public class ReconcilerUtils {
1113

1214
private static final String FINALIZER_NAME_SUFFIX = "/finalizer";
15+
protected static final String MISSING_GROUP_SUFFIX = ".javaoperatorsdk.io";
1316

14-
public static String getDefaultFinalizerName(String crdName) {
15-
return crdName + FINALIZER_NAME_SUFFIX;
17+
// prevent instantiation of util class
18+
private ReconcilerUtils() {}
19+
20+
public static boolean isFinalizerValid(String finalizer) {
21+
// todo: use fabric8 method when 5.12 is released
22+
// return HasMetadata.validateFinalizer(finalizer);
23+
final var validator = new HasMetadata() {
24+
25+
@Override
26+
public ObjectMeta getMetadata() {
27+
throw new UnsupportedOperationException();
28+
}
29+
30+
@Override
31+
public void setMetadata(ObjectMeta objectMeta) {
32+
throw new UnsupportedOperationException();
33+
}
34+
35+
@Override
36+
public void setApiVersion(String s) {
37+
throw new UnsupportedOperationException();
38+
}
39+
};
40+
return validator.isFinalizerValid(finalizer);
41+
}
42+
43+
public static String getResourceTypeName(Class<? extends HasMetadata> resourceClass) {
44+
// todo: use fabric8 method when 5.12 is released
45+
// return HasMetadata.getFullResourceName(resourceClass);
46+
final var group = HasMetadata.getGroup(resourceClass);
47+
final var plural = HasMetadata.getPlural(resourceClass);
48+
return (group == null || group.isEmpty()) ? plural : plural + "." + group;
49+
}
50+
51+
public static String getDefaultFinalizerName(Class<? extends HasMetadata> resourceClass) {
52+
var resourceName = getResourceTypeName(resourceClass);
53+
// resource names for historic resources such as Pods are missing periods and therefore do not
54+
// constitute valid domain names as mandated by Kubernetes so generate one that does
55+
if (resourceName.indexOf('.') < 0) {
56+
resourceName = resourceName + MISSING_GROUP_SUFFIX;
57+
}
58+
return resourceName + FINALIZER_NAME_SUFFIX;
1659
}
1760

1861
public static String getNameFor(Class<? extends Reconciler> reconcilerClass) {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ default String getName() {
1717
}
1818

1919
default String getResourceTypeName() {
20-
return HasMetadata.getFullResourceName(getResourceClass());
20+
return ReconcilerUtils.getResourceTypeName(getResourceClass());
2121
}
2222

2323
default String getFinalizer() {
24-
return ReconcilerUtils.getDefaultFinalizerName(getResourceTypeName());
24+
return ReconcilerUtils.getDefaultFinalizerName(getResourceClass());
2525
}
2626

2727
/**

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java

+21-3
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,35 @@
22

33
import org.junit.jupiter.api.Test;
44

5+
import io.fabric8.kubernetes.api.model.Pod;
56
import io.javaoperatorsdk.operator.sample.simple.TestCustomReconciler;
7+
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
68

9+
import static io.javaoperatorsdk.operator.ReconcilerUtils.getDefaultFinalizerName;
10+
import static io.javaoperatorsdk.operator.ReconcilerUtils.getDefaultNameFor;
11+
import static io.javaoperatorsdk.operator.ReconcilerUtils.getDefaultReconcilerName;
12+
import static io.javaoperatorsdk.operator.ReconcilerUtils.isFinalizerValid;
713
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertTrue;
815

916
class ReconcilerUtilsTest {
1017

1118
@Test
12-
void getDefaultResourceControllerName() {
19+
void defaultReconcilerNameShouldWork() {
1320
assertEquals(
1421
"testcustomreconciler",
15-
ReconcilerUtils.getDefaultReconcilerName(
16-
TestCustomReconciler.class.getCanonicalName()));
22+
getDefaultReconcilerName(TestCustomReconciler.class.getCanonicalName()));
23+
assertEquals(
24+
getDefaultNameFor(TestCustomReconciler.class),
25+
getDefaultReconcilerName(TestCustomReconciler.class.getCanonicalName()));
26+
assertEquals(
27+
getDefaultNameFor(TestCustomReconciler.class),
28+
getDefaultReconcilerName(TestCustomReconciler.class.getSimpleName()));
29+
}
30+
31+
@Test
32+
void defaultFinalizerShouldWork() {
33+
assertTrue(isFinalizerValid(getDefaultFinalizerName(Pod.class)));
34+
assertTrue(isFinalizerValid(getDefaultFinalizerName(TestCustomResource.class)));
1735
}
1836
}

operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AnnotationConfiguration.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.fabric8.kubernetes.api.model.HasMetadata;
77
import io.javaoperatorsdk.operator.ReconcilerUtils;
88
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
9+
import io.javaoperatorsdk.operator.api.reconciler.Constants;
910
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
1011
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
1112
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
@@ -31,9 +32,15 @@ public String getName() {
3132
@Override
3233
public String getFinalizer() {
3334
if (annotation == null || annotation.finalizerName().isBlank()) {
34-
return ReconcilerUtils.getDefaultFinalizerName(getResourceTypeName());
35+
return ReconcilerUtils.getDefaultFinalizerName(getResourceClass());
3536
} else {
36-
return annotation.finalizerName();
37+
final var finalizer = annotation.finalizerName();
38+
if (Constants.NO_FINALIZER.equals(finalizer) || ReconcilerUtils.isFinalizerValid(finalizer)) {
39+
return finalizer;
40+
} else {
41+
throw new IllegalArgumentException(finalizer
42+
+ " is not a valid finalizer. See https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers for details");
43+
}
3744
}
3845
}
3946

@@ -93,7 +100,7 @@ public ResourceEventFilter<R> getEventFilter() {
93100
answer = answer.and(filter);
94101
}
95102
} catch (Exception e) {
96-
throw new RuntimeException(e);
103+
throw new IllegalArgumentException(e);
97104
}
98105
}
99106
}

operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import static org.junit.jupiter.api.Assertions.assertEquals;
2323
import static org.junit.jupiter.api.Assertions.assertFalse;
2424

25-
public class DefaultConfigurationServiceTest {
25+
class DefaultConfigurationServiceTest {
2626

2727
public static final String CUSTOM_FINALIZER_NAME = "a.custom/finalizer";
2828

@@ -73,29 +73,29 @@ void attemptingToRetrieveAnUnknownControllerShouldLogWarning() {
7373
}
7474

7575
@Test
76-
public void returnsValuesFromControllerAnnotationFinalizer() {
76+
void returnsValuesFromControllerAnnotationFinalizer() {
7777
final var reconciler = new TestCustomReconciler();
7878
final var configuration =
7979
DefaultConfigurationService.instance().getConfigurationFor(reconciler);
8080
assertEquals(CustomResource.getCRDName(TestCustomResource.class),
8181
configuration.getResourceTypeName());
8282
assertEquals(
83-
ReconcilerUtils.getDefaultFinalizerName(configuration.getResourceTypeName()),
83+
ReconcilerUtils.getDefaultFinalizerName(TestCustomResource.class),
8484
configuration.getFinalizer());
8585
assertEquals(TestCustomResource.class, configuration.getResourceClass());
8686
assertFalse(configuration.isGenerationAware());
8787
}
8888

8989
@Test
90-
public void returnCustomerFinalizerNameIfSet() {
90+
void returnCustomerFinalizerNameIfSet() {
9191
final var reconciler = new TestCustomFinalizerReconciler();
9292
final var configuration =
9393
DefaultConfigurationService.instance().getConfigurationFor(reconciler);
9494
assertEquals(CUSTOM_FINALIZER_NAME, configuration.getFinalizer());
9595
}
9696

9797
@Test
98-
public void supportsInnerClassCustomResources() {
98+
void supportsInnerClassCustomResources() {
9999
final var reconciler = new TestCustomFinalizerReconciler();
100100
assertDoesNotThrow(
101101
() -> {

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomReconciler.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.util.concurrent.atomic.AtomicInteger;
44

5-
import io.fabric8.kubernetes.client.CustomResource;
65
import io.javaoperatorsdk.operator.ReconcilerUtils;
76
import io.javaoperatorsdk.operator.api.reconciler.*;
87
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
@@ -14,8 +13,7 @@ public class EventSourceTestCustomReconciler
1413
TestExecutionInfoProvider {
1514

1615
public static final String FINALIZER_NAME =
17-
ReconcilerUtils.getDefaultFinalizerName(
18-
CustomResource.getCRDName(EventSourceTestCustomResource.class));
16+
ReconcilerUtils.getDefaultFinalizerName(EventSourceTestCustomResource.class);
1917
public static final int TIMER_PERIOD = 500;
2018
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
2119

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/retry/RetryTestCustomReconciler.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
77

8-
import io.fabric8.kubernetes.client.CustomResource;
98
import io.javaoperatorsdk.operator.ReconcilerUtils;
109
import io.javaoperatorsdk.operator.api.reconciler.Context;
1110
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
@@ -18,8 +17,7 @@ public class RetryTestCustomReconciler
1817
implements Reconciler<RetryTestCustomResource>, TestExecutionInfoProvider {
1918

2019
public static final String FINALIZER_NAME =
21-
ReconcilerUtils.getDefaultFinalizerName(
22-
CustomResource.getCRDName(RetryTestCustomResource.class));
20+
ReconcilerUtils.getDefaultFinalizerName(RetryTestCustomResource.class);
2321
private static final Logger log =
2422
LoggerFactory.getLogger(RetryTestCustomReconciler.class);
2523
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestReconciler.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import io.fabric8.kubernetes.api.model.ConfigMap;
1111
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
1212
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
13-
import io.fabric8.kubernetes.client.CustomResource;
1413
import io.fabric8.kubernetes.client.KubernetesClient;
1514
import io.javaoperatorsdk.operator.ReconcilerUtils;
1615
import io.javaoperatorsdk.operator.api.reconciler.*;
@@ -26,7 +25,7 @@ public class TestReconciler
2625
private static final Logger log = LoggerFactory.getLogger(TestReconciler.class);
2726

2827
public static final String FINALIZER_NAME =
29-
ReconcilerUtils.getDefaultFinalizerName(CustomResource.getCRDName(TestCustomResource.class));
28+
ReconcilerUtils.getDefaultFinalizerName(TestCustomResource.class);
3029

3130
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
3231
private KubernetesClient kubernetesClient;

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomReconciler.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
77

8-
import io.fabric8.kubernetes.client.CustomResource;
98
import io.javaoperatorsdk.operator.ReconcilerUtils;
109
import io.javaoperatorsdk.operator.api.reconciler.Context;
1110
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
@@ -18,8 +17,7 @@ public class SubResourceTestCustomReconciler
1817
implements Reconciler<SubResourceTestCustomResource>, TestExecutionInfoProvider {
1918

2019
public static final String FINALIZER_NAME =
21-
ReconcilerUtils.getDefaultFinalizerName(
22-
CustomResource.getCRDName(SubResourceTestCustomResource.class));
20+
ReconcilerUtils.getDefaultFinalizerName(SubResourceTestCustomResource.class);
2321
private static final Logger log =
2422
LoggerFactory.getLogger(SubResourceTestCustomReconciler.class);
2523
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);

0 commit comments

Comments
 (0)