Skip to content

Commit 4ef09ec

Browse files
authored
feat: label selector on resources (#90)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 2c50839 commit 4ef09ec

File tree

7 files changed

+196
-10
lines changed

7 files changed

+196
-10
lines changed

Diff for: src/main/java/io/csviri/operator/glue/ControllerConfig.java

+2
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ public interface ControllerConfig {
99

1010
Map<String, String> glueOperatorManagedGlueLabel();
1111

12+
Map<String, String> resourceLabelSelector();
13+
1214
}

Diff for: src/main/java/io/csviri/operator/glue/reconciler/glue/GlueReconciler.java

+11-5
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
import io.javaoperatorsdk.operator.processing.dependent.workflow.KubernetesResourceDeletedCondition;
3030
import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowBuilder;
3131

32-
import jakarta.inject.Inject;
33-
3432
import static io.csviri.operator.glue.Utils.getResourceForSSAFrom;
3533
import static io.csviri.operator.glue.reconciler.operator.GlueOperatorReconciler.PARENT_RELATED_RESOURCE_NAME;
3634

@@ -42,14 +40,22 @@ public class GlueReconciler implements Reconciler<Glue>, Cleaner<Glue>, ErrorSta
4240
public static final String PARENT_GLUE_FINALIZER_PREFIX = "io.csviri.operator.resourceflow.glue/";
4341
public static final String GLUE_RECONCILER_NAME = "glue";
4442

45-
@Inject
46-
ValidationAndErrorHandler validationAndErrorHandler;
43+
44+
private final ValidationAndErrorHandler validationAndErrorHandler;
45+
private final InformerRegister informerRegister;
4746

4847
private final KubernetesResourceDeletedCondition deletePostCondition =
4948
new KubernetesResourceDeletedCondition();
50-
private final InformerRegister informerRegister = new InformerRegister();
49+
5150
private final GenericTemplateHandler genericTemplateHandler = new GenericTemplateHandler();
5251

52+
53+
public GlueReconciler(ValidationAndErrorHandler validationAndErrorHandler,
54+
InformerRegister informerRegister) {
55+
this.validationAndErrorHandler = validationAndErrorHandler;
56+
this.informerRegister = informerRegister;
57+
}
58+
5359
/**
5460
* Handling finalizers for GlueOperator: Glue ids a finalizer to parent, that is necessary since
5561
* on clean up the resource name might be calculated based on the parents name, and it this way
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.csviri.operator.glue.reconciler.glue;
2+
3+
4+
import io.csviri.operator.glue.customresource.glue.Glue;
5+
import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
6+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
7+
import io.javaoperatorsdk.operator.api.reconciler.Context;
8+
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
9+
10+
import jakarta.inject.Singleton;
11+
12+
13+
/** For mocking purpose only. Too complex to mock InformerEventSource creation. */
14+
@Singleton
15+
public class InformerProducer {
16+
17+
public InformerEventSource<GenericKubernetesResource, Glue> createInformer(
18+
InformerConfiguration<GenericKubernetesResource> configuration,
19+
Context<Glue> context) {
20+
return new InformerEventSource<>(configuration,
21+
context.eventSourceRetriever().eventSourceContextForDynamicRegistration());
22+
}
23+
24+
}

Diff for: src/main/java/io/csviri/operator/glue/reconciler/glue/InformerRegister.java

+36-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.slf4j.Logger;
88
import org.slf4j.LoggerFactory;
99

10+
import io.csviri.operator.glue.ControllerConfig;
1011
import io.csviri.operator.glue.Utils;
1112
import io.csviri.operator.glue.customresource.glue.Glue;
1213
import io.csviri.operator.glue.customresource.glue.RelatedResourceSpec;
@@ -17,8 +18,11 @@
1718
import io.javaoperatorsdk.operator.processing.event.ResourceID;
1819
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
1920

21+
import jakarta.inject.Singleton;
22+
2023
// todo unit test
21-
class InformerRegister {
24+
@Singleton
25+
public class InformerRegister {
2226

2327
private static final Logger log = LoggerFactory.getLogger(InformerRegister.class);
2428

@@ -27,6 +31,17 @@ class InformerRegister {
2731
private final Map<GroupVersionKind, RelatedAndOwnedResourceSecondaryToPrimaryMapper> relatedResourceMappers =
2832
new ConcurrentHashMap<>();
2933

34+
35+
private final InformerProducer informerProducer;
36+
private final ControllerConfig controllerConfig;
37+
38+
39+
public InformerRegister(InformerProducer informerProducer, ControllerConfig controllerConfig) {
40+
this.informerProducer = informerProducer;
41+
this.controllerConfig = controllerConfig;
42+
}
43+
44+
3045
// todo test related resources deleting
3146
public synchronized void deRegisterInformerOnResourceFlowChange(Context<Glue> context,
3247
Glue primary) {
@@ -78,13 +93,19 @@ public InformerEventSource<GenericKubernetesResource, Glue> registerInformer(
7893
mapper = relatedResourceMappers.get(gvk);
7994
markEventSource(gvk, glue);
8095
}
81-
var newES = new InformerEventSource<>(InformerConfiguration.<GenericKubernetesResource>from(gvk)
82-
.withSecondaryToPrimaryMapper(mapper)
83-
.build(), context.eventSourceRetriever().eventSourceContextForDynamicRegistration());
96+
97+
var configBuilder = InformerConfiguration.<GenericKubernetesResource>from(gvk)
98+
.withSecondaryToPrimaryMapper(mapper);
99+
labelSelectorForGVK(gvk).ifPresent(ls -> {
100+
log.debug("Registering label selector: {} for informer for gvk: {}", ls, gvk);
101+
configBuilder.withLabelSelector(ls);
102+
});
103+
104+
var newInformer = informerProducer.createInformer(configBuilder.build(), context);
84105

85106
return (InformerEventSource<GenericKubernetesResource, Glue>) context
86107
.eventSourceRetriever()
87-
.dynamicallyRegisterEventSource(gvk.toString(), newES);
108+
.dynamicallyRegisterEventSource(gvk.toString(), newInformer);
88109

89110
}
90111

@@ -147,4 +168,14 @@ private String workflowId(Glue glue) {
147168
return glue.getMetadata().getName() + "#" + glue.getMetadata().getNamespace();
148169
}
149170

171+
public Optional<String> labelSelectorForGVK(GroupVersionKind gvk) {
172+
return Optional.ofNullable(controllerConfig.resourceLabelSelector().get(toSimpleString(gvk)));
173+
}
174+
175+
public static String toSimpleString(GroupVersionKind gvk) {
176+
String groupVersion =
177+
gvk.getGroup() == null ? gvk.getVersion() : gvk.getGroup() + "/" + gvk.getVersion();
178+
return groupVersion + "#" + gvk.getKind();
179+
}
180+
150181
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.csviri.operator.glue;
2+
3+
import java.util.Map;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import io.fabric8.kubernetes.api.model.ConfigMap;
8+
import io.quarkus.test.junit.QuarkusTest;
9+
import io.quarkus.test.junit.QuarkusTestProfile;
10+
import io.quarkus.test.junit.TestProfile;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
import static org.awaitility.Awaitility.await;
14+
15+
@QuarkusTest
16+
@TestProfile(GlueResourceLabelSelectorTest.GlueResourceLabelSelectorTestProfile.class)
17+
public class GlueResourceLabelSelectorTest extends TestBase {
18+
19+
@Test
20+
void showCreatingResourceWithLabelSelectorAndLabel() {
21+
var glue = create(TestUtils.loadResoureFlow("/glue/SimpleGlueWithLabeledResource.yaml"));
22+
23+
// this serves more like a sample, hard to test here if the label selector is on informer
24+
await().untilAsserted(() -> {
25+
var cm = get(ConfigMap.class, "configmap");
26+
assertThat(cm).isNotNull();
27+
assertThat(cm.getMetadata().getLabels())
28+
.containsEntry("test-glue", "true");
29+
});
30+
31+
delete(glue);
32+
await().untilAsserted(() -> {
33+
var cm = get(ConfigMap.class, "configmap");
34+
assertThat(cm).isNull();
35+
});
36+
}
37+
38+
public static class GlueResourceLabelSelectorTestProfile implements QuarkusTestProfile {
39+
@Override
40+
public Map<String, String> getConfigOverrides() {
41+
return Map.of("glue.operator.resource-label-selector.v1#ConfigMap", "test-glue=true");
42+
}
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package io.csviri.operator.glue.reconciler.glue;
2+
3+
import java.util.Map;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import io.csviri.operator.glue.ControllerConfig;
8+
import io.csviri.operator.glue.customresource.glue.Glue;
9+
import io.fabric8.kubernetes.api.model.ConfigMap;
10+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
11+
import io.javaoperatorsdk.operator.api.reconciler.Context;
12+
import io.javaoperatorsdk.operator.processing.GroupVersionKind;
13+
import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
import static org.mockito.Mockito.*;
17+
18+
class InformerRegisterTest {
19+
20+
21+
public static final String LABEL_SELECTOR = "myapp=true";
22+
23+
@Test
24+
@SuppressWarnings("unchecked")
25+
void registersInformerWithLabelSelectorIfConfigured() {
26+
var gvk = GroupVersionKind.gvkFor(ConfigMap.class);
27+
var labelSelectors = Map.of(InformerRegister.toSimpleString(gvk),
28+
LABEL_SELECTOR);
29+
var config = mock(ControllerConfig.class);
30+
when(config.resourceLabelSelector()).thenReturn(labelSelectors);
31+
var informerProducer = mock(InformerProducer.class);
32+
var register = new InformerRegister(informerProducer, config);
33+
var mockContext = mock(Context.class);
34+
var mockEventSourceRetriever = mock(EventSourceRetriever.class);
35+
when(mockContext.eventSourceRetriever()).thenReturn(mockEventSourceRetriever);
36+
37+
register.registerInformer(mockContext, gvk, testGlue());
38+
39+
verify(informerProducer).createInformer(argThat(c -> {
40+
assertThat(c.getLabelSelector()).isEqualTo(LABEL_SELECTOR);
41+
return true;
42+
}), any());
43+
}
44+
45+
46+
@Test
47+
void gvkToSimpleString() {
48+
assertThat(InformerRegister.toSimpleString(new GroupVersionKind("apps", "v1", "Deployment")))
49+
.isEqualTo("apps/v1#Deployment");
50+
assertThat(InformerRegister.toSimpleString(new GroupVersionKind("v1", "ConfigMap")))
51+
.isEqualTo("v1#ConfigMap");
52+
}
53+
54+
Glue testGlue() {
55+
Glue glue = new Glue();
56+
glue.setMetadata(new ObjectMetaBuilder()
57+
.withName("test1")
58+
.build());
59+
return glue;
60+
}
61+
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Invalid GLUE, presents resources with non-unique name
2+
apiVersion: io.csviri.operator.glue/v1beta1
3+
kind: Glue
4+
metadata:
5+
name: glue
6+
spec:
7+
childResources:
8+
- name: configMap
9+
resource:
10+
apiVersion: v1
11+
kind: ConfigMap
12+
metadata:
13+
name: configmap
14+
labels:
15+
test-glue: true
16+
data:
17+
key: "value1"

0 commit comments

Comments
 (0)