Skip to content

Commit f1b02f7

Browse files
authored
fix: operator not starting when watching multi namespace and no permission for one (#1779)
1 parent 2a14850 commit f1b02f7

File tree

4 files changed

+104
-4
lines changed

4 files changed

+104
-4
lines changed

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

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
99
import io.fabric8.kubernetes.api.model.rbac.ClusterRole;
1010
import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding;
11+
import io.fabric8.kubernetes.api.model.rbac.Role;
12+
import io.fabric8.kubernetes.api.model.rbac.RoleBinding;
1113
import io.fabric8.kubernetes.client.ConfigBuilder;
1214
import io.fabric8.kubernetes.client.KubernetesClient;
1315
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
1416
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
1517
import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider;
18+
import io.javaoperatorsdk.operator.health.InformerHealthIndicator;
1619
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
1720
import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerResourceEventSource;
21+
import io.javaoperatorsdk.operator.sample.informerrelatedbehavior.ConfigMapDependentResource;
1822
import io.javaoperatorsdk.operator.sample.informerrelatedbehavior.InformerRelatedBehaviorTestCustomResource;
1923
import io.javaoperatorsdk.operator.sample.informerrelatedbehavior.InformerRelatedBehaviorTestReconciler;
2024

@@ -40,6 +44,7 @@
4044
class InformerRelatedBehaviorITS {
4145

4246
public static final String TEST_RESOURCE_NAME = "test1";
47+
public static final String ADDITIONAL_NAMESPACE_NAME = "additionalns";
4348

4449
KubernetesClient adminClient = new KubernetesClientBuilder().build();
4550
InformerRelatedBehaviorTestReconciler reconciler;
@@ -54,6 +59,8 @@ void beforeEach(TestInfo testInfo) {
5459
actualNamespace = KubernetesResourceUtil.sanitizeName(method.getName());
5560
adminClient.resource(namespace()).createOrReplace();
5661
});
62+
// cleans up binding before test, not all test cases use cluster role
63+
removeClusterRoleBinding();
5764
}
5865

5966
@AfterEach
@@ -101,6 +108,51 @@ void startsUpWhenNoPermissionToSecondaryResource() {
101108
assertReconciled();
102109
}
103110

111+
@Test
112+
void startsUpIfNoPermissionToOneOfTwoNamespaces() {
113+
var otherNamespace =
114+
adminClient.resource(namespace(ADDITIONAL_NAMESPACE_NAME)).createOrReplace();
115+
try {
116+
addRoleBindingsToTestNamespaces();
117+
var operator = startOperator(false, false, actualNamespace, ADDITIONAL_NAMESPACE_NAME);
118+
assertInformerNotWatchingForAdditionalNamespace(operator);
119+
120+
adminClient.resource(testCustomResource()).createOrReplace();
121+
waitForWatchReconnect();
122+
assertReconciled();
123+
124+
} finally {
125+
adminClient.resource(otherNamespace).delete();
126+
await().untilAsserted(() -> {
127+
var ns = adminClient.namespaces().resource(otherNamespace).fromServer().get();
128+
assertThat(ns).isNull();
129+
});
130+
}
131+
}
132+
133+
private void assertInformerNotWatchingForAdditionalNamespace(Operator operator) {
134+
assertThat(operator.getRuntimeInfo().allEventSourcesAreHealthy()).isFalse();
135+
var unhealthyEventSources =
136+
operator.getRuntimeInfo().unhealthyInformerWrappingEventSourceHealthIndicator()
137+
.get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER);
138+
139+
InformerHealthIndicator controllerHealthIndicator =
140+
(InformerHealthIndicator) unhealthyEventSources
141+
.get(ControllerResourceEventSource.class.getSimpleName())
142+
.informerHealthIndicators().get(ADDITIONAL_NAMESPACE_NAME);
143+
assertThat(controllerHealthIndicator).isNotNull();
144+
assertThat(controllerHealthIndicator.getTargetNamespace()).isEqualTo(ADDITIONAL_NAMESPACE_NAME);
145+
assertThat(controllerHealthIndicator.isWatching()).isFalse();
146+
147+
InformerHealthIndicator configMapHealthIndicator =
148+
(InformerHealthIndicator) unhealthyEventSources
149+
.get(ConfigMapDependentResource.class.getSimpleName())
150+
.informerHealthIndicators().get(ADDITIONAL_NAMESPACE_NAME);
151+
assertThat(configMapHealthIndicator).isNotNull();
152+
assertThat(configMapHealthIndicator.getTargetNamespace()).isEqualTo(ADDITIONAL_NAMESPACE_NAME);
153+
assertThat(configMapHealthIndicator.isWatching()).isFalse();
154+
}
155+
104156
@Test
105157
void resilientForLoosingPermissionForCustomResource() throws InterruptedException {
106158
setFullResourcesAccess();
@@ -240,7 +292,8 @@ Operator startOperator(boolean stopOnInformerErrorDuringStartup) {
240292
return startOperator(stopOnInformerErrorDuringStartup, true);
241293
}
242294

243-
Operator startOperator(boolean stopOnInformerErrorDuringStartup, boolean addStopHandler) {
295+
Operator startOperator(boolean stopOnInformerErrorDuringStartup, boolean addStopHandler,
296+
String... namespaces) {
244297
ConfigurationServiceProvider.reset();
245298
reconciler = new InformerRelatedBehaviorTestReconciler();
246299

@@ -252,7 +305,11 @@ Operator startOperator(boolean stopOnInformerErrorDuringStartup, boolean addStop
252305
co.withInformerStoppedHandler((informer, ex) -> replacementStopHandlerCalled = true);
253306
}
254307
});
255-
operator.register(reconciler);
308+
operator.register(reconciler, o -> {
309+
if (namespaces.length > 0) {
310+
o.settingNamespaces(namespaces);
311+
}
312+
});
256313
operator.start();
257314
return operator;
258315
}
@@ -272,6 +329,16 @@ private void setFullResourcesAccess() {
272329
applyClusterRoleBinding();
273330
}
274331

332+
private void addRoleBindingsToTestNamespaces() {
333+
var role = ReconcilerUtils
334+
.loadYaml(Role.class, this.getClass(), "rback-test-only-main-ns-access.yaml");
335+
adminClient.resource(role).inNamespace(actualNamespace).createOrReplace();
336+
var roleBinding = ReconcilerUtils
337+
.loadYaml(RoleBinding.class, this.getClass(),
338+
"rback-test-only-main-ns-access-binding.yaml");
339+
adminClient.resource(roleBinding).inNamespace(actualNamespace).createOrReplace();
340+
}
341+
275342
private void applyClusterRoleBinding() {
276343
var clusterRoleBinding = ReconcilerUtils
277344
.loadYaml(ClusterRoleBinding.class, this.getClass(), "rback-test-role-binding.yaml");
@@ -285,10 +352,20 @@ private void applyClusterRole(String filename) {
285352
}
286353

287354
private Namespace namespace() {
355+
return namespace(actualNamespace);
356+
}
357+
358+
private Namespace namespace(String name) {
288359
Namespace n = new Namespace();
289360
n.setMetadata(new ObjectMetaBuilder()
290-
.withName(actualNamespace)
361+
.withName(name)
291362
.build());
292363
return n;
293364
}
365+
366+
private void removeClusterRoleBinding() {
367+
var clusterRoleBinding = ReconcilerUtils
368+
.loadYaml(ClusterRoleBinding.class, this.getClass(), "rback-test-role-binding.yaml");
369+
adminClient.resource(clusterRoleBinding).delete();
370+
}
294371
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: RoleBinding
3+
metadata:
4+
name: read-namespace-access
5+
subjects:
6+
- kind: User
7+
name: rbac-test-user
8+
apiGroup: rbac.authorization.k8s.io
9+
roleRef:
10+
kind: Role
11+
name: rbac-behavior
12+
apiGroup: rbac.authorization.k8s.io
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: Role
3+
metadata:
4+
name: rbac-behavior
5+
rules:
6+
- apiGroups: [ "sample.javaoperatorsdk" ]
7+
resources: [ "informerrelatedbehaviortestcustomresources" ]
8+
verbs: [ "get", "watch", "list","post", "delete" ]
9+
- apiGroups: [ "" ]
10+
resources: [ "configmaps" ]
11+
verbs: [ "get", "watch", "list","post", "delete", "create" ]

operator-framework/src/test/resources/io/javaoperatorsdk/operator/rback-test-role-binding.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1
22
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
33
kind: ClusterRoleBinding
44
metadata:
5-
name: read-secrets-global
5+
name: informer-rbac-startup-global
66
subjects:
77
- kind: User
88
name: rbac-test-user

0 commit comments

Comments
 (0)