Skip to content

Commit 76b15fa

Browse files
authored
feat: follow all namespace change possible (#1639)
1 parent b6ef096 commit 76b15fa

File tree

3 files changed

+95
-49
lines changed

3 files changed

+95
-49
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,13 @@ private void validateCRDWithLocalModelIfRequired(Class<P> resClass, String contr
358358
}
359359

360360
public void changeNamespaces(Set<String> namespaces) {
361-
if (namespaces.contains(Constants.WATCH_ALL_NAMESPACES)
362-
|| namespaces.contains(WATCH_CURRENT_NAMESPACE)) {
361+
if (namespaces.contains(WATCH_CURRENT_NAMESPACE)) {
363362
throw new OperatorException("Unexpected value in target namespaces: " + namespaces);
364363
}
364+
if (namespaces.contains(Constants.WATCH_ALL_NAMESPACES) && namespaces.size() > 1) {
365+
throw new OperatorException(
366+
"Watching all namespaces, but additional specific namespace is present");
367+
}
365368
eventProcessor.stop();
366369
eventSourceManager.changeNamespaces(namespaces);
367370
eventProcessor.start();

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,12 @@ void initSources(MixedOperation<T, KubernetesResourceList<T>, Resource<T>> clien
5858
final var labelSelector = configuration.getLabelSelector();
5959

6060
if (ResourceConfiguration.allNamespacesWatched(targetNamespaces)) {
61-
final var filteredBySelectorClient =
62-
client.inAnyNamespace().withLabelSelector(labelSelector);
63-
final var source =
64-
createEventSource(filteredBySelectorClient, eventHandler, WATCH_ALL_NAMESPACES);
61+
var source = createEventSourceForNamespace(WATCH_ALL_NAMESPACES);
6562
log.debug("Registered {} -> {} for any namespace", this, source);
6663
} else {
6764
targetNamespaces.forEach(
6865
ns -> {
69-
final var source =
70-
createEventSource(client.inNamespace(ns).withLabelSelector(labelSelector),
71-
eventHandler, ns);
66+
final var source = createEventSourceForNamespace(ns);
7267
log.debug("Registered {} -> {} for namespace: {}", this, source,
7368
ns);
7469
});
@@ -87,10 +82,7 @@ public void changeNamespaces(Set<String> namespaces) {
8782

8883
namespaces.forEach(ns -> {
8984
if (!sources.containsKey(ns)) {
90-
final var source =
91-
createEventSource(
92-
client.inNamespace(ns).withLabelSelector(configuration.getLabelSelector()),
93-
eventHandler, ns);
85+
final InformerWrapper<T> source = createEventSourceForNamespace(ns);
9486
source.addIndexers(this.indexers);
9587
source.start();
9688
log.debug("Registered new {} -> {} for namespace: {}", this, source,
@@ -100,6 +92,19 @@ public void changeNamespaces(Set<String> namespaces) {
10092
}
10193

10294

95+
private InformerWrapper<T> createEventSourceForNamespace(String namespace) {
96+
final InformerWrapper<T> source;
97+
if (namespace.equals(WATCH_ALL_NAMESPACES)) {
98+
final var filteredBySelectorClient =
99+
client.inAnyNamespace().withLabelSelector(configuration.getLabelSelector());
100+
source = createEventSource(filteredBySelectorClient, eventHandler, WATCH_ALL_NAMESPACES);
101+
} else {
102+
source = createEventSource(
103+
client.inNamespace(namespace).withLabelSelector(configuration.getLabelSelector()),
104+
eventHandler, namespace);
105+
}
106+
return source;
107+
}
103108

104109
private InformerWrapper<T> createEventSource(
105110
FilterWatchListDeletable<T, KubernetesResourceList<T>, Resource<T>> filteredBySelectorClient,

operator-framework/src/test/java/io/javaoperatorsdk/operator/ChangeNamespaceIT.java

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import java.util.Map;
55
import java.util.Set;
66

7+
import org.junit.jupiter.api.AfterEach;
8+
import org.junit.jupiter.api.BeforeEach;
79
import org.junit.jupiter.api.Test;
810
import org.junit.jupiter.api.extension.RegisterExtension;
911

@@ -12,6 +14,7 @@
1214
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
1315
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
1416
import io.fabric8.kubernetes.client.KubernetesClient;
17+
import io.javaoperatorsdk.operator.api.reconciler.Constants;
1518
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
1619
import io.javaoperatorsdk.operator.sample.changenamespace.ChangeNamespaceTestCustomResource;
1720
import io.javaoperatorsdk.operator.sample.changenamespace.ChangeNamespaceTestReconciler;
@@ -25,63 +28,98 @@ class ChangeNamespaceIT {
2528
public static final String TEST_RESOURCE_NAME_2 = "test2";
2629
public static final String TEST_RESOURCE_NAME_3 = "test3";
2730
public static final String ADDITIONAL_TEST_NAMESPACE = "additional-test-namespace";
31+
2832
@RegisterExtension
2933
LocallyRunOperatorExtension operator =
3034
LocallyRunOperatorExtension.builder().withReconciler(new ChangeNamespaceTestReconciler())
3135
.build();
3236

37+
@BeforeEach
38+
void setup() {
39+
client().namespaces().resource(additionalTestNamespace()).create();
40+
}
41+
42+
@AfterEach
43+
void cleanup() {
44+
client().namespaces().resource(additionalTestNamespace()).delete();
45+
}
46+
3347
@SuppressWarnings("rawtypes")
3448
@Test
3549
void addNewAndRemoveOldNamespaceTest() {
36-
try {
37-
var reconciler = operator.getReconcilerOfType(ChangeNamespaceTestReconciler.class);
38-
var defaultNamespaceResource = operator.create(customResource(TEST_RESOURCE_NAME_1));
50+
var reconciler = operator.getReconcilerOfType(ChangeNamespaceTestReconciler.class);
51+
var defaultNamespaceResource = operator.create(customResource(TEST_RESOURCE_NAME_1));
52+
53+
assertReconciled(reconciler, defaultNamespaceResource);
54+
var resourceInAdditionalTestNamespace = createResourceInAdditionalNamespace();
55+
56+
assertNotReconciled(reconciler, resourceInAdditionalTestNamespace);
57+
// adding additional namespace
58+
RegisteredController registeredController =
59+
operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class);
60+
registeredController
61+
.changeNamespaces(Set.of(operator.getNamespace(), ADDITIONAL_TEST_NAMESPACE));
62+
63+
assertReconciled(reconciler, resourceInAdditionalTestNamespace);
3964

40-
await().pollDelay(Duration.ofMillis(100)).untilAsserted(() -> assertThat(
41-
reconciler.numberOfResourceReconciliations(defaultNamespaceResource)).isEqualTo(2));
65+
// removing a namespace
66+
registeredController.changeNamespaces(Set.of(ADDITIONAL_TEST_NAMESPACE));
4267

43-
client().namespaces().create(additionalTestNamespace());
44-
var resourceInAdditionalTestNamespace = createResourceInTestNamespace();
4568

46-
await().pollDelay(Duration.ofMillis(200)).untilAsserted(
47-
() -> assertThat(
48-
reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace))
49-
.isZero());
69+
var newResourceInDefaultNamespace = operator.create(customResource(TEST_RESOURCE_NAME_3));
70+
assertNotReconciled(reconciler, newResourceInDefaultNamespace);
5071

51-
// adding additional namespace
52-
RegisteredController registeredController =
53-
operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class);
54-
registeredController
55-
.changeNamespaces(Set.of(operator.getNamespace(), ADDITIONAL_TEST_NAMESPACE));
72+
ConfigMap firstMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME_1);
73+
firstMap.setData(Map.of("data", "newdata"));
74+
operator.replace(firstMap);
75+
assertReconciled(reconciler, defaultNamespaceResource);
76+
}
77+
78+
@Test
79+
void changeToWatchAllNamespaces() {
80+
var reconciler = operator.getReconcilerOfType(ChangeNamespaceTestReconciler.class);
81+
var resourceInAdditionalTestNamespace = createResourceInAdditionalNamespace();
82+
83+
assertNotReconciled(reconciler, resourceInAdditionalTestNamespace);
5684

57-
await().untilAsserted(
58-
() -> assertThat(
59-
reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace))
60-
.isEqualTo(2));
85+
var registeredController =
86+
operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class);
6187

62-
// removing a namespace
63-
registeredController.changeNamespaces(Set.of(ADDITIONAL_TEST_NAMESPACE));
88+
registeredController
89+
.changeNamespaces(Set.of(Constants.WATCH_ALL_NAMESPACES));
6490

65-
var newResourceInDefaultNamespace = operator.create(customResource(TEST_RESOURCE_NAME_3));
66-
await().pollDelay(Duration.ofMillis(200))
67-
.untilAsserted(() -> assertThat(
68-
reconciler.numberOfResourceReconciliations(newResourceInDefaultNamespace)).isZero());
91+
assertReconciled(reconciler, resourceInAdditionalTestNamespace);
6992

93+
registeredController.changeNamespaces(Set.of(operator.getNamespace()));
7094

71-
ConfigMap firstMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME_1);
72-
firstMap.setData(Map.of("data", "newdata"));
73-
operator.replace(firstMap);
95+
var defaultNamespaceResource = operator.create(customResource(TEST_RESOURCE_NAME_1));
96+
var resource2InAdditionalResource = createResourceInAdditionalNamespace(TEST_RESOURCE_NAME_3);
97+
assertReconciled(reconciler, defaultNamespaceResource);
98+
assertNotReconciled(reconciler, resource2InAdditionalResource);
99+
}
100+
101+
private static void assertReconciled(ChangeNamespaceTestReconciler reconciler,
102+
ChangeNamespaceTestCustomResource resourceInAdditionalTestNamespace) {
103+
await().untilAsserted(
104+
() -> assertThat(
105+
reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace))
106+
.isEqualTo(2));
107+
}
74108

75-
await().untilAsserted(() -> assertThat(
76-
reconciler.numberOfResourceReconciliations(defaultNamespaceResource)).isEqualTo(2));
109+
private static void assertNotReconciled(ChangeNamespaceTestReconciler reconciler,
110+
ChangeNamespaceTestCustomResource resourceInAdditionalTestNamespace) {
111+
await().pollDelay(Duration.ofMillis(200)).untilAsserted(
112+
() -> assertThat(
113+
reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace))
114+
.isZero());
115+
}
77116

78-
} finally {
79-
client().namespaces().delete(additionalTestNamespace());
80-
}
117+
private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace() {
118+
return createResourceInAdditionalNamespace(TEST_RESOURCE_NAME_2);
81119
}
82120

83-
private ChangeNamespaceTestCustomResource createResourceInTestNamespace() {
84-
var res = customResource(TEST_RESOURCE_NAME_2);
121+
private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace(String name) {
122+
var res = customResource(name);
85123
return client().resources(ChangeNamespaceTestCustomResource.class)
86124
.inNamespace(ADDITIONAL_TEST_NAMESPACE)
87125
.create(res);

0 commit comments

Comments
 (0)