diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java index b6c2d976e3..a6bf6dd4e8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java @@ -14,10 +14,15 @@ public static ResourceID fromResource(HasMetadata resource) { } public static Optional fromFirstOwnerReference(HasMetadata resource) { + return fromFirstOwnerReference(resource, false); + } + + public static Optional fromFirstOwnerReference(HasMetadata resource, + boolean clusterScoped) { var ownerReferences = resource.getMetadata().getOwnerReferences(); if (!ownerReferences.isEmpty()) { return Optional.of(new ResourceID(ownerReferences.get(0).getName(), - resource.getMetadata().getNamespace())); + clusterScoped ? null : resource.getMetadata().getNamespace())); } else { return Optional.empty(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java index 5764e5f888..8ec9b42af0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java @@ -42,7 +42,17 @@ public static SecondaryToPrimaryMapper fromLabel( } public static SecondaryToPrimaryMapper fromOwnerReference() { - return resource -> ResourceID.fromFirstOwnerReference(resource).map(Set::of) + return fromOwnerReference(false); + } + + /** + * @param clusterScope if the owner is a cluster scoped resource + * @return mapper + * @param type of the secondary resource, where the owner reference is + */ + public static SecondaryToPrimaryMapper fromOwnerReference( + boolean clusterScope) { + return resource -> ResourceID.fromFirstOwnerReference(resource, clusterScope).map(Set::of) .orElseGet(Collections::emptySet); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/clusterscopedresource/ClusterScopedCustomResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/clusterscopedresource/ClusterScopedCustomResourceReconciler.java index edbf22a69f..9d4a4fbd74 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/clusterscopedresource/ClusterScopedCustomResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/clusterscopedresource/ClusterScopedCustomResourceReconciler.java @@ -6,23 +6,36 @@ import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; @ControllerConfiguration public class ClusterScopedCustomResourceReconciler - implements Reconciler, Cleaner, - KubernetesClientAware { + implements Reconciler, + KubernetesClientAware, EventSourceInitializer { public static final String DATA_KEY = "data-key"; + public static final String TEST_LABEL_VALUE = "clusterscopecrtest"; + public static final String TEST_LABEL_KEY = "test"; + private KubernetesClient client; @Override public UpdateControl reconcile( ClusterScopedCustomResource resource, Context context) { - client.configMaps().resource(desired(resource)).createOrReplace(); + var optionalConfigMap = context.getSecondaryResource(ConfigMap.class); + + optionalConfigMap.ifPresentOrElse(cm -> { + if (!resource.getSpec().getData().equals(cm.getData().get(DATA_KEY))) { + client.configMaps().resource(desired(resource)).replace(); + } + }, () -> client.configMaps().resource(desired(resource)).create()); resource.setStatus(new ClusterScopedCustomResourceStatus()); resource.getStatus().setCreated(true); @@ -30,13 +43,16 @@ public UpdateControl reconcile( } private ConfigMap desired(ClusterScopedCustomResource resource) { - return new ConfigMapBuilder() + var cm = new ConfigMapBuilder() .withMetadata(new ObjectMetaBuilder() .withName(resource.getMetadata().getName()) .withNamespace(resource.getSpec().getTargetNamespace()) + .withLabels(Map.of(TEST_LABEL_KEY, TEST_LABEL_VALUE)) .build()) .withData(Map.of(DATA_KEY, resource.getSpec().getData())) .build(); + cm.addOwnerReference(resource); + return cm; } @Override @@ -50,9 +66,12 @@ public void setKubernetesClient(KubernetesClient kubernetesClient) { } @Override - public DeleteControl cleanup(ClusterScopedCustomResource resource, - Context context) { - client.configMaps().resource(desired(resource)).delete(); - return DeleteControl.defaultDelete(); + public Map prepareEventSources( + EventSourceContext context) { + var ies = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .withSecondaryToPrimaryMapper(Mappers.fromOwnerReference(true)) + .withLabelSelector(TEST_LABEL_KEY + "=" + TEST_LABEL_VALUE) + .build(), context); + return EventSourceInitializer.nameEventSources(ies); } }