Skip to content

Commit 73b1d8d

Browse files
committed
feat: customize mapping annotation in kubernetes dependent resource (#2075)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent d45328f commit 73b1d8d

File tree

9 files changed

+193
-15
lines changed

9 files changed

+193
-15
lines changed

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java

+14-9
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private SecondaryToPrimaryMapper<R> getSecondaryToPrimaryMapper() {
8888
return (SecondaryToPrimaryMapper<R>) this;
8989
} else if (garbageCollected) {
9090
return Mappers.fromOwnerReference();
91-
} else if (useDefaultAnnotationsToIdentifyPrimary()) {
91+
} else if (useNonOwnerRefBasedSecondaryToPrimaryMapping()) {
9292
return Mappers.fromDefaultAnnotations();
9393
} else {
9494
throw new OperatorException("Provide a SecondaryToPrimaryMapper to associate " +
@@ -238,8 +238,8 @@ protected Resource<R> prepare(R desired, P primary, String actionName) {
238238
protected void addReferenceHandlingMetadata(R desired, P primary) {
239239
if (addOwnerReference()) {
240240
desired.addOwnerReference(primary);
241-
} else if (useDefaultAnnotationsToIdentifyPrimary()) {
242-
addDefaultSecondaryToPrimaryMapperAnnotations(desired, primary);
241+
} else if (useNonOwnerRefBasedSecondaryToPrimaryMapping()) {
242+
addSecondaryToPrimaryMapperAnnotations(desired, primary);
243243
}
244244
}
245245

@@ -269,17 +269,22 @@ protected InformerEventSource<R, P> createEventSource(EventSourceContext<P> cont
269269
return eventSource().orElseThrow();
270270
}
271271

272-
private boolean useDefaultAnnotationsToIdentifyPrimary() {
273-
return !(this instanceof SecondaryToPrimaryMapper) && !garbageCollected && isCreatable();
272+
private boolean useNonOwnerRefBasedSecondaryToPrimaryMapping() {
273+
return !garbageCollected && isCreatable();
274274
}
275275

276-
private void addDefaultSecondaryToPrimaryMapperAnnotations(R desired, P primary) {
276+
protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary) {
277+
addSecondaryToPrimaryMapperAnnotations(desired, primary, Mappers.DEFAULT_ANNOTATION_FOR_NAME,
278+
Mappers.DEFAULT_ANNOTATION_FOR_NAMESPACE);
279+
}
280+
281+
protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary, String nameKey,
282+
String namespaceKey) {
277283
var annotations = desired.getMetadata().getAnnotations();
278-
annotations.put(Mappers.DEFAULT_ANNOTATION_FOR_NAME, primary.getMetadata().getName());
284+
annotations.put(nameKey, primary.getMetadata().getName());
279285
var primaryNamespaces = primary.getMetadata().getNamespace();
280286
if (primaryNamespaces != null) {
281-
annotations.put(
282-
Mappers.DEFAULT_ANNOTATION_FOR_NAMESPACE, primary.getMetadata().getNamespace());
287+
annotations.put(namespaceKey, primary.getMetadata().getNamespace());
283288
}
284289
}
285290

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/SecondaryToPrimaryMapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

77
@FunctionalInterface
88
public interface SecondaryToPrimaryMapper<R> {
9-
Set<ResourceID> toPrimaryResourceIDs(R dependentResource);
9+
Set<ResourceID> toPrimaryResourceIDs(R resource);
1010
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.RegisterExtension;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
8+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
9+
import io.javaoperatorsdk.operator.sample.dependentcustommappingannotation.CustomMappingConfigMapDependentResource;
10+
import io.javaoperatorsdk.operator.sample.dependentcustommappingannotation.DependentCustomMappingCustomResource;
11+
import io.javaoperatorsdk.operator.sample.dependentcustommappingannotation.DependentCustomMappingReconciler;
12+
import io.javaoperatorsdk.operator.sample.dependentcustommappingannotation.DependentCustomMappingSpec;
13+
14+
import static io.javaoperatorsdk.operator.sample.dependentcustommappingannotation.CustomMappingConfigMapDependentResource.CUSTOM_NAMESPACE_KEY;
15+
import static io.javaoperatorsdk.operator.sample.dependentcustommappingannotation.CustomMappingConfigMapDependentResource.CUSTOM_NAME_KEY;
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.awaitility.Awaitility.await;
18+
19+
class DependentCustomMappingAnnotationIT {
20+
21+
public static final String INITIAL_VALUE = "initial value";
22+
public static final String CHANGED_VALUE = "changed value";
23+
public static final String TEST_RESOURCE_NAME = "test1";
24+
25+
@RegisterExtension
26+
LocallyRunOperatorExtension extension =
27+
LocallyRunOperatorExtension.builder()
28+
.withReconciler(DependentCustomMappingReconciler.class)
29+
.build();
30+
31+
32+
@Test
33+
void testCustomMappingAnnotationForDependent() {
34+
var cr = extension.create(testResource());
35+
assertConfigMapData(INITIAL_VALUE);
36+
37+
cr.getSpec().setValue(CHANGED_VALUE);
38+
cr = extension.replace(cr);
39+
assertConfigMapData(CHANGED_VALUE);
40+
41+
extension.delete(cr);
42+
43+
await().untilAsserted(() -> {
44+
var resource = extension.get(ConfigMap.class, TEST_RESOURCE_NAME);
45+
assertThat(resource).isNull();
46+
});
47+
}
48+
49+
private void assertConfigMapData(String val) {
50+
await().untilAsserted(() -> {
51+
var resource = extension.get(ConfigMap.class, TEST_RESOURCE_NAME);
52+
assertThat(resource).isNotNull();
53+
assertThat(resource.getMetadata().getAnnotations())
54+
.containsKey(CUSTOM_NAME_KEY)
55+
.containsKey(CUSTOM_NAMESPACE_KEY);
56+
assertThat(resource.getData()).containsEntry(CustomMappingConfigMapDependentResource.KEY,
57+
val);
58+
});
59+
}
60+
61+
62+
DependentCustomMappingCustomResource testResource() {
63+
var dr = new DependentCustomMappingCustomResource();
64+
dr.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build());
65+
dr.setSpec(new DependentCustomMappingSpec());
66+
dr.getSpec().setValue(INITIAL_VALUE);
67+
68+
return dr;
69+
}
70+
71+
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.javaoperatorsdk.operator.sample.dependentcustommappingannotation;
2+
3+
import java.util.Map;
4+
import java.util.Set;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
8+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
9+
import io.javaoperatorsdk.operator.api.reconciler.Context;
10+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource;
11+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
12+
import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper;
13+
import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;
14+
15+
public class CustomMappingConfigMapDependentResource
16+
extends CRUDNoGCKubernetesDependentResource<ConfigMap, DependentCustomMappingCustomResource>
17+
implements SecondaryToPrimaryMapper<ConfigMap> {
18+
19+
public static final String CUSTOM_NAME_KEY = "customNameKey";
20+
public static final String CUSTOM_NAMESPACE_KEY = "customNamespaceKey";
21+
public static final String KEY = "key";
22+
23+
private SecondaryToPrimaryMapper<ConfigMap> mapper =
24+
Mappers.fromAnnotation(CUSTOM_NAME_KEY, CUSTOM_NAMESPACE_KEY);
25+
26+
public CustomMappingConfigMapDependentResource() {
27+
super(ConfigMap.class);
28+
}
29+
30+
@Override
31+
protected ConfigMap desired(DependentCustomMappingCustomResource primary,
32+
Context<DependentCustomMappingCustomResource> context) {
33+
return new ConfigMapBuilder()
34+
.withMetadata(new ObjectMetaBuilder()
35+
.withName(primary.getMetadata().getName())
36+
.withNamespace(primary.getMetadata().getNamespace())
37+
.build())
38+
.withData(Map.of(KEY, primary.getSpec().getValue()))
39+
.build();
40+
}
41+
42+
@Override
43+
public Set<ResourceID> toPrimaryResourceIDs(ConfigMap resource) {
44+
return mapper.toPrimaryResourceIDs(resource);
45+
}
46+
47+
@Override
48+
protected void addSecondaryToPrimaryMapperAnnotations(ConfigMap desired,
49+
DependentCustomMappingCustomResource primary) {
50+
addSecondaryToPrimaryMapperAnnotations(desired, primary, CUSTOM_NAME_KEY, CUSTOM_NAMESPACE_KEY);
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.javaoperatorsdk.operator.sample.dependentcustommappingannotation;
2+
3+
import io.fabric8.kubernetes.api.model.Namespaced;
4+
import io.fabric8.kubernetes.client.CustomResource;
5+
import io.fabric8.kubernetes.model.annotation.Group;
6+
import io.fabric8.kubernetes.model.annotation.Version;
7+
8+
@Group("sample.javaoperatorsdk")
9+
@Version("v1")
10+
public class DependentCustomMappingCustomResource
11+
extends CustomResource<DependentCustomMappingSpec, Void>
12+
implements Namespaced {
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.javaoperatorsdk.operator.sample.dependentcustommappingannotation;
2+
3+
import io.javaoperatorsdk.operator.api.reconciler.*;
4+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
5+
6+
@ControllerConfiguration(
7+
dependents = {@Dependent(type = CustomMappingConfigMapDependentResource.class)})
8+
public class DependentCustomMappingReconciler
9+
implements Reconciler<DependentCustomMappingCustomResource> {
10+
11+
@Override
12+
public UpdateControl<DependentCustomMappingCustomResource> reconcile(
13+
DependentCustomMappingCustomResource resource,
14+
Context<DependentCustomMappingCustomResource> context) throws Exception {
15+
16+
return UpdateControl.noUpdate();
17+
}
18+
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample.dependentcustommappingannotation;
2+
3+
public class DependentCustomMappingSpec {
4+
5+
private String value;
6+
7+
public String getValue() {
8+
return value;
9+
}
10+
11+
public DependentCustomMappingSpec setValue(String value) {
12+
this.value = value;
13+
return this;
14+
}
15+
}

Diff for: operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public ReadOnlyConfigMapDependent() {
3131
}
3232

3333
@Override
34-
public Set<ResourceID> toPrimaryResourceIDs(ConfigMap dependentResource) {
35-
return cache.byIndex(CONFIG_MAP_RELATION_INDEXER, dependentResource.getMetadata().getName())
34+
public Set<ResourceID> toPrimaryResourceIDs(ConfigMap resource) {
35+
return cache.byIndex(CONFIG_MAP_RELATION_INDEXER, resource.getMetadata().getName())
3636
.stream()
3737
.map(ResourceID::fromResource)
3838
.collect(Collectors.toSet());

Diff for: sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SecretDependentResource.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ public Result<Secret> match(Secret actual, MySQLSchema primary, Context<MySQLSch
6161
}
6262

6363
@Override
64-
public Set<ResourceID> toPrimaryResourceIDs(Secret dependentResource) {
65-
String name = dependentResource.getMetadata().getName();
64+
public Set<ResourceID> toPrimaryResourceIDs(Secret resource) {
65+
String name = resource.getMetadata().getName();
6666
return Set.of(new ResourceID(name.substring(0, name.length() - SECRET_SUFFIX.length()),
67-
dependentResource.getMetadata().getNamespace()));
67+
resource.getMetadata().getNamespace()));
6868
}
6969
}

0 commit comments

Comments
 (0)