diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 5d3fadc762..c3e5bfbe96 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -12,7 +12,7 @@ on: http-client: type: string required: false - default: 'okhttp' + default: 'jdk' experimental: type: boolean required: false @@ -22,7 +22,7 @@ jobs: integration_tests: runs-on: ubuntu-latest continue-on-error: ${{ inputs.experimental }} - timeout-minutes: 20 + timeout-minutes: 40 steps: - name: Output test information run: echo "Running ITs with ${{ inputs.http-client }}, ${{ inputs.kube-version }}, ${{ inputs.java-version }}" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8206b0d43a..d4c48c5683 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -34,7 +34,7 @@ jobs: integration_tests: strategy: matrix: - java: [ 11, 17 ] + java: [ 17, 21 ] kubernetes: [ 'v1.26.13', 'v1.27.10', 'v1.28.6', 'v1.29.1' ] uses: ./.github/workflows/integration-tests.yml with: @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 11, 17 ] + java: [ 17, 21 ] steps: - uses: actions/checkout@v4 - name: Set up Java and Maven diff --git a/.github/workflows/release-project-in-dir.yml b/.github/workflows/release-project-in-dir.yml index b271f05f02..dc79b6f6c2 100644 --- a/.github/workflows/release-project-in-dir.yml +++ b/.github/workflows/release-project-in-dir.yml @@ -26,7 +26,7 @@ jobs: - name: Set up Java and Maven uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: temurin cache: 'maven' @@ -61,7 +61,7 @@ jobs: - name: Set up Java and Maven uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: temurin cache: 'maven' diff --git a/.github/workflows/snapshot-releases.yml b/.github/workflows/snapshot-releases.yml index e5aff55e62..66fe9d25a3 100644 --- a/.github/workflows/snapshot-releases.yml +++ b/.github/workflows/snapshot-releases.yml @@ -21,7 +21,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: temurin - java-version: 11 + java-version: 17 cache: 'maven' - name: Build and test project run: ./mvnw ${MAVEN_ARGS} clean install --file pom.xml @@ -34,7 +34,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: temurin - java-version: 11 + java-version: 17 cache: 'maven' - name: Release Maven package uses: samuelmeuli/action-maven-publish@v1 diff --git a/bootstrapper-maven-plugin/pom.xml b/bootstrapper-maven-plugin/pom.xml index 54eff9484f..8920b78de7 100644 --- a/bootstrapper-maven-plugin/pom.xml +++ b/bootstrapper-maven-plugin/pom.xml @@ -5,7 +5,7 @@ java-operator-sdk io.javaoperatorsdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT bootstrapper @@ -39,7 +39,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl test diff --git a/bootstrapper-maven-plugin/src/main/resources/templates/Reconciler.java b/bootstrapper-maven-plugin/src/main/resources/templates/Reconciler.java index 03ac06f882..6d03196fe9 100644 --- a/bootstrapper-maven-plugin/src/main/resources/templates/Reconciler.java +++ b/bootstrapper-maven-plugin/src/main/resources/templates/Reconciler.java @@ -5,19 +5,20 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; +import io.javaoperatorsdk.operator.api.reconciler.Workflow; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import java.util.Map; import java.util.Optional; -@ControllerConfiguration(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) +@ControllerConfiguration public class {{artifactClassId}}Reconciler implements Reconciler<{{artifactClassId}}CustomResource> { public UpdateControl<{{artifactClassId}}CustomResource> reconcile({{artifactClassId}}CustomResource primary, diff --git a/bootstrapper-maven-plugin/src/main/resources/templates/pom.xml b/bootstrapper-maven-plugin/src/main/resources/templates/pom.xml index d2ffb66b46..bf0e91d383 100644 --- a/bootstrapper-maven-plugin/src/main/resources/templates/pom.xml +++ b/bootstrapper-maven-plugin/src/main/resources/templates/pom.xml @@ -11,7 +11,7 @@ jar - 11 + 17 ${java.version} ${java.version} {{josdkVersion}} @@ -64,7 +64,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl ${log4j.version} diff --git a/caffeine-bounded-cache-support/pom.xml b/caffeine-bounded-cache-support/pom.xml index dcb293e86d..fa3cec762e 100644 --- a/caffeine-bounded-cache-support/pom.xml +++ b/caffeine-bounded-cache-support/pom.xml @@ -5,18 +5,13 @@ java-operator-sdk io.javaoperatorsdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT 4.0.0 caffeine-bounded-cache-support Operator SDK - Caffeine Bounded Cache Support - - 11 - 11 - - io.javaoperatorsdk @@ -44,7 +39,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl test diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index b6e3ba2c8f..7a53db8bd9 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -28,7 +28,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; public abstract class AbstractTestReconciler

> - implements Reconciler

, EventSourceInitializer

{ + implements Reconciler

{ private static final Logger log = LoggerFactory.getLogger(BoundedCacheClusterScopeTestReconciler.class); @@ -82,7 +82,7 @@ public Map prepareEventSources( Mappers.fromOwnerReference(this instanceof BoundedCacheClusterScopeTestReconciler)) .build(), context); - return EventSourceInitializer.nameEventSources(es); + return EventSourceUtils.nameEventSources(es); } private void ensureStatus(P resource) { diff --git a/docs/_data/sidebar.yml b/docs/_data/sidebar.yml index 8c083a6cc1..01918ca31f 100644 --- a/docs/_data/sidebar.yml +++ b/docs/_data/sidebar.yml @@ -32,4 +32,6 @@ - title: Migrating from v4.3 to v4.4 url: /docs/v4-4-migration - title: Migrating from v4.4 to v4.5 - url: /docs/v4-5-migration \ No newline at end of file + url: /docs/v4-5-migration + - title: Migrating from v4.7 to v5.0 + url: /docs/v5-0-migration diff --git a/docs/documentation/dependent-resources.md b/docs/documentation/dependent-resources.md index 61e18fd3f2..db7a00fda8 100644 --- a/docs/documentation/dependent-resources.md +++ b/docs/documentation/dependent-resources.md @@ -101,13 +101,13 @@ and labels, which are ignored by default: ```java public class MyDependentResource extends KubernetesDependentResource - implements Matcher { - // your implementation + implements Matcher { + // your implementation - public Result match(MyDependent actualResource, MyPrimary primary, - Context context) { - return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, true); - } + public Result match(MyDependent actualResource, MyPrimary primary, + Context context) { + return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, true); + } } ``` @@ -141,24 +141,24 @@ Deleted (or set to be garbage collected). The following example shows how to cre @KubernetesDependent(labelSelector = WebPageManagedDependentsReconciler.SELECTOR) class DeploymentDependentResource extends CRUDKubernetesDependentResource { - public DeploymentDependentResource() { - super(Deployment.class); - } - - @Override - protected Deployment desired(WebPage webPage, Context context) { - var deploymentName = deploymentName(webPage); - Deployment deployment = loadYaml(Deployment.class, getClass(), "deployment.yaml"); - deployment.getMetadata().setName(deploymentName); - deployment.getMetadata().setNamespace(webPage.getMetadata().getNamespace()); - deployment.getSpec().getSelector().getMatchLabels().put("app", deploymentName); - - deployment.getSpec().getTemplate().getMetadata().getLabels() - .put("app", deploymentName); - deployment.getSpec().getTemplate().getSpec().getVolumes().get(0) - .setConfigMap(new ConfigMapVolumeSourceBuilder().withName(configMapName(webPage)).build()); - return deployment; - } + public DeploymentDependentResource() { + super(Deployment.class); + } + + @Override + protected Deployment desired(WebPage webPage, Context context) { + var deploymentName = deploymentName(webPage); + Deployment deployment = loadYaml(Deployment.class, getClass(), "deployment.yaml"); + deployment.getMetadata().setName(deploymentName); + deployment.getMetadata().setNamespace(webPage.getMetadata().getNamespace()); + deployment.getSpec().getSelector().getMatchLabels().put("app", deploymentName); + + deployment.getSpec().getTemplate().getMetadata().getLabels() + .put("app", deploymentName); + deployment.getSpec().getTemplate().getSpec().getVolumes().get(0) + .setConfigMap(new ConfigMapVolumeSourceBuilder().withName(configMapName(webPage)).build()); + return deployment; + } } ``` @@ -194,25 +194,25 @@ instances are managed by JOSDK, an example of which can be seen below: ```java @ControllerConfiguration( - labelSelector = SELECTOR, - dependents = { - @Dependent(type = ConfigMapDependentResource.class), - @Dependent(type = DeploymentDependentResource.class), - @Dependent(type = ServiceDependentResource.class) - }) + labelSelector = SELECTOR, + dependents = { + @Dependent(type = ConfigMapDependentResource.class), + @Dependent(type = DeploymentDependentResource.class), + @Dependent(type = ServiceDependentResource.class) + }) public class WebPageManagedDependentsReconciler - implements Reconciler, ErrorStatusHandler { + implements Reconciler, ErrorStatusHandler { - // omitted code + // omitted code - @Override - public UpdateControl reconcile(WebPage webPage, Context context) { + @Override + public UpdateControl reconcile(WebPage webPage, Context context) { - final var name = context.getSecondaryResource(ConfigMap.class).orElseThrow() - .getMetadata().getName(); - webPage.setStatus(createStatus(name)); - return UpdateControl.patchStatus(webPage); - } + final var name = context.getSecondaryResource(ConfigMap.class).orElseThrow() + .getMetadata().getName(); + webPage.setStatus(createStatus(name)); + return UpdateControl.patchStatus(webPage); + } } ``` @@ -227,104 +227,11 @@ It is also possible to wire dependent resources programmatically. In practice th developer is responsible for initializing and managing the dependent resources as well as calling their `reconcile` method. However, this makes it possible for developers to fully customize the reconciliation process. Standalone dependent resources should be used in cases when the managed use -case does not fit. - -Note that [Workflows](https://javaoperatorsdk.io/docs/workflows) also can be invoked from standalone -resources. +case does not fit. You can, of course, also use [Workflows](https://javaoperatorsdk.io/docs/workflows) when managing +resources programmatically. -The following sample is similar to the one above, simply performing additional checks, and -conditionally creating an `Ingress`: - -```java - -@ControllerConfiguration -public class WebPageStandaloneDependentsReconciler - implements Reconciler, ErrorStatusHandler, - EventSourceInitializer { - - private KubernetesDependentResource configMapDR; - private KubernetesDependentResource deploymentDR; - private KubernetesDependentResource serviceDR; - private KubernetesDependentResource ingressDR; - - public WebPageStandaloneDependentsReconciler(KubernetesClient kubernetesClient) { - // 1. - createDependentResources(kubernetesClient); - } - - @Override - public List prepareEventSources(EventSourceContext context) { - // 2. - return List.of( - configMapDR.initEventSource(context), - deploymentDR.initEventSource(context), - serviceDR.initEventSource(context)); - } - - @Override - public UpdateControl reconcile(WebPage webPage, Context context) { - - // 3. - if (!isValidHtml(webPage.getHtml())) { - return UpdateControl.patchStatus(setInvalidHtmlErrorMessage(webPage)); - } - - // 4. - configMapDR.reconcile(webPage, context); - deploymentDR.reconcile(webPage, context); - serviceDR.reconcile(webPage, context); - - // 5. - if (Boolean.TRUE.equals(webPage.getSpec().getExposed())) { - ingressDR.reconcile(webPage, context); - } else { - ingressDR.delete(webPage, context); - } - - // 6. - webPage.setStatus( - createStatus(configMapDR.getResource(webPage).orElseThrow().getMetadata().getName())); - return UpdateControl.patchStatus(webPage); - } - - private void createDependentResources(KubernetesClient client) { - this.configMapDR = new ConfigMapDependentResource(); - this.deploymentDR = new DeploymentDependentResource(); - this.serviceDR = new ServiceDependentResource(); - this.ingressDR = new IngressDependentResource(); - - Arrays.asList(configMapDR, deploymentDR, serviceDR, ingressDR).forEach(dr -> { - dr.setKubernetesClient(client); - dr.configureWith(new KubernetesDependentResourceConfig() - .setLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR)); - }); - } - - // omitted code -} -``` - -There are multiple things happening here: - -1. Dependent resources are explicitly created and can be access later by reference. -2. Event sources are produced by the dependent resources, but needs to be explicitly registered in - this case by implementing - the [`EventSourceInitializer`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java) - interface. -3. The input html is validated, and error message is set in case it is invalid. -4. Reconciliation of dependent resources is called explicitly, but here the workflow - customization is fully in the hand of the developer. -5. An `Ingress` is created but only in case `exposed` flag set to true on custom resource. Tries to - delete it if not. -6. Status is set in a different way, this is just an alternative way to show, that the actual state - can be read using the reference. This could be written in a same way as in the managed example. - -See the full source code of -sample [here](https://github.com/operator-framework/java-operator-sdk/blob/main/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java) -. - -Note also the Workflows feature makes it possible to also support this conditional creation use -case in managed dependent resources. +You can see a commented example of how to do +so [here](https://github.com/operator-framework/java-operator-sdk/blob/main/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java). ## Creating/Updating Kubernetes Resources @@ -357,17 +264,17 @@ Since SSA is a complex feature, JOSDK implements a feature flag allowing users t these implementations. See in [ConfigurationService](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L332-L358). -It is, however, important to note that these implementations are default, generic -implementations that the framework can provide expected behavior out of the box. In many -situations, these will work just fine but it is also possible to provide matching algorithms +It is, however, important to note that these implementations are default, generic +implementations that the framework can provide expected behavior out of the box. In many +situations, these will work just fine but it is also possible to provide matching algorithms optimized for specific use cases. This is easily done by simply overriding -the `match(...)` [method](https://github.com/java-operator-sdk/java-operator-sdk/blob/e16559fd41bbb8bef6ce9d1f47bffa212a941b09/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java#L156-L156). +the `match(...)` [method](https://github.com/java-operator-sdk/java-operator-sdk/blob/e16559fd41bbb8bef6ce9d1f47bffa212a941b09/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java#L156-L156). -It is also possible to bypass the matching logic altogether to simply rely on the server-side +It is also possible to bypass the matching logic altogether to simply rely on the server-side apply mechanism if always sending potentially unchanged resources to the cluster is not an issue. JOSDK's matching mechanism allows to spare some potentially useless calls to the Kubernetes API -server. To bypass the matching feature completely, simply override the `match` method to always -return `false`, thus telling JOSDK that the actual state never matches the desired one, making +server. To bypass the matching feature completely, simply override the `match` method to always +return `false`, thus telling JOSDK that the actual state never matches the desired one, making it always update the resources using SSA. WARNING: Older versions of Kubernetes before 1.25 would create an additional resource version for every SSA update @@ -394,20 +301,31 @@ tests [here](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/op When dealing with multiple dependent resources of same type, the dependent resource implementation needs to know which specific resource should be targeted when reconciling a given dependent -resource, since there will be multiple instances of that type which could possibly be used, each -associated with the same primary resource. In order to do this, JOSDK relies on the -[resource discriminator](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java) -concept. Resource discriminators uniquely identify the target resource of a dependent resource. -In the managed Kubernetes dependent resources case, the discriminator can be declaratively set -using the `@KubernetesDependent` annotation: - -```java - -@KubernetesDependent(resourceDiscriminator = ConfigMap1Discriminator.class) -public class MultipleManagedDependentResourceConfigMap1 { -//... -} -``` +resource, since there could be multiple instances of that type which could possibly be used, each +associated with the same primary resource. In this situation, JOSDK automatically selects the appropriate secondary +resource matching the desired state associated with the primary resource. This makes sense because the desired +state computation already needs to be able to discriminate among multiple related secondary resources to tell JOSDK how +they should be reconciled. + +There might be casees, though, where it might be problematic to call the `desired` method several times (for example, because it is costly to do so), it is always possible to override this automated discrimination using several means: + +- Implement your own `getSecondaryResource` method on your `DependentResource` implementation from scratch. +- Override the `selectManagedSecondaryResource` method, if your `DependentResource` extends `AbstractDependentResource`. + This should be relatively simple to override this method to optimize the matching to your needs. You can see an + example of such an implementation in + the [`ExternalWithStateDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/6cd0f884a7c9b60c81bd2d52da54adbd64d6e118/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java#L43-L49) + class. +- Override the `managedSecondaryResourceID` method, if your `DependentResource` extends `KubernetesDependentResource`, + where it's very often possible to easily determine the `ResourceID` of the secondary resource. This would probably be + the easiest solution if you're working with Kubernetes resources. +- Configure + a [`ResourceDiscriminator`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java) + implementation for your `DependentResource`. This was the approach that was used before JOSDK v5 but should not be + needed anymore as it is simpler and more efficient to override one the methods above instead of creating a separate + class. Discriminators can be declaratively set when using managed Kubernetes dependent resources via + the `resourceDiscriminator` field of the `@KubernetesDependent` annotation. + +### Sharing an Event Source Between Dependent Resources Dependent resources usually also provide event sources. When dealing with multiple dependents of the same type, one needs to decide whether these dependent resources should track the same @@ -423,10 +341,10 @@ would look as follows: useEventSourceWithName = "configMapSource") ``` -A sample is provided as an integration test both -for [managed](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java) -and -for [standalone](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java) +A sample is provided as an integration test both: +for [managed](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java) + +For [standalone](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java) cases. ## Bulk Dependent Resources @@ -489,15 +407,18 @@ also be created, one per dependent resource. See [integration test](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java) as a sample. - ## GenericKubernetesResource based Dependent Resources -In rare circumstances resource handling where there is no class representation or just typeless handling might be needed. -Fabric8 Client provides [GenericKubernetesResource](https://github.com/fabric8io/kubernetes-client/blob/main/doc/CHEATSHEET.md#resource-typeless-api) -to support that. +In rare circumstances resource handling where there is no class representation or just typeless handling might be +needed. +Fabric8 Client +provides [GenericKubernetesResource](https://github.com/fabric8io/kubernetes-client/blob/main/doc/CHEATSHEET.md#resource-typeless-api) +to support that. -For dependent resource this is supported by [GenericKubernetesDependentResource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java#L8-L8) -. See samples [here](https://github.com/java-operator-sdk/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource). +For dependent resource this is supported +by [GenericKubernetesDependentResource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java#L8-L8) +. See +samples [here](https://github.com/java-operator-sdk/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource). ## Other Dependent Resource Features diff --git a/docs/documentation/features.md b/docs/documentation/features.md index 564b5233f5..3a3eb0c876 100644 --- a/docs/documentation/features.md +++ b/docs/documentation/features.md @@ -66,7 +66,8 @@ and/or re-schedule a reconciliation with a desired time delay: @Override public UpdateControl reconcile( EventSourceTestCustomResource resource, Context context) { - ... + // omitted code + return UpdateControl.patchStatus(resource).rescheduleAfter(10, TimeUnit.SECONDS); } ``` @@ -77,7 +78,8 @@ without an update: @Override public UpdateControl reconcile( EventSourceTestCustomResource resource, Context context) { - ... + // omitted code + return UpdateControl.noUpdate().rescheduleAfter(10, TimeUnit.SECONDS); } ``` @@ -108,7 +110,8 @@ resource are cleaned up in `cleanup` implementation. ```java public DeleteControl cleanup(MyCustomResource customResource,Context context){ - ... + // omitted code + return DeleteControl.defaultDelete(); } @@ -192,10 +195,7 @@ the [WebPage example](https://github.com/java-operator-sdk/java-operator-sdk/blo ```java public class WebPageStatus extends ObservedGenerationAwareStatus { - - private String htmlConfigMap; - - ... + // omitted code } ``` @@ -247,11 +247,12 @@ for reconciling deployments. public class DeploymentReconciler implements Reconciler, TestExecutionInfoProvider { - @Override - public UpdateControl reconcile( - Deployment resource, Context context) { - ... - } + @Override + public UpdateControl reconcile( + Deployment resource, Context context) { + // omitted code + } +} ``` ## Max Interval Between Reconciliations @@ -269,6 +270,7 @@ standard annotation: @ControllerConfiguration(maxReconciliationInterval = @MaxReconciliationInterval( interval = 50, timeUnit = TimeUnit.MILLISECONDS)) +public class MyReconciler implements Reconciler {} ``` The event is not propagated at a fixed rate, rather it's scheduled after each reconciliation. So the @@ -491,9 +493,8 @@ related [method](https://github.com/java-operator-sdk/java-operator-sdk/blob/mai ### Registering Event Sources -To register event sources, your `Reconciler` has to implement the -[`EventSourceInitializer`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java) -interface and initialize a list of event sources to register. One way to see this in action is +To register event sources, your `Reconciler` has to override the `prepareEventSources` and return +list of event sources to register. One way to see this in action is to look at the [tomcat example](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java) (irrelevant details omitted): @@ -501,8 +502,10 @@ to look at the ```java @ControllerConfiguration -public class TomcatReconciler implements Reconciler, EventSourceInitializer { +public class TomcatReconciler implements Reconciler { + // omitted code + @Override public List prepareEventSources(EventSourceContext context) { var configMapEventSource = @@ -511,9 +514,9 @@ public class TomcatReconciler implements Reconciler, EventSourceInitiali .withSecondaryToPrimaryMapper( Mappers.fromAnnotation(ANNOTATION_NAME, ANNOTATION_NAMESPACE) .build(), context)); - return EventSourceInitializer.nameEventSources(configMapEventSource); + return EventSourceUtils.nameEventSources(configMapEventSource); } - ... + } ``` @@ -657,15 +660,15 @@ registering an associated `Informer` and then calling the `changeNamespaces` met ```java -public static void main(String[]args)throws IOException{ - KubernetesClient client=new DefaultKubernetesClient(); - Operator operator=new Operator(client); - RegisteredController registeredController=operator.register(new WebPageReconciler(client)); - operator.installShutdownHook(); - operator.start(); +public static void main(String[] args) { + KubernetesClient client = new DefaultKubernetesClient(); + Operator operator = new Operator(client); + RegisteredController registeredController = operator.register(new WebPageReconciler(client)); + operator.installShutdownHook(); + operator.start(); - // call registeredController further while operator is running - } + // call registeredController further while operator is running +} ``` @@ -677,8 +680,7 @@ configured appropriately so that the `followControllerNamespaceChanges` method r ```java @ControllerConfiguration -public class MyReconciler - implements Reconciler, EventSourceInitializer { +public class MyReconciler implements Reconciler { @Override public Map prepareEventSources( @@ -689,7 +691,7 @@ public class MyReconciler .withNamespacesInheritedFromController(context) .build(), context); - return EventSourceInitializer.nameEventSources(configMapES); + return EventSourceUtils.nameEventSources(configMapES); } } @@ -768,8 +770,8 @@ You can use a different implementation by overriding the default one provided by follows: ```java -Metrics metrics= …; -Operator operator = new Operator(client, o -> o.withMetrics()); +Metrics metrics; // initialize your metrics implementation +Operator operator = new Operator(client, o -> o.withMetrics(metrics)); ``` ### Micrometer implementation @@ -785,8 +787,8 @@ To create a `MicrometerMetrics` implementation that behaves how it has historica instance via: ```java -MeterRegistry registry= …; -Metrics metrics=new MicrometerMetrics(registry) +MeterRegistry registry; // initialize your registry implementation +Metrics metrics = new MicrometerMetrics(registry); ``` Note, however, that this constructor is deprecated and we encourage you to use the factory methods instead, which either @@ -802,7 +804,7 @@ basis, deleting the associated meters after 5 seconds when a resource is deleted MicrometerMetrics.newPerResourceCollectingMicrometerMetricsBuilder(registry) .withCleanUpDelayInSeconds(5) .withCleaningThreadNumber(2) - .build() + .build(); ``` The micrometer implementation records the following metrics: diff --git a/docs/documentation/v5-0-migration.md b/docs/documentation/v5-0-migration.md new file mode 100644 index 0000000000..b34a7a6b21 --- /dev/null +++ b/docs/documentation/v5-0-migration.md @@ -0,0 +1,28 @@ +--- +title: Migrating from v4.7 to v5.0 +description: Migrating from v4.7 to v5.0 +layout: docs +permalink: /docs/v5-0-migration +--- + +# Migrating from v4.7 to v5.0 + +## API Tweaks + +1. [Result of managed dependent resources](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceContext.java#L55-L57) + is not `Optional` anymore. In case you use this result, simply use the result + objects directly. +2. `EventSourceInitializer` is not a separate interface anymore. It is part of the `Reconciler` interface with a + default implementation. You can simply remove this interface from your reconciler. The + [`EventSourceUtils`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java#L11-L11) + now contains all the utility methods used for event sources naming that were previously defined in + the `EventSourceInitializer` interface. +3. Patching status through `UpdateControl` like the `patchStatus` method now by default + uses Server Side Apply instead of simple patch. To use the former approach, use the feature flag + in [`ConfigurationService`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L400-L400) + !!! IMPORTANT !!! + Migration from a non-SSA based controller to SSA based controller can cause problems, due to known issues. + See the following [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchSSAMigrationIT.java#L71-L82) where it is demonstrated. + Also, the related part of a [workaround](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchSSAMigrationIT.java#L110-L116). +4. `ManagedDependentResourceContext` has been renamed to `ManagedWorkflowAndDependentResourceContext` and is accessed + via the accordingly renamed `managedWorkflowAndDependentResourceContext` method. diff --git a/docs/documentation/workflows.md b/docs/documentation/workflows.md index 7190f8e8e3..ea83016430 100644 --- a/docs/documentation/workflows.md +++ b/docs/documentation/workflows.md @@ -122,7 +122,7 @@ page sample): @ControllerConfiguration( labelSelector = WebPageDependentsWorkflowReconciler.DEPENDENT_RESOURCE_LABEL_SELECTOR) public class WebPageDependentsWorkflowReconciler - implements Reconciler, ErrorStatusHandler, EventSourceInitializer { + implements Reconciler, ErrorStatusHandler { public static final String DEPENDENT_RESOURCE_LABEL_SELECTOR = "!low-level"; private static final Logger log = @@ -147,7 +147,7 @@ public class WebPageDependentsWorkflowReconciler @Override public Map prepareEventSources(EventSourceContext context) { - return EventSourceInitializer.nameEventSources( + return EventSourceUtils.nameEventSources( configMapDR.initEventSource(context), deploymentDR.initEventSource(context), serviceDR.initEventSource(context), @@ -338,6 +338,34 @@ and NOT `CRUDKubernetesDependentResource` since otherwise the Kubernetes Garbage In other words if a Kubernetes Dependent Resource depends on another dependent resource, it should not implement `GargageCollected` interface, otherwise the deletion order won't be guaranteed. + +## Explicit Managed Workflow Invocation + +Managed workflows, i.e. ones that are declared via annotations and therefore completely managed by JOSDK, are reconciled +before the primary resource. Each dependent resource that can be reconciled (according to the workflow configuration) +will therefore be reconciled before the primary reconciler is called to reconcile the primary resource. There are, +however, situations where it would be be useful to perform additional steps before the workflow is reconciled, for +example to validate the current state, execute arbitrary logic or even skip reconciliation altogether. Explicit +invocation of managed workflow was therefore introduced to solve these issues. + +To use this feature, you need to set the `explicitInvocation` field to `true` on the `@Workflow` annotation and then +call the `reconcileManagedWorkflow` method from the ` +ManagedWorkflowAndDependentResourceContext` retrieved from the reconciliation `Context` provided as part of your primary +resource reconciler `reconcile` method arguments. + +See +related [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitInvocationIT.java) +for more details. + +For `cleanup`, if the `Cleaner` interface is implemented, the `cleanupManageWorkflow()` needs to be called explicitly. +However, if `Cleaner` interface is not implemented, it will be called implicitly. +See +related [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitCleanupIT.java). + +While nothing prevents calling the workflow multiple times in a reconciler, it isn't typical or even recommended to do +so. Conversely, if explicit invocation is requested but `reconcileManagedWorkflow` is not called in the primary resource +reconciler, the workflow won't be reconciled at all. + ## Notes and Caveats - Delete is almost always called on every resource during the cleanup. However, it might be the case diff --git a/micrometer-support/pom.xml b/micrometer-support/pom.xml index 0ecfd38d9c..6ca02379b1 100644 --- a/micrometer-support/pom.xml +++ b/micrometer-support/pom.xml @@ -5,18 +5,13 @@ java-operator-sdk io.javaoperatorsdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT 4.0.0 micrometer-support Operator SDK - Micrometer Support - - - 11 - 11 - - + io.micrometer diff --git a/operator-framework-bom/pom.xml b/operator-framework-bom/pom.xml index 4367d104d2..b4d013bc90 100644 --- a/operator-framework-bom/pom.xml +++ b/operator-framework-bom/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk operator-framework-bom - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT Operator SDK - Bill of Materials pom Java SDK for implementing Kubernetes operators diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml index 9f29d69e28..61eb1ce733 100644 --- a/operator-framework-core/pom.xml +++ b/operator-framework-core/pom.xml @@ -6,7 +6,7 @@ io.javaoperatorsdk java-operator-sdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT ../pom.xml @@ -81,7 +81,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl test diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java index d908f52ebe..846c16046c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -19,14 +19,14 @@ import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.Utils.Configurator; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; import io.javaoperatorsdk.operator.api.reconciler.Constants; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.Workflow; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; @@ -99,6 +99,7 @@ public ControllerConfiguration getConfigurationFor( protected

ControllerConfiguration

configFor(Reconciler

reconciler) { final var annotation = reconciler.getClass().getAnnotation( io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class); + if (annotation == null) { throw new OperatorException( "Missing mandatory @" @@ -163,49 +164,26 @@ protected

ControllerConfiguration

configFor(Reconcile Utils.instantiate(annotation.itemStore(), ItemStore.class, context), dependentFieldManager, this, informerListLimit); - ResourceEventFilter

answer = deprecatedEventFilter(annotation); - config.setEventFilter(answer != null ? answer : ResourceEventFilters.passthrough()); - List specs = dependentResources(annotation, config); - config.setDependentResources(specs); + final var workflowAnnotation = reconciler.getClass().getAnnotation( + io.javaoperatorsdk.operator.api.reconciler.Workflow.class); + if (workflowAnnotation != null) { + List specs = dependentResources(workflowAnnotation, config); + WorkflowSpec workflowSpec = new WorkflowSpec(specs, workflowAnnotation.explicitInvocation()); + config.setWorkflowSpec(workflowSpec); + } return config; } - @SuppressWarnings("unchecked") - private static

ResourceEventFilter

deprecatedEventFilter( - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration annotation) { - ResourceEventFilter

answer = null; - - Class>[] filterTypes = - (Class>[]) valueOrDefault(annotation, - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::eventFilters, - new Object[] {}); - for (var filterType : filterTypes) { - try { - ResourceEventFilter

filter = filterType.getConstructor().newInstance(); - - if (answer == null) { - answer = filter; - } else { - answer = answer.and(filter); - } - } catch (Exception e) { - throw new IllegalArgumentException(e); - } - } - return answer; - } - @SuppressWarnings({"unchecked", "rawtypes"}) private static List dependentResources( - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration annotation, + Workflow annotation, ControllerConfiguration parent) { - final var dependents = - valueOrDefault(annotation, - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::dependents, - new Dependent[] {}); - if (dependents.length == 0) { + final var dependents = annotation.dependents(); + + + if (dependents == null || dependents.length == 0) { return Collections.emptyList(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java index 53bfc75df9..a55502a691 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java @@ -391,4 +391,14 @@ default boolean parseResourceVersionsForEventFilteringAndCaching() { return false; } + /** + * {@link io.javaoperatorsdk.operator.api.reconciler.UpdateControl} patchStatus can either use + * simple update or SSA for status subresource patching. + * + * @return true by default + */ + default boolean useSSAForResourceStatusPatch() { + return true; + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java index 5879383464..6116af5c0f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java @@ -40,6 +40,7 @@ public class ConfigurationServiceOverrider { private Set> defaultNonSSAResource; private Boolean previousAnnotationForDependentResources; private Boolean parseResourceVersions; + private Boolean useSSAForResourceStatusPatch; private DependentResourceFactory dependentResourceFactory; ConfigurationServiceOverrider(ConfigurationService original) { @@ -174,12 +175,17 @@ public ConfigurationServiceOverrider withPreviousAnnotationForDependentResources return this; } - public ConfigurationServiceOverrider wihtParseResourceVersions( + public ConfigurationServiceOverrider withParseResourceVersions( boolean value) { this.parseResourceVersions = value; return this; } + public ConfigurationServiceOverrider withUseSSAForResourceStatusPatch(boolean value) { + this.useSSAForResourceStatusPatch = value; + return this; + } + public ConfigurationService build() { return new BaseConfigurationService(original.getVersion(), cloner, client) { @Override @@ -312,6 +318,14 @@ public boolean parseResourceVersionsForEventFilteringAndCaching() { ? parseResourceVersions : super.parseResourceVersionsForEventFilteringAndCaching(); } + + @Override + public boolean useSSAForResourceStatusPatch() { + return useSSAForResourceStatusPatch != null + ? useSSAForResourceStatusPatch + : super.useSSAForResourceStatusPatch(); + + } }; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java index 13ddd995ad..2031283f37 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java @@ -1,21 +1,16 @@ package io.javaoperatorsdk.operator.api.config; import java.time.Duration; -import java.util.Collections; -import java.util.List; import java.util.Optional; import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.ReconcilerUtils; -import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; import io.javaoperatorsdk.operator.api.reconciler.MaxReconciliationInterval; import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; -import io.javaoperatorsdk.operator.processing.retry.GradualRetry; import io.javaoperatorsdk.operator.processing.retry.Retry; public interface ControllerConfiguration

extends ResourceConfiguration

{ @@ -60,22 +55,7 @@ default boolean isGenerationAware() { String getAssociatedReconcilerClassName(); default Retry getRetry() { - final var configuration = getRetryConfiguration(); - return !RetryConfiguration.DEFAULT.equals(configuration) - ? GenericRetry.fromConfiguration(configuration) - : GenericRetry.DEFAULT; // NOSONAR - } - - /** - * Use {@link #getRetry()} instead. - * - * @return configuration for retry. - * @deprecated provide your own {@link Retry} implementation or use the {@link GradualRetry} - * annotation instead - */ - @Deprecated(forRemoval = true) - default RetryConfiguration getRetryConfiguration() { - return RetryConfiguration.DEFAULT; + return GenericRetry.DEFAULT; } @SuppressWarnings("rawtypes") @@ -83,28 +63,8 @@ default RateLimiter getRateLimiter() { return DEFAULT_RATE_LIMITER; } - /** - * Allow controllers to filter events before they are passed to the - * {@link io.javaoperatorsdk.operator.processing.event.EventHandler}. - * - *

- * Resource event filters only applies on events of the main custom resource. Not on events from - * other event sources nor the periodic events. - *

- * - * @return filter - * @deprecated use {@link ResourceConfiguration#onAddFilter()}, - * {@link ResourceConfiguration#onUpdateFilter()} or - * {@link ResourceConfiguration#genericFilter()} instead - */ - @Deprecated(forRemoval = true) - default ResourceEventFilter

getEventFilter() { - return ResourceEventFilters.passthrough(); - } - - @SuppressWarnings("rawtypes") - default List getDependentResources() { - return Collections.emptyList(); + default Optional getWorkflowSpec() { + return Optional.empty(); } default Optional maxReconciliationInterval() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java index 328d912109..9f98214b91 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java @@ -10,12 +10,11 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; -import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import io.javaoperatorsdk.operator.processing.retry.Retry; import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_NAMESPACES_SET; @@ -29,7 +28,6 @@ public class ControllerConfigurationOverrider { private Set namespaces; private Retry retry; private String labelSelector; - private ResourceEventFilter customResourcePredicate; private final ControllerConfiguration original; private Duration reconciliationMaxInterval; private OnAddFilter onAddFilter; @@ -41,6 +39,7 @@ public class ControllerConfigurationOverrider { private String name; private String fieldManager; private Long informerListLimit; + private WorkflowSpec workflowSpec; private ControllerConfigurationOverrider(ControllerConfiguration original) { this.finalizer = original.getFinalizerName(); @@ -48,7 +47,6 @@ private ControllerConfigurationOverrider(ControllerConfiguration original) { this.namespaces = new HashSet<>(original.getNamespaces()); this.retry = original.getRetry(); this.labelSelector = original.getLabelSelector(); - this.customResourcePredicate = original.getEventFilter(); this.reconciliationMaxInterval = original.maxReconciliationInterval().orElse(null); this.onAddFilter = original.onAddFilter().orElse(null); this.onUpdateFilter = original.onUpdateFilter().orElse(null); @@ -59,6 +57,7 @@ private ControllerConfigurationOverrider(ControllerConfiguration original) { this.fieldManager = original.fieldManager(); this.informerListLimit = original.getInformerListLimit().orElse(null); this.itemStore = original.getItemStore().orElse(null); + this.workflowSpec = original.getWorkflowSpec().orElse(null); } public ControllerConfigurationOverrider withFinalizer(String finalizer) { @@ -110,17 +109,6 @@ public ControllerConfigurationOverrider watchingAllNamespaces() { return this; } - /** - * @param retry configuration - * @return current instance of overrider - * @deprecated Use {@link #withRetry(Retry)} instead - */ - @Deprecated(forRemoval = true) - public ControllerConfigurationOverrider withRetry(RetryConfiguration retry) { - this.retry = GenericRetry.fromConfiguration(retry); - return this; - } - public ControllerConfigurationOverrider withRetry(Retry retry) { this.retry = retry; return this; @@ -136,12 +124,6 @@ public ControllerConfigurationOverrider withLabelSelector(String labelSelecto return this; } - public ControllerConfigurationOverrider withCustomResourcePredicate( - ResourceEventFilter customResourcePredicate) { - this.customResourcePredicate = customResourcePredicate; - return this; - } - public ControllerConfigurationOverrider withReconciliationMaxInterval( Duration reconciliationMaxInterval) { this.reconciliationMaxInterval = reconciliationMaxInterval; @@ -196,7 +178,7 @@ public ControllerConfigurationOverrider withInformerListLimit( public ControllerConfigurationOverrider replacingNamedDependentResourceConfig(String name, Object dependentResourceConfig) { - final var specs = original.getDependentResources(); + final var specs = original.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); final var spec = specs.stream() .filter(drs -> drs.getName().equals(name)).findFirst() .orElseThrow( @@ -210,15 +192,13 @@ public ControllerConfigurationOverrider replacingNamedDependentResourceConfig } public ControllerConfiguration build() { - final var overridden = new ResolvedControllerConfiguration<>(original.getResourceClass(), + return new ResolvedControllerConfiguration<>(original.getResourceClass(), name, generationAware, original.getAssociatedReconcilerClassName(), retry, rateLimiter, reconciliationMaxInterval, onAddFilter, onUpdateFilter, genericFilter, - original.getDependentResources(), namespaces, finalizer, labelSelector, configurations, itemStore, fieldManager, - original.getConfigurationService(), informerListLimit); - overridden.setEventFilter(customResourcePredicate); - return overridden; + original.getConfigurationService(), informerListLimit, + original.getWorkflowSpec().orElse(null)); } public static ControllerConfigurationOverrider override( diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultRetryConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultRetryConfiguration.java deleted file mode 100644 index 40fbb38aa7..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultRetryConfiguration.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.javaoperatorsdk.operator.api.config; - -public class DefaultRetryConfiguration implements RetryConfiguration { - -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java index 307e75080f..9a94d5d667 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java @@ -8,9 +8,9 @@ import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationProvider; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; @@ -33,9 +33,7 @@ public class ResolvedControllerConfiguration

private final ItemStore

itemStore; private final ConfigurationService configurationService; private final String fieldManager; - - private ResourceEventFilter

eventFilter; - private List dependentResources; + private WorkflowSpec workflowSpec; public ResolvedControllerConfiguration(Class

resourceClass, ControllerConfiguration

other) { this(resourceClass, other.getName(), other.isGenerationAware(), @@ -43,11 +41,11 @@ public ResolvedControllerConfiguration(Class

resourceClass, ControllerConfigu other.maxReconciliationInterval().orElse(null), other.onAddFilter().orElse(null), other.onUpdateFilter().orElse(null), other.genericFilter().orElse(null), - other.getDependentResources(), other.getNamespaces(), + other.getNamespaces(), other.getFinalizerName(), other.getLabelSelector(), Collections.emptyMap(), other.getItemStore().orElse(null), other.fieldManager(), other.getConfigurationService(), - other.getInformerListLimit().orElse(null)); + other.getInformerListLimit().orElse(null), other.getWorkflowSpec().orElse(null)); } public static Duration getMaxReconciliationInterval(long interval, TimeUnit timeUnit) { @@ -72,16 +70,16 @@ public ResolvedControllerConfiguration(Class

resourceClass, String name, RateLimiter rateLimiter, Duration maxReconciliationInterval, OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, GenericFilter genericFilter, - List dependentResources, Set namespaces, String finalizer, String labelSelector, Map configurations, ItemStore

itemStore, String fieldManager, - ConfigurationService configurationService, Long informerListLimit) { + ConfigurationService configurationService, Long informerListLimit, + WorkflowSpec workflowSpec) { this(resourceClass, name, generationAware, associatedReconcilerClassName, retry, rateLimiter, maxReconciliationInterval, onAddFilter, onUpdateFilter, genericFilter, namespaces, finalizer, labelSelector, configurations, itemStore, fieldManager, configurationService, informerListLimit); - setDependentResources(dependentResources); + setWorkflowSpec(workflowSpec); } protected ResolvedControllerConfiguration(Class

resourceClass, String name, @@ -107,6 +105,7 @@ protected ResolvedControllerConfiguration(Class

resourceClass, String name, this.finalizer = ControllerConfiguration.ensureValidFinalizerName(finalizer, getResourceTypeName()); this.fieldManager = fieldManager; + this.workflowSpec = workflowSpec; } protected ResolvedControllerConfiguration(Class

resourceClass, String name, @@ -146,14 +145,14 @@ public RateLimiter getRateLimiter() { return rateLimiter; } + @Override - public List getDependentResources() { - return dependentResources; + public Optional getWorkflowSpec() { + return Optional.ofNullable(workflowSpec); } - protected void setDependentResources(List dependentResources) { - this.dependentResources = dependentResources == null ? Collections.emptyList() - : Collections.unmodifiableList(dependentResources); + public void setWorkflowSpec(WorkflowSpec workflowSpec) { + this.workflowSpec = workflowSpec; } @Override @@ -166,21 +165,6 @@ public ConfigurationService getConfigurationService() { return configurationService; } - @Override - public ResourceEventFilter

getEventFilter() { - return eventFilter; - } - - /** - * @deprecated Use {@link OnAddFilter}, {@link OnUpdateFilter} and {@link GenericFilter} instead - * - * @param eventFilter generic event filter - */ - @Deprecated(forRemoval = true) - protected void setEventFilter(ResourceEventFilter

eventFilter) { - this.eventFilter = eventFilter; - } - @Override public Object getConfigurationFor(DependentResourceSpec spec) { return configurations.get(spec); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/RetryConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/RetryConfiguration.java deleted file mode 100644 index b293c7e33f..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/RetryConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.javaoperatorsdk.operator.api.config; - -import io.javaoperatorsdk.operator.processing.retry.GradualRetry; - -/** - * @deprecated specify your own {@link io.javaoperatorsdk.operator.processing.retry.Retry} - * implementation or use {@link GradualRetry} annotation instead - */ -@Deprecated(forRemoval = true) -public interface RetryConfiguration { - - RetryConfiguration DEFAULT = new DefaultRetryConfiguration(); - - int DEFAULT_MAX_ATTEMPTS = 5; - long DEFAULT_INITIAL_INTERVAL = 2000L; - double DEFAULT_MULTIPLIER = 1.5D; - - default int getMaxAttempts() { - return DEFAULT_MAX_ATTEMPTS; - } - - default long getInitialInterval() { - return DEFAULT_INITIAL_INTERVAL; - } - - default double getIntervalMultiplier() { - return DEFAULT_MULTIPLIER; - } - - default long getMaxInterval() { - return (long) (DEFAULT_INITIAL_INTERVAL * Math.pow(DEFAULT_MULTIPLIER, DEFAULT_MAX_ATTEMPTS)); - } -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java index 19d8a73689..d318560480 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java @@ -6,7 +6,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Arrays; import java.util.Date; @@ -34,11 +33,8 @@ public class Utils { * via the {@code git-commit-id-plugin} maven plugin. * * @return a {@link Version} object encapsulating the version information - * @deprecated use {@link #VERSION} instead, as this method will be made internal in a future - * release */ - @Deprecated - public static Version loadFromProperties() { + private static Version loadFromProperties() { final var is = Thread.currentThread().getContextClassLoader().getResourceAsStream("version.properties"); @@ -57,9 +53,7 @@ public static Version loadFromProperties() { try { String time = properties.getProperty("git.build.time"); if (time != null) { - builtTime = - // RFC 822 date is the default format used by git-commit-id-plugin - new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(time); + builtTime = Date.from(Instant.parse(time)); } else { builtTime = Date.from(Instant.EPOCH); } @@ -82,9 +76,8 @@ public static int ensureValid(int value, String description, int minValue, int d throw new IllegalArgumentException( "Default value for " + description + " must be greater than " + minValue); } - log.warn("Requested " + description + " should be greater than " + minValue + ". Requested: " - + value + ", using " + defaultValue + (defaultValue == minValue ? "" : " (default)") + - " instead"); + log.warn("Requested {} should be greater than {}. Requested: {}, using {}{} instead", + description, minValue, value, defaultValue, defaultValue == minValue ? "" : " (default)"); value = defaultValue; } return value; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/workflow/WorkflowSpec.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/workflow/WorkflowSpec.java new file mode 100644 index 0000000000..ab89bb07db --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/workflow/WorkflowSpec.java @@ -0,0 +1,26 @@ +package io.javaoperatorsdk.operator.api.config.workflow; + +import java.util.List; + +import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; + +public class WorkflowSpec { + + @SuppressWarnings("rawtypes") + private final List dependentResourceSpecs; + private final boolean explicitInvocation; + + public WorkflowSpec(List dependentResourceSpecs, + boolean explicitInvocation) { + this.dependentResourceSpecs = dependentResourceSpecs; + this.explicitInvocation = explicitInvocation; + } + + public List getDependentResourceSpecs() { + return dependentResourceSpecs; + } + + public boolean isExplicitInvocation() { + return explicitInvocation; + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index e157ed5fd7..a997835822 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -8,7 +8,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedWorkflowAndDependentResourceContext; import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; @@ -34,7 +34,14 @@ Optional getSecondaryResource(Class expectedType, ControllerConfiguration

getControllerConfiguration(); - ManagedDependentResourceContext managedDependentResourceContext(); + /** + * Retrieve the {@link ManagedWorkflowAndDependentResourceContext} used to interact with + * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource}s and associated + * {@link io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow} + * + * @return the {@link ManagedWorkflowAndDependentResourceContext} + */ + ManagedWorkflowAndDependentResourceContext managedWorkflowAndDependentResourceContext(); EventSourceRetriever

eventSourceRetriever(); @@ -51,5 +58,18 @@ Optional getSecondaryResource(Class expectedType, * @return the {@link IndexerResourceCache} associated with the associated {@link Reconciler} for * this context */ + @SuppressWarnings("unused") IndexedResourceCache

getPrimaryCache(); + + /** + * Determines whether a new reconciliation will be triggered right after the current + * reconciliation is finished. This allows to optimize certain situations, helping avoid unneeded + * API calls. A reconciler might, for example, skip updating the status when it's known another + * reconciliation is already scheduled, which would in turn trigger another status update, thus + * rendering the current one moot. + * + * @return {@code true} is another reconciliation is already scheduled, {@code false} otherwise + **/ + boolean isNextReconciliationImminent(); + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java index c064e669e0..49cf56c1aa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java @@ -7,11 +7,9 @@ import java.lang.annotation.Target; import io.fabric8.kubernetes.client.informers.cache.ItemStore; -import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; import io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; @@ -64,19 +62,6 @@ */ String labelSelector() default Constants.NO_VALUE_SET; - /** - * @deprecated Use onAddFilter, onUpdateFilter instead. - * - *

- * Resource event filters only applies on events of the main custom resource. Not on - * events from other event sources nor the periodic events. - *

- * - * @return the list of event filters. - */ - @Deprecated(forRemoval = true) - Class[] eventFilters() default {}; - /** * Filter of onAdd events of resources. * @@ -107,15 +92,6 @@ MaxReconciliationInterval maxReconciliationInterval() default @MaxReconciliationInterval( interval = MaxReconciliationInterval.DEFAULT_INTERVAL); - - /** - * Optional list of {@link Dependent} configurations which associate a resource type to a - * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} implementation - * - * @return the array of {@link Dependent} configurations - */ - Dependent[] dependents() default {}; - /** * Optional {@link Retry} implementation for the associated controller to use. * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index 2b0f20ef33..9ff7ddd7a3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -9,10 +9,11 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext; -import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedWorkflowAndDependentResourceContext; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedWorkflowAndDependentResourceContext; import io.javaoperatorsdk.operator.processing.Controller; import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.processing.event.ResourceID; public class DefaultContext

implements Context

{ @@ -20,14 +21,15 @@ public class DefaultContext

implements Context

{ private final Controller

controller; private final P primaryResource; private final ControllerConfiguration

controllerConfiguration; - private final DefaultManagedDependentResourceContext defaultManagedDependentResourceContext; + private final DefaultManagedWorkflowAndDependentResourceContext

defaultManagedDependentResourceContext; public DefaultContext(RetryInfo retryInfo, Controller

controller, P primaryResource) { this.retryInfo = retryInfo; this.controller = controller; this.primaryResource = primaryResource; this.controllerConfiguration = controller.getConfiguration(); - this.defaultManagedDependentResourceContext = new DefaultManagedDependentResourceContext(); + this.defaultManagedDependentResourceContext = + new DefaultManagedWorkflowAndDependentResourceContext<>(controller, primaryResource, this); } @Override @@ -45,6 +47,12 @@ public IndexedResourceCache

getPrimaryCache() { return controller.getEventSourceManager().getControllerResourceEventSource(); } + @Override + public boolean isNextReconciliationImminent() { + return controller.getEventProcessor() + .isNextReconciliationImminent(ResourceID.fromResource(primaryResource)); + } + @Override public Stream getSecondaryResourcesAsStream(Class expectedType) { return controller.getEventSourceManager().getResourceEventSourcesFor(expectedType).stream() @@ -72,7 +80,7 @@ public ControllerConfiguration

getControllerConfiguration() { } @Override - public ManagedDependentResourceContext managedDependentResourceContext() { + public ManagedWorkflowAndDependentResourceContext managedWorkflowAndDependentResourceContext() { return defaultManagedDependentResourceContext; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java similarity index 69% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java index 09c1687e1e..8b89d95b71 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java @@ -8,24 +8,7 @@ import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventSource; -/** - * An interface that a {@link Reconciler} can implement to have the SDK register the provided - * {@link EventSource} - * - * @param

the primary resource type handled by the associated {@link Reconciler} - */ -public interface EventSourceInitializer

{ - - /** - * Prepares a map of {@link EventSource} implementations keyed by the name with which they need to - * be registered by the SDK. - * - * @param context a {@link EventSourceContext} providing access to information useful to event - * sources - * @return a map of event sources to register - */ - Map prepareEventSources(EventSourceContext

context); - +public class EventSourceUtils { /** * Utility method to easily create map with generated name for event sources. This is for the use * case when the event sources are not access explicitly by name in the reconciler. @@ -33,7 +16,7 @@ public interface EventSourceInitializer

{ * @param eventSources to name * @return even source with default names */ - static Map nameEventSources(EventSource... eventSources) { + public static Map nameEventSources(EventSource... eventSources) { Map eventSourceMap = new HashMap<>(eventSources.length); for (EventSource eventSource : eventSources) { eventSourceMap.put(generateNameFor(eventSource), eventSource); @@ -42,7 +25,7 @@ static Map nameEventSources(EventSource... eventSources) { } @SuppressWarnings("unchecked") - static Map eventSourcesFromWorkflow( + public static Map eventSourcesFromWorkflow( EventSourceContext context, Workflow workflow) { Map result = new HashMap<>(); @@ -54,13 +37,13 @@ static Map eventSourcesFromWorkflow } @SuppressWarnings("rawtypes") - static Map nameEventSourcesFromDependentResource( + public static Map nameEventSourcesFromDependentResource( EventSourceContext context, DependentResource... dependentResources) { return nameEventSourcesFromDependentResource(context, Arrays.asList(dependentResources)); } @SuppressWarnings("unchecked,rawtypes") - static Map nameEventSourcesFromDependentResource( + public static Map nameEventSourcesFromDependentResource( EventSourceContext context, Collection dependentResources) { if (dependentResources != null) { @@ -81,9 +64,8 @@ static Map nameEventSourcesFromDepe * @param eventSource EventSource * @return generated name */ - static String generateNameFor(EventSource eventSource) { + public static String generateNameFor(EventSource eventSource) { // we can have multiple event sources for the same class return eventSource.getClass().getName() + "#" + eventSource.hashCode(); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java index 55df9d1cea..2047762c35 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java @@ -1,8 +1,11 @@ package io.javaoperatorsdk.operator.api.reconciler; +import java.util.*; + import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; -public interface Reconciler { +public interface Reconciler

{ /** * The implementation of this operation is required to be idempotent. Always use the UpdateControl @@ -14,6 +17,19 @@ public interface Reconciler { * @return UpdateControl to manage updates on the custom resource (usually the status) after * reconciliation. */ - UpdateControl reconcile(R resource, Context context) throws Exception; + UpdateControl

reconcile(P resource, Context

context) throws Exception; + + + /** + * Prepares a map of {@link EventSource} implementations keyed by the name with which they need to + * be registered by the SDK. + * + * @param context a {@link EventSourceContext} providing access to information useful to event + * sources + * @return a map of event sources to register + */ + default Map prepareEventSources(EventSourceContext

context) { + return Map.of(); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java new file mode 100644 index 0000000000..04a5b21606 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java @@ -0,0 +1,22 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.lang.annotation.*; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Workflow { + + Dependent[] dependents(); + + /** + * If true, managed workflow should be explicitly invoked within the reconciler implementation. If + * false workflow is invoked just before the {@link Reconciler#reconcile(HasMetadata, Context)} + * method. + */ + boolean explicitInvocation() default false; + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java index 8230dc4cf9..eec011e5e8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java @@ -49,6 +49,16 @@ default Optional> eventSource( return Optional.empty(); } + /** + * Retrieves the secondary resource (if it exists) associated with the specified primary resource + * for this DependentResource. + * + * @param primary the primary resource for which we want to retrieve the secondary resource + * associated with this DependentResource + * @param context the current {@link Context} in which the operation is called + * @return the secondary resource or {@link Optional#empty()} if it doesn't exist + * @throws IllegalStateException if more than one secondary is found to match the primary resource + */ default Optional getSecondaryResource(P primary, Context

context) { return Optional.empty(); } @@ -68,4 +78,9 @@ static String defaultNameFor(Class dependentResourc default boolean isDeletable() { return this instanceof Deleter; } + + default String name() { + return defaultNameFor(getClass()); + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java new file mode 100644 index 0000000000..952bf14490 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.api.reconciler.dependent; + +public interface NameSetter { + + void setName(String name); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java similarity index 50% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContext.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java index 5b1a21e5dd..f93f45ecf7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java @@ -3,15 +3,30 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.Controller; import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowCleanupResult; import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult; @SuppressWarnings("rawtypes") -public class DefaultManagedDependentResourceContext implements ManagedDependentResourceContext { +public class DefaultManagedWorkflowAndDependentResourceContext

+ implements ManagedWorkflowAndDependentResourceContext { + private final ConcurrentHashMap attributes = new ConcurrentHashMap(); + private final Controller

controller; + private final P primaryResource; + private final Context

context; private WorkflowReconcileResult workflowReconcileResult; private WorkflowCleanupResult workflowCleanupResult; - private final ConcurrentHashMap attributes = new ConcurrentHashMap(); + + public DefaultManagedWorkflowAndDependentResourceContext(Controller

controller, + P primaryResource, + Context

context) { + this.controller = controller; + this.primaryResource = primaryResource; + this.context = context; + } @Override public Optional get(Object key, Class expectedType) { @@ -37,25 +52,42 @@ public T getMandatory(Object key, Class expectedType) { + ") is missing or not of the expected type")); } - public DefaultManagedDependentResourceContext setWorkflowExecutionResult( + public DefaultManagedWorkflowAndDependentResourceContext setWorkflowExecutionResult( WorkflowReconcileResult workflowReconcileResult) { this.workflowReconcileResult = workflowReconcileResult; return this; } - public DefaultManagedDependentResourceContext setWorkflowCleanupResult( + public DefaultManagedWorkflowAndDependentResourceContext setWorkflowCleanupResult( WorkflowCleanupResult workflowCleanupResult) { this.workflowCleanupResult = workflowCleanupResult; return this; } @Override - public Optional getWorkflowReconcileResult() { - return Optional.ofNullable(workflowReconcileResult); + public WorkflowReconcileResult getWorkflowReconcileResult() { + return workflowReconcileResult; } @Override - public Optional getWorkflowCleanupResult() { - return Optional.ofNullable(workflowCleanupResult); + public WorkflowCleanupResult getWorkflowCleanupResult() { + return workflowCleanupResult; } + + @Override + public void reconcileManagedWorkflow() { + if (!controller.isWorkflowExplicitInvocation()) { + throw new IllegalStateException("Workflow explicit invocation is not set."); + } + controller.reconcileManagedWorkflow(primaryResource, context); + } + + @Override + public void cleanupManageWorkflow() { + if (!controller.isWorkflowExplicitInvocation()) { + throw new IllegalStateException("Workflow explicit invocation is not set."); + } + controller.cleanupManagedWorkflow(primaryResource, context); + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java similarity index 72% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceContext.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java index 9c5b3dddb1..f5a25a6d9c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java @@ -10,7 +10,7 @@ * Contextual information related to {@link DependentResource} either to retrieve the actual * implementations to interact with them or to pass information between them and/or the reconciler */ -public interface ManagedDependentResourceContext { +public interface ManagedWorkflowAndDependentResourceContext { /** * Retrieve a contextual object, if it exists and is of the specified expected type, associated @@ -37,7 +37,6 @@ public interface ManagedDependentResourceContext { * @return an Optional containing the previous value associated with the key or * {@link Optional#empty()} if none existed */ - @SuppressWarnings("unchecked") T put(Object key, T value); /** @@ -52,7 +51,27 @@ public interface ManagedDependentResourceContext { @SuppressWarnings("unused") T getMandatory(Object key, Class expectedType); - Optional getWorkflowReconcileResult(); + WorkflowReconcileResult getWorkflowReconcileResult(); + + @SuppressWarnings("unused") + WorkflowCleanupResult getWorkflowCleanupResult(); + + /** + * Explicitly reconcile the declared workflow for the associated + * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler} + * + * @throws IllegalStateException if called when explicit invocation is not requested + */ + void reconcileManagedWorkflow(); + + /** + * Explicitly clean-up dependent resources in the declared workflow for the associated + * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler}. Note that calling this method is + * only needed if the associated reconciler implements the + * {@link io.javaoperatorsdk.operator.api.reconciler.Cleaner} interface. + * + * @throws IllegalStateException if called when explicit invocation is not requested + */ + void cleanupManageWorkflow(); - Optional getWorkflowCleanupResult(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 70069148c3..7ab1eca457 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -23,6 +23,7 @@ import io.javaoperatorsdk.operator.RegisteredController; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.config.ExecutorServiceManager; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; import io.javaoperatorsdk.operator.api.monitoring.Metrics; import io.javaoperatorsdk.operator.api.monitoring.Metrics.ControllerExecution; import io.javaoperatorsdk.operator.api.reconciler.Cleaner; @@ -31,14 +32,13 @@ import io.javaoperatorsdk.operator.api.reconciler.ContextInitializer; import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceNotFoundException; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceReferencer; -import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedWorkflowAndDependentResourceContext; import io.javaoperatorsdk.operator.health.ControllerHealthInfo; import io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow; import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowCleanupResult; @@ -144,12 +144,11 @@ public Map metadata() { @Override public UpdateControl

execute() throws Exception { initContextIfNeeded(resource, context); - if (!managedWorkflow.isEmpty()) { - var res = managedWorkflow.reconcile(resource, context); - ((DefaultManagedDependentResourceContext) context.managedDependentResourceContext()) - .setWorkflowExecutionResult(res); - res.throwAggregateExceptionIfErrorsPresent(); - } + configuration.getWorkflowSpec().ifPresent(ws -> { + if (!isWorkflowExplicitInvocation()) { + reconcileManagedWorkflow(resource, context); + } + }); return reconciler.reconcile(resource, context); } }); @@ -189,12 +188,13 @@ public Map metadata() { public DeleteControl execute() { initContextIfNeeded(resource, context); WorkflowCleanupResult workflowCleanupResult = null; - if (managedWorkflow.hasCleaner()) { - workflowCleanupResult = managedWorkflow.cleanup(resource, context); - ((DefaultManagedDependentResourceContext) context.managedDependentResourceContext()) - .setWorkflowCleanupResult(workflowCleanupResult); - workflowCleanupResult.throwAggregateExceptionIfErrorsPresent(); + + // The cleanup is called also when explicit invocation is true, but the cleaner is not + // implemented + if (!isCleaner || !isWorkflowExplicitInvocation()) { + workflowCleanupResult = cleanupManagedWorkflow(resource, context); } + if (isCleaner) { var cleanupResult = ((Cleaner

) reconciler).cleanup(resource, context); if (!cleanupResult.isRemoveFinalizer()) { @@ -230,11 +230,8 @@ private void initContextIfNeeded(P resource, Context

context) { } public void initAndRegisterEventSources(EventSourceContext

context) { - if (reconciler instanceof EventSourceInitializer) { - final var provider = (EventSourceInitializer

) this.reconciler; - final var ownSources = provider.prepareEventSources(context); - ownSources.forEach(eventSourceManager::registerEventSource); - } + final var ownSources = this.reconciler.prepareEventSources(context); + ownSources.forEach(eventSourceManager::registerEventSource); // register created event sources final var dependentResourcesByName = @@ -446,4 +443,32 @@ public ExecutorServiceManager getExecutorServiceManager() { public EventSourceContext

eventSourceContext() { return eventSourceContext; } + + public void reconcileManagedWorkflow(P primary, Context

context) { + if (!managedWorkflow.isEmpty()) { + var res = managedWorkflow.reconcile(primary, context); + ((DefaultManagedWorkflowAndDependentResourceContext) context + .managedWorkflowAndDependentResourceContext()) + .setWorkflowExecutionResult(res); + res.throwAggregateExceptionIfErrorsPresent(); + } + } + + public WorkflowCleanupResult cleanupManagedWorkflow(P resource, Context

context) { + if (managedWorkflow.hasCleaner()) { + var workflowCleanupResult = managedWorkflow.cleanup(resource, context); + ((DefaultManagedWorkflowAndDependentResourceContext) context + .managedWorkflowAndDependentResourceContext()) + .setWorkflowCleanupResult(workflowCleanupResult); + workflowCleanupResult.throwAggregateExceptionIfErrorsPresent(); + return workflowCleanupResult; + } else { + return null; + } + } + + public boolean isWorkflowExplicitInvocation() { + return configuration.getWorkflowSpec().map(WorkflowSpec::isExplicitInvocation) + .orElse(false); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java index ea1e020bfb..590affe617 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent; import java.util.Optional; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,13 +12,14 @@ import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.api.reconciler.dependent.NameSetter; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result; import io.javaoperatorsdk.operator.processing.event.ResourceID; @Ignore public abstract class AbstractDependentResource - implements DependentResource { + implements DependentResource, NameSetter { private static final Logger log = LoggerFactory.getLogger(AbstractDependentResource.class); private final boolean creatable = this instanceof Creator; @@ -29,14 +31,21 @@ public abstract class AbstractDependentResource private ResourceDiscriminator resourceDiscriminator; private final DependentResourceReconciler dependentResourceReconciler; + protected String name; + @SuppressWarnings({"unchecked"}) protected AbstractDependentResource() { + this(null); + } + + protected AbstractDependentResource(String name) { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; dependentResourceReconciler = this instanceof BulkDependentResource ? new BulkDependentResourceReconciler<>((BulkDependentResource) this) : new SingleDependentResourceReconciler<>(this); + this.name = name == null ? DependentResource.defaultNameFor(this.getClass()) : name; } /** @@ -89,8 +98,39 @@ protected ReconcileResult reconcile(P primary, R actualResource, Context

c @Override public Optional getSecondaryResource(P primary, Context

context) { - return resourceDiscriminator == null ? context.getSecondaryResource(resourceType()) - : resourceDiscriminator.distinguish(resourceType(), primary, context); + if (resourceDiscriminator != null) { + return resourceDiscriminator.distinguish(resourceType(), primary, context); + } else { + var secondaryResources = context.getSecondaryResources(resourceType()); + if (secondaryResources.isEmpty()) { + return Optional.empty(); + } else { + return selectManagedSecondaryResource(secondaryResources, primary, context); + } + } + } + + /** + * Selects the actual secondary resource matching the desired state derived from the primary + * resource when several resources of the same type are found in the context. This method allows + * for optimized implementations in subclasses since this default implementation will check each + * secondary candidates for equality with the specified desired state, which might end up costly. + * + * @param secondaryResources to select the target resource from + * + * @return the matching secondary resource or {@link Optional#empty()} if none matches + * @throws IllegalStateException if more than one candidate is found, in which case some other + * mechanism might be necessary to distinguish between candidate secondary resources + */ + protected Optional selectManagedSecondaryResource(Set secondaryResources, P primary, + Context

context) { + R desired = desired(primary, context); + var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList(); + if (targetResources.size() > 1) { + throw new IllegalStateException( + "More than one secondary resource related to primary: " + targetResources); + } + return targetResources.isEmpty() ? Optional.empty() : Optional.of(targetResources.get(0)); } private void throwIfNull(R desired, P primary, String descriptor) { @@ -158,8 +198,7 @@ protected void handleDelete(P primary, R secondary, Context

context) { "handleDelete method must be implemented if Deleter trait is supported"); } - public void setResourceDiscriminator( - ResourceDiscriminator resourceDiscriminator) { + public void setResourceDiscriminator(ResourceDiscriminator resourceDiscriminator) { this.resourceDiscriminator = resourceDiscriminator; } @@ -176,4 +215,13 @@ protected boolean isUpdatable() { public boolean isDeletable() { return deletable; } + + @Override + public String name() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index 6562afd09f..0b9f2ae897 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -31,6 +31,11 @@ public abstract class AbstractEventSourceHolderDependentResource resourceType) { + this(resourceType, null); + } + + protected AbstractEventSourceHolderDependentResource(Class resourceType, String name) { + super(name); this.resourceType = resourceType; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java index c71da5d5ad..05ee05b036 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java @@ -58,7 +58,7 @@ static Matcher matcherFor( @Override public Result match(R actualResource, P primary, Context

context) { var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, false, false, false, context); + return match(desired, actualResource, false, false, context); } /** @@ -67,9 +67,6 @@ public Result match(R actualResource, P primary, Context

context) { * * @param desired the desired resource * @param actualResource the actual resource - * @param considerLabelsAndAnnotations {@code true} if labels and annotations will be checked for - * equality, {@code false} otherwise (meaning that metadata changes will be ignored for - * matching purposes) * @param labelsAndAnnotationsEquality if true labels and annotation match exactly in the actual * and desired state if false, additional elements are allowed in actual annotations. * Considered only if considerLabelsAndAnnotations is true. @@ -89,9 +86,9 @@ public Result match(R actualResource, P primary, Context

context) { */ public static Result match(R desired, R actualResource, - boolean considerLabelsAndAnnotations, boolean labelsAndAnnotationsEquality, + boolean labelsAndAnnotationsEquality, boolean valuesEquality, Context

context) { - return match(desired, actualResource, considerLabelsAndAnnotations, + return match(desired, actualResource, labelsAndAnnotationsEquality, valuesEquality, context, EMPTY_ARRAY); } @@ -101,9 +98,6 @@ public static Result match(R d * * @param desired the desired resource * @param actualResource the actual resource - * @param considerLabelsAndAnnotations {@code true} if labels and annotations will be checked for - * equality, {@code false} otherwise (meaning that metadata changes will be ignored for - * matching purposes) * @param labelsAndAnnotationsEquality if true labels and annotation match exactly in the actual * and desired state if false, additional elements are allowed in actual annotations. * Considered only if considerLabelsAndAnnotations is true. @@ -116,9 +110,9 @@ public static Result match(R d */ public static Result match(R desired, R actualResource, - boolean considerLabelsAndAnnotations, boolean labelsAndAnnotationsEquality, + boolean labelsAndAnnotationsEquality, Context

context, String... ignorePaths) { - return match(desired, actualResource, considerLabelsAndAnnotations, + return match(desired, actualResource, labelsAndAnnotationsEquality, false, context, ignorePaths); } @@ -133,9 +127,6 @@ public static Result match(R d * matches the desired state or not * @param primary the primary resource from which we want to compute the desired state * @param context the {@link Context} instance within which this method is called - * @param considerLabelsAndAnnotations {@code true} to consider the metadata of the actual - * resource when determining if it matches the desired state, {@code false} if matching - * should occur only considering the spec of the resources * @param labelsAndAnnotationsEquality if true labels and annotation match exactly in the actual * and desired state if false, additional elements are allowed in actual annotations. * Considered only if considerLabelsAndAnnotations is true. @@ -150,28 +141,28 @@ public static Result match(R d */ public static Result match( KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerLabelsAndAnnotations, + Context

context, boolean labelsAndAnnotationsEquality, String... ignorePaths) { final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, considerLabelsAndAnnotations, + return match(desired, actualResource, labelsAndAnnotationsEquality, context, ignorePaths); } public static Result match( KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerLabelsAndAnnotations, + Context

context, + boolean specEquality, boolean labelsAndAnnotationsEquality, - boolean specEquality) { + String... ignorePaths) { final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, considerLabelsAndAnnotations, - labelsAndAnnotationsEquality, specEquality, context); + return match(desired, actualResource, + labelsAndAnnotationsEquality, specEquality, context, ignorePaths); } public static Result match(R desired, - R actualResource, - boolean considerMetadata, boolean labelsAndAnnotationsEquality, boolean valuesEquality, + R actualResource, boolean labelsAndAnnotationsEquality, boolean valuesEquality, Context

context, String... ignoredPaths) { final List ignoreList = @@ -195,8 +186,7 @@ public static Result match(R d matched = match(valuesEquality, node, ignoreList); } else if (nodeIsChildOf(node, List.of(METADATA))) { // conditionally consider labels and annotations - if (considerMetadata - && nodeIsChildOf(node, List.of(METADATA_LABELS, METADATA_ANNOTATIONS))) { + if (nodeIsChildOf(node, List.of(METADATA_LABELS, METADATA_ANNOTATIONS))) { matched = match(labelsAndAnnotationsEquality, node, Collections.emptyList()); } } else if (!nodeIsChildOf(node, IGNORED_FIELDS)) { @@ -227,20 +217,4 @@ static String getPath(JsonNode n) { return n.get(PATH).asText(); } - @Deprecated(forRemoval = true) - public static Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerLabelsAndAnnotations, boolean specEquality) { - final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, considerLabelsAndAnnotations, specEquality, context); - } - - @Deprecated(forRemoval = true) - public static Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, - Context

context, boolean considerLabelsAndAnnotations, String... ignorePaths) { - final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, considerLabelsAndAnnotations, true, context, ignorePaths); - } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java index b804b88a30..edc7491469 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -43,7 +44,12 @@ public abstract class KubernetesDependentResource resourceType) { - super(resourceType); + this(resourceType, null); + } + + @SuppressWarnings("unchecked") + public KubernetesDependentResource(Class resourceType, String name) { + super(resourceType, name); usingCustomResourceUpdateMatcher = this instanceof ResourceUpdaterMatcher; updaterMatcher = usingCustomResourceUpdateMatcher @@ -289,6 +295,29 @@ protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary, Stri } } + @Override + protected Optional selectManagedSecondaryResource(Set secondaryResources, P primary, + Context

context) { + ResourceID managedResourceID = managedSecondaryResourceID(primary, context); + return secondaryResources.stream() + .filter(r -> r.getMetadata().getName().equals(managedResourceID.getName()) && + Objects.equals(r.getMetadata().getNamespace(), + managedResourceID.getNamespace().orElse(null))) + .findFirst(); + } + + /** + * Override this method in order to optimize and not compute the desired when selecting the target + * secondary resource. Simply, a static ResourceID can be returned. + * + * @param primary resource + * @param context of current reconciliation + * @return id of the target managed resource + */ + protected ResourceID managedSecondaryResourceID(P primary, Context

context) { + return ResourceID.fromResource(desired(primary, context)); + } + protected boolean addOwnerReference() { return garbageCollected; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/updatermatcher/GenericResourceUpdaterMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/updatermatcher/GenericResourceUpdaterMatcher.java index 2a5bae03b9..e3b999315c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/updatermatcher/GenericResourceUpdaterMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/updatermatcher/GenericResourceUpdaterMatcher.java @@ -41,7 +41,7 @@ public R updateResource(R actual, R desired, Context context) { @Override public boolean matches(R actual, R desired, Context context) { - return GenericKubernetesResourceMatcher.match(desired, actual, true, + return GenericKubernetesResourceMatcher.match(desired, actual, false, false, context).matched(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java index 56e5e17d07..05b546553c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java @@ -18,7 +18,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; @SuppressWarnings("rawtypes") -public abstract class AbstractWorkflowExecutor

{ +abstract class AbstractWorkflowExecutor

{ protected final Workflow

workflow; protected final P primary; @@ -133,17 +133,16 @@ protected void registerOrDeregisterEventSourceBasedOnActivation( boolean activationConditionMet, DependentResourceNode dependentResourceNode) { if (dependentResourceNode.getActivationCondition().isPresent()) { + final var dr = dependentResourceNode.getDependentResource(); + final var eventSourceRetriever = context.eventSourceRetriever(); if (activationConditionMet) { var eventSource = - dependentResourceNode.getDependentResource().eventSource(context.eventSourceRetriever() - .eventSourceContextForDynamicRegistration()); + dr.eventSource(eventSourceRetriever.eventSourceContextForDynamicRegistration()); var es = eventSource.orElseThrow(); - context.eventSourceRetriever() - .dynamicallyRegisterEventSource(dependentResourceNode.getName(), es); + eventSourceRetriever.dynamicallyRegisterEventSource(dr.name(), es); } else { - context.eventSourceRetriever() - .dynamicallyDeRegisterEventSource(dependentResourceNode.getName()); + eventSourceRetriever.dynamicallyDeRegisterEventSource(dr.name()); } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java index 27400230df..fb0b733c32 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java @@ -12,8 +12,10 @@ import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceReferencer; +import io.javaoperatorsdk.operator.api.reconciler.dependent.NameSetter; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; +import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET; import static io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow.THROW_EXCEPTION_AUTOMATICALLY_DEFAULT; @SuppressWarnings("rawtypes") @@ -77,13 +79,14 @@ public Workflow

resolve(KubernetesClient client, ControllerConfiguration

configuration) { final var alreadyResolved = new HashMap(orderedSpecs.size()); for (DependentResourceSpec spec : orderedSpecs) { - final var node = new DependentResourceNode(spec.getName(), + final var dependentResource = resolve(spec, client, configuration); + final var node = new DependentResourceNode( spec.getReconcileCondition(), spec.getDeletePostCondition(), spec.getReadyCondition(), spec.getActivationCondition(), - resolve(spec, client, configuration)); - alreadyResolved.put(node.getName(), node); + dependentResource); + alreadyResolved.put(dependentResource.name(), node); spec.getDependsOn() .forEach(depend -> node.addDependsOnRelation(alreadyResolved.get(depend))); } @@ -104,6 +107,11 @@ private DependentResource resolve(DependentResourceSpec spec, configuration.getConfigurationService().dependentResourceFactory() .createFrom(spec, configuration); + final var name = spec.getName(); + if (name != null && !NO_VALUE_SET.equals(name) && dependentResource instanceof NameSetter) { + ((NameSetter) dependentResource).setName(name); + } + if (dependentResource instanceof KubernetesClientAware) { ((KubernetesClientAware) dependentResource).setKubernetesClient(client); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java index d31f42419d..1c241aebbc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java @@ -20,7 +20,7 @@ * @param

primary resource */ @SuppressWarnings("rawtypes") -public class DefaultWorkflow

implements Workflow

{ +class DefaultWorkflow

implements Workflow

{ private final Map dependentResourceNodes; private final Set topLevelResources; @@ -77,9 +77,9 @@ private Map toMap(Set node bottomLevelResource.remove(dependsOn); } } - map.put(node.getName(), node); + map.put(node.getDependentResource().name(), node); } - if (topLevelResources.size() == 0) { + if (topLevelResources.isEmpty()) { throw new IllegalStateException( "No top-level dependent resources found. This might indicate a cyclic Set of DependentResourceNode has been provided."); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java index 4c82ee19f3..3e8a762c5e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java @@ -8,29 +8,24 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @SuppressWarnings("rawtypes") -public class DependentResourceNode { +class DependentResourceNode { private final List dependsOn = new LinkedList<>(); private final List parents = new LinkedList<>(); - private final String name; + private Condition reconcilePrecondition; private Condition deletePostcondition; private Condition readyPostcondition; private Condition activationCondition; private final DependentResource dependentResource; - DependentResourceNode(String name, DependentResource dependentResource) { - this(name, null, null, null, null, dependentResource); - } - DependentResourceNode(DependentResource dependentResource) { - this(getNameFor(dependentResource), null, null, null, null, dependentResource); + this(null, null, null, null, dependentResource); } - public DependentResourceNode(String name, Condition reconcilePrecondition, + public DependentResourceNode(Condition reconcilePrecondition, Condition deletePostcondition, Condition readyPostcondition, Condition activationCondition, DependentResource dependentResource) { - this.name = name; this.reconcilePrecondition = reconcilePrecondition; this.deletePostcondition = deletePostcondition; this.readyPostcondition = readyPostcondition; @@ -55,16 +50,10 @@ public List getParents() { return parents; } - public String getName() { - return name; - } - - public Optional> getReconcilePrecondition() { return Optional.ofNullable(reconcilePrecondition); } - public Optional> getDeletePostcondition() { return Optional.ofNullable(deletePostcondition); } @@ -106,18 +95,12 @@ public boolean equals(Object o) { return false; } DependentResourceNode that = (DependentResourceNode) o; - return name.equals(that.name); + return this.getDependentResource().name().equals(that.getDependentResource().name()); } @Override public int hashCode() { - return name.hashCode(); - } - - @SuppressWarnings("rawtypes") - static String getNameFor(DependentResource dependentResource) { - return DependentResource.defaultNameFor(dependentResource.getClass()) + "#" - + dependentResource.hashCode(); + return this.getDependentResource().name().hashCode(); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java index eb01dcd3f4..cc735f137e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java @@ -1,17 +1,20 @@ package io.javaoperatorsdk.operator.processing.dependent.workflow; +import java.util.Optional; + import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; public interface ManagedWorkflowFactory> { @SuppressWarnings({"rawtypes", "unchecked"}) ManagedWorkflowFactory DEFAULT = (configuration) -> { - final var dependentResourceSpecs = configuration.getDependentResources(); - if (dependentResourceSpecs == null || dependentResourceSpecs.isEmpty()) { + final Optional workflowSpec = configuration.getWorkflowSpec(); + if (workflowSpec.isEmpty()) { return (ManagedWorkflow) (client, configuration1) -> new DefaultWorkflow(null); } ManagedWorkflowSupport support = new ManagedWorkflowSupport(); - return support.createWorkflow(dependentResourceSpecs); + return support.createWorkflow(workflowSpec.orElseThrow()); }; @SuppressWarnings("rawtypes") diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java index b5a6fd26b2..f3fa894712 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java @@ -12,6 +12,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; @SuppressWarnings({"rawtypes", "unchecked"}) class ManagedWorkflowSupport { @@ -38,10 +39,10 @@ public void checkForNameDuplication(List dependentResourc } } - public

ManagedWorkflow

createWorkflow( - List dependentResourceSpecs) { - return createAsDefault(dependentResourceSpecs); + WorkflowSpec workflowSpec) { + + return createAsDefault(workflowSpec.getDependentResourceSpecs()); }

DefaultManagedWorkflow

createAsDefault( diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java index d65e21659c..90ece70d26 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java @@ -21,14 +21,9 @@ public class WorkflowBuilder

{ private boolean isCleaner = false; public WorkflowBuilder

addDependentResource(DependentResource dependentResource) { - return addDependentResource(dependentResource, null); - } - - public WorkflowBuilder

addDependentResource(DependentResource dependentResource, String name) { - currentNode = name == null ? new DependentResourceNode<>(dependentResource) - : new DependentResourceNode<>(name, dependentResource); + currentNode = new DependentResourceNode<>(dependentResource); isCleaner = isCleaner || dependentResource.isDeletable(); - final var actualName = currentNode.getName(); + final var actualName = dependentResource.name(); dependentResourceNodes.put(actualName, currentNode); return this; } @@ -70,8 +65,7 @@ public WorkflowBuilder

withActivationCondition(Condition activationCondition) DependentResourceNode getNodeByDependentResource(DependentResource dependentResource) { // first check by name - final var node = - dependentResourceNodes.get(DependentResourceNode.getNameFor(dependentResource)); + final var node = dependentResourceNodes.get(dependentResource.name()); if (node != null) { return node; } else { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java index 77fc2dcbfe..75366925bd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java @@ -1,8 +1,8 @@ package io.javaoperatorsdk.operator.processing.dependent.workflow; -import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.stream.Collectors; import io.javaoperatorsdk.operator.AggregatedOperatorException; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; @@ -37,22 +37,9 @@ public boolean erroredDependentsExist() { public void throwAggregateExceptionIfErrorsPresent() { if (erroredDependentsExist()) { - Map exceptionMap = new HashMap<>(); - Map numberOfClasses = new HashMap<>(); - - for (Entry entry : erroredDependents.entrySet()) { - String name = entry.getKey().getClass().getName(); - var num = numberOfClasses.getOrDefault(name, 0); - if (num > 0) { - exceptionMap.put(name + NUMBER_DELIMITER + num, entry.getValue()); - } else { - exceptionMap.put(name, entry.getValue()); - } - numberOfClasses.put(name, num + 1); - } - throw new AggregatedOperatorException("Exception(s) during workflow execution.", - exceptionMap); + erroredDependents.entrySet().stream() + .collect(Collectors.toMap(e -> e.getKey().name(), Entry::getValue))); } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java index e94f5f3a7e..b0bf48802a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java @@ -408,6 +408,10 @@ public void start() throws OperatorException { handleAlreadyMarkedEvents(); } + public boolean isNextReconciliationImminent(ResourceID resourceID) { + return resourceStateManager.getOrCreate(resourceID).eventPresent(); + } + private void handleAlreadyMarkedEvents() { for (var state : resourceStateManager.resourcesWithEventPresent()) { handleMarkedEventForResource(state); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java index bd299b464a..9772c9edd5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java @@ -14,7 +14,7 @@ import io.javaoperatorsdk.operator.api.config.ExecutorServiceManager; import io.javaoperatorsdk.operator.api.config.NamespaceChangeable; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceUtils; import io.javaoperatorsdk.operator.processing.Controller; import io.javaoperatorsdk.operator.processing.LifecycleAware; import io.javaoperatorsdk.operator.processing.event.source.EventSource; @@ -150,7 +150,7 @@ public final synchronized void registerEventSource(String name, EventSource even Objects.requireNonNull(eventSource, "EventSource must not be null"); try { if (name == null || name.isBlank()) { - name = EventSourceInitializer.generateNameFor(eventSource); + name = EventSourceUtils.generateNameFor(eventSource); } if (eventSource instanceof ManagedInformerEventSource) { var managedInformerEventSource = ((ManagedInformerEventSource) eventSource); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/NamedEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/NamedEventSource.java index a1d1a601e4..a4e4ead83a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/NamedEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/NamedEventSource.java @@ -4,7 +4,7 @@ import java.util.Optional; import io.javaoperatorsdk.operator.OperatorException; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceUtils; import io.javaoperatorsdk.operator.processing.event.source.Configurable; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.EventSourceStartPriority; @@ -19,7 +19,7 @@ class NamedEventSource implements EventSource, EventSourceMetadata { NamedEventSource(EventSource original, String name) { this.original = original; this.name = name; - nameSet = !name.equals(EventSourceInitializer.generateNameFor(original)); + nameSet = !name.equals(EventSourceUtils.generateNameFor(original)); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java index cd1d601487..a240827d14 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java @@ -12,6 +12,8 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.dsl.base.PatchContext; +import io.fabric8.kubernetes.client.dsl.base.PatchType; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.api.ObservedGenerationAware; import io.javaoperatorsdk.operator.api.config.Cloner; @@ -25,9 +27,7 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.processing.Controller; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getName; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; /** * Handles calls and results of a Reconciler and finalizer related logic @@ -45,8 +45,7 @@ class ReconciliationDispatcher

{ private final boolean retryConfigurationHasZeroAttempts; private final Cloner cloner; - ReconciliationDispatcher(Controller

controller, - CustomResourceFacade

customResourceFacade) { + ReconciliationDispatcher(Controller

controller, CustomResourceFacade

customResourceFacade) { this.controller = controller; this.customResourceFacade = customResourceFacade; this.cloner = controller.getConfiguration().getConfigurationService().getResourceCloner(); @@ -56,7 +55,8 @@ class ReconciliationDispatcher

{ } public ReconciliationDispatcher(Controller

controller) { - this(controller, new CustomResourceFacade<>(controller.getCRClient())); + this(controller, + new CustomResourceFacade<>(controller.getCRClient(), controller.getConfiguration())); } public PostExecutionControl

handleExecution(ExecutionScope

executionScope) { @@ -138,33 +138,25 @@ private PostExecutionControl

reconcileExecution(ExecutionScope

executionSc UpdateControl

updateControl = controller.reconcile(resourceForExecution, context); P updatedCustomResource = null; + final var toUpdate = + updateControl.isNoUpdate() ? originalResource : updateControl.getResource(); if (updateControl.isUpdateResourceAndStatus()) { - updatedCustomResource = - updateCustomResource(updateControl.getResource()); - updateControl - .getResource() + updatedCustomResource = updateCustomResource(toUpdate); + toUpdate .getMetadata() .setResourceVersion(updatedCustomResource.getMetadata().getResourceVersion()); - updatedCustomResource = - updateStatusGenerationAware(updateControl.getResource(), originalResource, - updateControl.isPatchStatus()); - } else if (updateControl.isUpdateStatus()) { - updatedCustomResource = - updateStatusGenerationAware(updateControl.getResource(), originalResource, - updateControl.isPatchStatus()); } else if (updateControl.isUpdateResource()) { + updatedCustomResource = updateCustomResource(toUpdate); + } + + // check if status also needs to be updated + final var updateObservedGeneration = updateControl.isNoUpdate() + ? shouldUpdateObservedGenerationAutomatically(resourceForExecution) + : shouldUpdateObservedGenerationAutomatically(updatedCustomResource); + if (updateControl.isUpdateResourceAndStatus() || updateControl.isUpdateStatus() + || updateObservedGeneration) { updatedCustomResource = - updateCustomResource(updateControl.getResource()); - if (shouldUpdateObservedGenerationAutomatically(updatedCustomResource)) { - updatedCustomResource = - updateStatusGenerationAware(updateControl.getResource(), originalResource, - updateControl.isPatchStatus()); - } - } else if (updateControl.isNoUpdate() - && shouldUpdateObservedGenerationAutomatically(resourceForExecution)) { - updatedCustomResource = - updateStatusGenerationAware(originalResource, originalResource, - updateControl.isPatchStatus()); + updateStatusGenerationAware(toUpdate, originalResource, updateControl.isPatchStatus()); } return createPostExecutionControl(updatedCustomResource, updateControl); } @@ -379,10 +371,16 @@ public P conflictRetryingUpdate(P resource, Function modificationFun static class CustomResourceFacade { private final MixedOperation, Resource> resourceOperation; + private final boolean useSSAToUpdateStatus; + private final String fieldManager; public CustomResourceFacade( - MixedOperation, Resource> resourceOperation) { + MixedOperation, Resource> resourceOperation, + ControllerConfiguration configuration) { this.resourceOperation = resourceOperation; + this.useSSAToUpdateStatus = + configuration.getConfigurationService().useSSAForResourceStatusPatch(); + this.fieldManager = configuration.fieldManager(); } public R getResource(String namespace, String name) { @@ -412,14 +410,30 @@ public R updateStatus(R resource) { } public R patchStatus(R resource, R originalResource) { - log.trace("Updating status for resource: {}", resource); + log.trace("Patching status for resource: {} with ssa: {}", resource, useSSAToUpdateStatus); String resourceVersion = resource.getMetadata().getResourceVersion(); // don't do optimistic locking on patch originalResource.getMetadata().setResourceVersion(null); resource.getMetadata().setResourceVersion(null); try { - return resource(originalResource) - .editStatus(r -> resource); + if (useSSAToUpdateStatus) { + var managedFields = resource.getMetadata().getManagedFields(); + try { + + resource.getMetadata().setManagedFields(null); + var res = resource(resource); + return res.subresource("status").patch(new PatchContext.Builder() + .withFieldManager(fieldManager) + .withForce(true) + .withPatchType(PatchType.SERVER_SIDE_APPLY) + .build()); + } finally { + resource.getMetadata().setManagedFields(managedFields); + } + } else { + var res = resource(originalResource); + return res.editStatus(r -> resource); + } } finally { // restore initial resource version originalResource.getMetadata().setResourceVersion(resourceVersion); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java index ec2783f797..05a034a7a7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java @@ -7,10 +7,8 @@ /** * Creates an event source to trigger your reconciler whenever something happens to a secondary or - * external resource that would not normally trigger your reconciler (as the primary resources are - * not changed). To register EventSources with so that your reconciler is triggered, please make - * your reconciler implement - * {@link io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer}. + * external resource that should cause a reconciliation of the primary resource. EventSource + * generalizes the concept of Informers and extends it to external (i.e. non Kubernetes) resources. */ public interface EventSource extends LifecycleAware, EventSourceHealthIndicator { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java index c1ac2d3352..614525e970 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java @@ -28,7 +28,6 @@ public class ControllerResourceEventSource private static final Logger log = LoggerFactory.getLogger(ControllerResourceEventSource.class); private final Controller controller; - private final ResourceEventFilter legacyFilters; @SuppressWarnings({"unchecked", "rawtypes"}) public ControllerResourceEventSource(Controller controller) { @@ -42,8 +41,6 @@ public ControllerResourceEventSource(Controller controller) { .or(onUpdateGenerationAware(config.isGenerationAware())) .or(onUpdateMarkedForDeletion()); - legacyFilters = config.getEventFilter(); - // by default the on add should be processed in all cases regarding internal filters config.onAddFilter().ifPresent(this::setOnAddFilter); config.onUpdateFilter() @@ -74,9 +71,7 @@ public void eventReceived(ResourceAction action, T resource, T oldResource) { } MDCUtils.addResourceInfo(resource); controller.getEventSourceManager().broadcastOnResourceEvent(action, resource, oldResource); - if ((legacyFilters == null || - legacyFilters.acceptChange(controller, oldResource, resource)) - && isAcceptedByFilters(action, resource, oldResource)) { + if (isAcceptedByFilters(action, resource, oldResource)) { getEventHandler().handleEvent( new ResourceEvent(action, ResourceID.fromResource(resource), resource)); } else { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEventFilter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEventFilter.java deleted file mode 100644 index 08a86c92ae..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEventFilter.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source.controller; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.Controller; - -/** - * A functional interface to determine whether resource events should be processed by the SDK. This - * allows users to more finely tuned which events trigger a reconciliation than was previously - * possible (where the logic was limited to generation-based checking). - * - * @param

the type of custom resources handled by this filter - */ -@Deprecated(forRemoval = true) -@FunctionalInterface -public interface ResourceEventFilter

{ - - /** - * Determines whether the change between the old version of the resource and the new one needs to - * be propagated to the controller or not. - * - * @param controller the target controller - * @param oldResource the old version of the resource, null if no old resource available - * @param newResource the new version of the resource - * @return {@code true} if the change needs to be propagated to the controller, {@code false} - * otherwise - */ - boolean acceptChange(Controller

controller, P oldResource, P newResource); - - /** - * Combines this filter with the provided one with an AND logic, i.e. the resulting filter will - * only accept the change if both this and the other filter accept it, reject it otherwise. - * - * @param other the possibly {@code null} other filter to combine this one with - * @return a composite filter implementing the AND logic between this and the provided filter - */ - default ResourceEventFilter

and(ResourceEventFilter

other) { - return other == null ? this - : (Controller

controller, P oldResource, P newResource) -> { - boolean result = acceptChange(controller, oldResource, newResource); - return result && other.acceptChange(controller, oldResource, newResource); - }; - } - - /** - * Combines this filter with the provided one with an OR logic, i.e. the resulting filter will - * accept the change if any of this or the other filter accept it, rejecting it only if both - * reject it. - * - * @param other the possibly {@code null} other filter to combine this one with - * @return a composite filter implementing the OR logic between this and the provided filter - */ - default ResourceEventFilter

or(ResourceEventFilter

other) { - return other == null ? this - : (Controller

controller, P oldResource, P newResource) -> { - boolean result = acceptChange(controller, oldResource, newResource); - return result || other.acceptChange(controller, oldResource, newResource); - }; - } -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEventFilters.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEventFilters.java deleted file mode 100644 index 7024388b8b..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEventFilters.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source.controller; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -/** - * Convenience implementations of, and utility methods for, {@link ResourceEventFilter}. - */ -@Deprecated -public final class ResourceEventFilters { - - private static final ResourceEventFilter PASSTHROUGH = - (configuration, oldResource, newResource) -> true; - - private ResourceEventFilters() {} - - /** - * Retrieves a filter that accepts all events. - * - * @param the type of custom resource the filter should handle - * @return a filter that accepts all events - */ - @SuppressWarnings("unchecked") - public static ResourceEventFilter passthrough() { - return (ResourceEventFilter) PASSTHROUGH; - } - -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java index 9d5a83dc51..d1809de566 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java @@ -1,7 +1,6 @@ package io.javaoperatorsdk.operator.processing.retry; import io.javaoperatorsdk.operator.api.config.AnnotationConfigurable; -import io.javaoperatorsdk.operator.api.config.RetryConfiguration; public class GenericRetry implements Retry, AnnotationConfigurable { private int maxAttempts = GradualRetry.DEFAULT_MAX_ATTEMPTS; @@ -19,23 +18,6 @@ public static GenericRetry noRetry() { return new GenericRetry().setMaxAttempts(0); } - /** - * @deprecated Use the {@link GradualRetry} annotation instead - * - * @param configuration retry config - * @return Retry instance - */ - @Deprecated(forRemoval = true) - public static Retry fromConfiguration(RetryConfiguration configuration) { - return configuration == null ? defaultLimitedExponentialRetry() - : new GenericRetry() - .setInitialInterval(configuration.getInitialInterval()) - .setMaxAttempts(configuration.getMaxAttempts()) - .setIntervalMultiplier(configuration.getIntervalMultiplier()) - .setMaxInterval(configuration.getMaxInterval()); - } - - public static GenericRetry every10second10TimesRetry() { return new GenericRetry().withLinearRetry().setMaxAttempts(10).setInitialInterval(10000); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 1c86886d89..d8caf50868 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -11,11 +11,8 @@ import io.fabric8.kubernetes.client.informers.cache.BasicItemStore; import io.fabric8.kubernetes.client.informers.cache.Cache; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationResolver; -import io.javaoperatorsdk.operator.api.reconciler.Constants; -import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; @@ -26,11 +23,7 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfigBuilder; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class ControllerConfigurationOverriderTest { private final BaseConfigurationService configurationService = new BaseConfigurationService(); @@ -46,7 +39,8 @@ void overridingNSShouldPreserveUntouchedDependents() { var configuration = createConfiguration(new NamedDependentReconciler()); // check that we have the proper number of dependent configs - var dependentResources = configuration.getDependentResources(); + var dependentResources = + configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertEquals(2, dependentResources.size()); // override the NS @@ -61,60 +55,13 @@ void overridingNSShouldPreserveUntouchedDependents() { assertEquals(Set.of(namespace), configuration.getNamespaces()); // check that we still have the proper number of dependent configs - dependentResources = configuration.getDependentResources(); + dependentResources = configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertEquals(2, dependentResources.size()); final var resourceConfig = extractDependentKubernetesResourceConfig( configuration, 1); assertEquals(stringConfig, resourceConfig); } - @ControllerConfiguration(dependents = { - @Dependent(type = NamedDependentReconciler.NamedDependentResource.class), - @Dependent(type = NamedDependentReconciler.ExternalDependentResource.class) - }) - private static class NamedDependentReconciler implements Reconciler { - - @Override - public UpdateControl reconcile(ConfigMap resource, Context context) - throws Exception { - return null; - } - - private static class NamedDependentResource - extends KubernetesDependentResource { - - public NamedDependentResource() { - super(ConfigMap.class); - } - } - - private static class ExternalDependentResource implements DependentResource, - DependentResourceConfigurator { - - private String config = "UNSET"; - - @Override - public ReconcileResult reconcile(ConfigMap primary, Context context) { - return null; - } - - @Override - public Class resourceType() { - return Object.class; - } - - @Override - public void configureWith(String config) { - this.config = config; - } - - @Override - public Optional configuration() { - return Optional.of(config); - } - } - } - @SuppressWarnings("rawtypes") private KubernetesDependentResourceConfig extractFirstDependentKubernetesResourceConfig( io.javaoperatorsdk.operator.api.config.ControllerConfiguration configuration) { @@ -124,7 +71,8 @@ private KubernetesDependentResourceConfig extractFirstDependentKubernetesResourc private Object extractDependentKubernetesResourceConfig( io.javaoperatorsdk.operator.api.config.ControllerConfiguration configuration, int index) { - final var spec = configuration.getDependentResources().get(index); + final var spec = + configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().get(index); return DependentResourceConfigurationResolver.configurationFor(spec, configuration); } @@ -321,7 +269,7 @@ void alreadyOverriddenDependentNamespacesShouldNotBePropagated() { @Test void replaceNamedDependentResourceConfigShouldWork() { var configuration = createConfiguration(new OneDepReconciler()); - var dependents = configuration.getDependentResources(); + var dependents = configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertFalse(dependents.isEmpty()); assertEquals(1, dependents.size()); @@ -354,7 +302,7 @@ void replaceNamedDependentResourceConfigShouldWork() { .withLabelSelector(labelSelector) .build()) .build(); - dependents = overridden.getDependentResources(); + dependents = overridden.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); dependentSpec = dependents.stream().filter(dr -> dr.getName().equals(dependentResourceName)) .findFirst().orElseThrow(); config = (KubernetesDependentResourceConfig) DependentResourceConfigurationResolver @@ -366,7 +314,8 @@ void replaceNamedDependentResourceConfigShouldWork() { assertTrue(dependentSpec.getReadyCondition() instanceof TestCondition); } - @ControllerConfiguration(dependents = @Dependent(type = ReadOnlyDependent.class)) + @Workflow(dependents = @Dependent(type = ReadOnlyDependent.class)) + @ControllerConfiguration private static class WatchAllNamespacesReconciler implements Reconciler { @Override @@ -375,7 +324,8 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration(dependents = @Dependent(type = WatchAllNSDependent.class)) + @Workflow(dependents = @Dependent(type = WatchAllNSDependent.class)) + @ControllerConfiguration private static class DependentWatchesAllNSReconciler implements Reconciler { @Override @@ -394,9 +344,9 @@ public boolean isMet(DependentResource dependentResource, } } - @ControllerConfiguration(namespaces = OneDepReconciler.CONFIGURED_NS, - dependents = @Dependent(type = ReadOnlyDependent.class, - readyPostcondition = TestCondition.class)) + @Workflow(dependents = @Dependent(type = ReadOnlyDependent.class, + readyPostcondition = TestCondition.class)) + @ControllerConfiguration(namespaces = OneDepReconciler.CONFIGURED_NS) private static class OneDepReconciler implements Reconciler { private static final String CONFIGURED_NS = "foo"; @@ -423,8 +373,8 @@ public WatchAllNSDependent() { } } - @ControllerConfiguration(namespaces = OverriddenNSOnDepReconciler.CONFIGURED_NS, - dependents = @Dependent(type = OverriddenNSDependent.class)) + @Workflow(dependents = @Dependent(type = OverriddenNSDependent.class)) + @ControllerConfiguration(namespaces = OverriddenNSOnDepReconciler.CONFIGURED_NS) private static class OverriddenNSOnDepReconciler implements Reconciler { private static final String CONFIGURED_NS = "parentNS"; @@ -445,4 +395,51 @@ public OverriddenNSDependent() { super(ConfigMap.class); } } + + @Workflow(dependents = { + @Dependent(type = NamedDependentReconciler.NamedDependentResource.class), + @Dependent(type = NamedDependentReconciler.ExternalDependentResource.class) + }) + @ControllerConfiguration + private static class NamedDependentReconciler implements Reconciler { + + @Override + public UpdateControl reconcile(ConfigMap resource, Context context) { + return null; + } + + private static class NamedDependentResource + extends KubernetesDependentResource { + + public NamedDependentResource() { + super(ConfigMap.class); + } + } + + private static class ExternalDependentResource implements DependentResource, + DependentResourceConfigurator { + + private String config = "UNSET"; + + @Override + public ReconcileResult reconcile(ConfigMap primary, Context context) { + return null; + } + + @Override + public Class resourceType() { + return Object.class; + } + + @Override + public void configureWith(String config) { + this.config = config; + } + + @Override + public Optional configuration() { + return Optional.of(config); + } + } + } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java index 3187b32645..0b7dcd53a1 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java @@ -12,10 +12,7 @@ import io.fabric8.kubernetes.api.model.Service; import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; @@ -24,10 +21,7 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import static io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationResolverTest.CustomAnnotationReconciler.DR_NAME; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class DependentResourceConfigurationResolverTest { @@ -60,7 +54,7 @@ void controllerConfigurationProvidedShouldBeReturnedIfAvailable() { final var overridden = ControllerConfigurationOverrider.override(cfg) .replacingNamedDependentResourceConfig(DR_NAME, newConfig) .build(); - final var spec = cfg.getDependentResources().stream() + final var spec = cfg.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().stream() .filter(s -> DR_NAME.equals(s.getName())) .findFirst() .orElseThrow(); @@ -125,12 +119,13 @@ public Object configFrom(Annotation configAnnotation, assertEquals(overriddenConverter, converter); } - @ControllerConfiguration(dependents = { + @Workflow(dependents = { @Dependent(type = CustomAnnotatedDep.class, name = DR_NAME), @Dependent(type = ChildCustomAnnotatedDep.class), @Dependent(type = ConfigMapDep.class), @Dependent(type = ServiceDep.class) }) + @ControllerConfiguration static class CustomAnnotationReconciler implements Reconciler { public static final String DR_NAME = "first"; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtilsTest.java similarity index 75% rename from operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java rename to operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtilsTest.java index b89ae730ee..b606f1fc1c 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtilsTest.java @@ -8,15 +8,15 @@ import static org.assertj.core.api.Assertions.assertThat; -class EventSourceInitializerTest { +class EventSourceUtilsTest { @Test @SuppressWarnings({"rawtypes", "unchecked"}) void defaultNameDifferentForOtherInstance() { var eventSource1 = new PollingEventSource(HashMap::new, 1000, String.class); var eventSource2 = new PollingEventSource(HashMap::new, 1000, String.class); - var eventSourceName1 = EventSourceInitializer.generateNameFor(eventSource1); - var eventSourceName2 = EventSourceInitializer.generateNameFor(eventSource2); + var eventSourceName1 = EventSourceUtils.generateNameFor(eventSource1); + var eventSourceName2 = EventSourceUtils.generateNameFor(eventSource2); assertThat(eventSourceName1).isNotEqualTo(eventSourceName2); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index 4fe88296ac..b41fc0bac7 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -9,6 +9,8 @@ public class EmptyTestDependentResource implements DependentResource { + private String name; + @Override public ReconcileResult reconcile(TestCustomResource primary, Context context) { @@ -19,5 +21,14 @@ public ReconcileResult reconcile(TestCustomResource primary, public Class resourceType() { return Deployment.class; } + + @Override + public String name() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java index a2eea9279c..370d3d62c1 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java @@ -55,8 +55,7 @@ void matchesAdditiveOnlyChanges() { @Test void matchesWithStrongSpecEquality() { actual.getSpec().getTemplate().getMetadata().getLabels().put("new-key", "val"); - assertThat(match(dependentResource, actual, null, context, true, true, - true) + assertThat(match(desired, actual, true, true, context) .matched()) .withFailMessage("Adding values should fail matching when strong equality is required") .isFalse(); @@ -127,11 +126,11 @@ void matchesMetadata() { .withFailMessage("Annotations shouldn't matter when metadata is not considered") .isTrue(); - assertThat(match(dependentResource, actual, null, context, true, true, true).matched()) + assertThat(match(desired, actual, true, true, context).matched()) .withFailMessage("Annotations should matter when metadata is considered") .isFalse(); - assertThat(match(dependentResource, actual, null, context, true, false).matched()) + assertThat(match(desired, actual, false, false, context).matched()) .withFailMessage( "Should match when strong equality is not considered and only additive changes are made") .isTrue(); @@ -157,7 +156,7 @@ void matchConfigMap() { var actual = createConfigMap(); actual.getData().put("key2", "val2"); - var match = GenericKubernetesResourceMatcher.match(desired, actual, true, + var match = GenericKubernetesResourceMatcher.match(desired, actual, true, false, context); assertThat(match.matched()).isTrue(); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 941c3fa434..219e9af869 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -35,11 +35,8 @@ public class AbstractWorkflowExecutorTest { public class TestDependent extends KubernetesDependentResource { - private final String name; - public TestDependent(String name) { - super(ConfigMap.class); - this.name = name; + super(ConfigMap.class, name); } @Override @@ -52,7 +49,7 @@ public ReconcileResult reconcile(TestCustomResource primary, @Override public String toString() { - return name; + return name(); } } @@ -110,6 +107,11 @@ public Class resourceType() { return String.class; } + @Override + public String name() { + return name; + } + @Override public String toString() { return name; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java index 649e0c3050..fc254aa217 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java @@ -1,12 +1,14 @@ package io.javaoperatorsdk.operator.processing.dependent.workflow; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; @@ -62,7 +64,9 @@ ManagedWorkflow managedWorkflow(DependentResourceSpec... specs) { final var configuration = mock(ControllerConfiguration.class); final var specList = List.of(specs); - when(configuration.getDependentResources()).thenReturn(specList); + var ws = new WorkflowSpec(specList, false); + when(configuration.getWorkflowSpec()).thenReturn(Optional.of(ws)); + return new BaseConfigurationService().getWorkflowFactory() .workflowFor(configuration); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java index 9a61931a61..b41ee430f7 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java @@ -13,8 +13,10 @@ class WorkflowBuilderTest { @Test void workflowIsCleanerIfAtLeastOneDRIsCleaner() { var dr = mock(DependentResource.class); + when(dr.name()).thenReturn("dr"); var deleter = mock(DependentResource.class); when(deleter.isDeletable()).thenReturn(true); + when(deleter.name()).thenReturn("deleter"); var workflow = new WorkflowBuilder() .addDependentResource(deleter) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java index 099d5fad2b..c66fcb02d6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java @@ -538,7 +538,7 @@ void reconcilePreconditionNotCheckedOnNonActiveDependent() { @Test void deletesDependentsOfNonActiveDependentButNotTheNonActive() { TestDeleterDependent drDeleter2 = new TestDeleterDependent("DR_DELETER_2"); - TestDeleterDependent drDeleter3 = new TestDeleterDependent("DR_DELETER_2"); + TestDeleterDependent drDeleter3 = new TestDeleterDependent("DR_DELETER_3"); var workflow = new WorkflowBuilder() .addDependentResource(dr1).withActivationCondition(notMetCondition) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResultTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResultTest.java index 48cf3fa75a..c89ca53f07 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResultTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResultTest.java @@ -28,8 +28,8 @@ void throwsExceptionWithoutNumberingIfAllDifferentClass() { @Test void numbersDependentClassNamesIfMoreOfSameType() { - var res = new WorkflowResult(Map.of(new DependentA(), new RuntimeException(), - new DependentA(), new RuntimeException())); + var res = new WorkflowResult(Map.of(new DependentA("name1"), new RuntimeException(), + new DependentA("name2"), new RuntimeException())); try { res.throwAggregateExceptionIfErrorsPresent(); } catch (AggregatedOperatorException e) { @@ -39,6 +39,25 @@ void numbersDependentClassNamesIfMoreOfSameType() { @SuppressWarnings("rawtypes") static class DependentA implements DependentResource { + + private final String name; + + public DependentA() { + this(null); + } + + public DependentA(String name) { + this.name = name; + } + + @Override + public String name() { + if (name == null) { + return DependentResource.super.name(); + } + return name; + } + @Override public ReconcileResult reconcile(HasMetadata primary, Context context) { return null; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java index bc6ab8515f..ef48fccd6e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java @@ -17,8 +17,7 @@ import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.withSettings; +import static org.mockito.Mockito.*; @SuppressWarnings("rawtypes") class WorkflowTest { @@ -27,9 +26,9 @@ class WorkflowTest { @Test void zeroTopLevelDRShouldThrowException() { - var dr1 = mock(DependentResource.class); - var dr2 = mock(DependentResource.class); - var dr3 = mock(DependentResource.class); + var dr1 = mockDependent("dr1"); + var dr2 = mockDependent("dr2"); + var dr3 = mockDependent("dr3"); var cyclicWorkflowBuilderSetup = new WorkflowBuilder() .addDependentResource(dr1).dependsOn() @@ -43,9 +42,9 @@ void zeroTopLevelDRShouldThrowException() { @Test void calculatesTopLevelResources() { - var dr1 = mock(DependentResource.class); - var dr2 = mock(DependentResource.class); - var independentDR = mock(DependentResource.class); + var dr1 = mockDependent("dr1"); + var dr2 = mockDependent("dr2"); + var independentDR = mockDependent("independentDR"); var workflow = new WorkflowBuilder() .addDependentResource(independentDR) @@ -63,9 +62,9 @@ void calculatesTopLevelResources() { @Test void calculatesBottomLevelResources() { - var dr1 = mock(DependentResource.class); - var dr2 = mock(DependentResource.class); - var independentDR = mock(DependentResource.class); + var dr1 = mockDependent("dr1"); + var dr2 = mockDependent("dr2"); + var independentDR = mockDependent("independentDR"); Workflow workflow = new WorkflowBuilder() .addDependentResource(independentDR) @@ -100,4 +99,11 @@ void isDeletableShouldWork() { GarbageCollected.class)); assertFalse(DefaultWorkflow.isDeletable(dr.getClass())); } + + static DependentResource mockDependent(String name) { + var res = mock(DependentResource.class); + when(res.name()).thenReturn(name); + return res; + } + } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java index 9d538713c1..c52a976b86 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java @@ -18,7 +18,6 @@ import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.config.RetryConfiguration; import io.javaoperatorsdk.operator.api.monitoring.Metrics; import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; @@ -28,6 +27,7 @@ import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEvent; import io.javaoperatorsdk.operator.processing.event.source.timer.TimerEventSource; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; +import io.javaoperatorsdk.operator.processing.retry.GradualRetry; import io.javaoperatorsdk.operator.processing.retry.Retry; import io.javaoperatorsdk.operator.processing.retry.RetryExecution; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; @@ -135,7 +135,7 @@ void schedulesAnEventRetryOnException() { verify(retryTimerEventSourceMock, times(1)) .scheduleOnce(eq(ResourceID.fromResource(customResource)), - eq(RetryConfiguration.DEFAULT_INITIAL_INTERVAL)); + eq(GradualRetry.DEFAULT_INITIAL_INTERVAL)); } @Test @@ -167,7 +167,7 @@ void executesTheControllerInstantlyAfterErrorIfNewEventsReceived() { assertThat(allValues).hasSize(2); verify(retryTimerEventSourceMock, never()) .scheduleOnce(eq(ResourceID.fromResource(customResource)), - eq(RetryConfiguration.DEFAULT_INITIAL_INTERVAL)); + eq(GradualRetry.DEFAULT_INITIAL_INTERVAL)); } @Test diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java index c6aacb9071..d2e4cd52dc 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java @@ -43,19 +43,9 @@ import static io.javaoperatorsdk.operator.TestUtils.markForDeletion; import static io.javaoperatorsdk.operator.processing.event.ReconciliationDispatcher.MAX_UPDATE_RETRY; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @SuppressWarnings({"unchecked", "rawtypes"}) class ReconciliationDispatcherTest { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java deleted file mode 100644 index c8ec839b59..0000000000 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source; - -import java.util.List; -import java.util.Objects; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.MockKubernetesClient; -import io.javaoperatorsdk.operator.ReconcilerUtils; -import io.javaoperatorsdk.operator.TestUtils; -import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; -import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.config.ResolvedControllerConfiguration; -import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.event.EventHandler; -import io.javaoperatorsdk.operator.processing.event.EventSourceManager; -import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerResourceEventSource; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -class ResourceEventFilterTest { - public static final String FINALIZER = - ReconcilerUtils.getDefaultFinalizerName(TestCustomResource.class); - - private EventHandler eventHandler; - - @BeforeEach - public void before() { - this.eventHandler = mock(EventHandler.class); - } - - private ControllerResourceEventSource init(Controller controller) { - var eventSource = new ControllerResourceEventSource<>(controller); - eventSource.setEventHandler(eventHandler); - return eventSource; - } - - @Test - public void eventFilteredByCustomPredicate() { - var config = new TestControllerConfig( - FINALIZER, - false, - (configuration, oldResource, newResource) -> oldResource == null || !Objects.equals( - oldResource.getStatus().getConfigMapStatus(), - newResource.getStatus().getConfigMapStatus())); - - final var eventSource = init(new TestController(config)); - - TestCustomResource cr = TestUtils.testCustomResource(); - cr.getMetadata().setFinalizers(List.of(FINALIZER)); - cr.getMetadata().setGeneration(1L); - cr.getStatus().setConfigMapStatus("1"); - - TestCustomResource cr2 = TestUtils.testCustomResource(); - cr.getMetadata().setFinalizers(List.of(FINALIZER)); - cr.getMetadata().setGeneration(1L); - cr.getStatus().setConfigMapStatus("2"); - - eventSource.eventReceived(ResourceAction.UPDATED, cr, cr2); - verify(eventHandler, times(1)).handleEvent(any()); - - cr.getMetadata().setGeneration(1L); - cr.getStatus().setConfigMapStatus("1"); - - eventSource.eventReceived(ResourceAction.UPDATED, cr, cr); - verify(eventHandler, times(1)).handleEvent(any()); - } - - @Test - public void eventFilteredByCustomPredicateAndGenerationAware() { - var config = new TestControllerConfig( - FINALIZER, - true, - (configuration, oldResource, newResource) -> oldResource == null || !Objects.equals( - oldResource.getStatus().getConfigMapStatus(), - newResource.getStatus().getConfigMapStatus())); - - final var eventSource = init(new TestController(config)); - - TestCustomResource cr = TestUtils.testCustomResource(); - cr.getMetadata().setFinalizers(List.of(FINALIZER)); - cr.getMetadata().setGeneration(1L); - cr.getStatus().setConfigMapStatus("1"); - - TestCustomResource cr2 = TestUtils.testCustomResource(); - cr.getMetadata().setFinalizers(List.of(FINALIZER)); - cr.getMetadata().setGeneration(2L); - cr.getStatus().setConfigMapStatus("1"); - - eventSource.eventReceived(ResourceAction.UPDATED, cr, cr2); - verify(eventHandler, times(1)).handleEvent(any()); - - cr.getMetadata().setGeneration(1L); - cr.getStatus().setConfigMapStatus("2"); - - eventSource.eventReceived(ResourceAction.UPDATED, cr, cr); - verify(eventHandler, times(1)).handleEvent(any()); - } - - @Test - public void eventAlwaysFilteredByCustomPredicate() { - var config = new TestControllerConfig( - FINALIZER, - false, - (configuration, oldResource, newResource) -> !Objects.equals( - oldResource.getStatus().getConfigMapStatus(), - newResource.getStatus().getConfigMapStatus())); - - final var eventSource = init(new TestController(config)); - - TestCustomResource cr = TestUtils.testCustomResource(); - cr.getMetadata().setGeneration(1L); - cr.getStatus().setConfigMapStatus("1"); - - eventSource.eventReceived(ResourceAction.UPDATED, cr, cr); - verify(eventHandler, times(0)).handleEvent(any()); - } - - private static class TestControllerConfig extends ControllerConfig { - public TestControllerConfig(String finalizer, boolean generationAware, - ResourceEventFilter eventFilter) { - super(finalizer, generationAware, eventFilter, TestCustomResource.class); - } - } - - private static class ControllerConfig extends - ResolvedControllerConfiguration { - - public ControllerConfig(String finalizer, boolean generationAware, - ResourceEventFilter eventFilter, Class customResourceClass) { - super(customResourceClass, - "test", - generationAware, - null, - null, - null, - null, - null, - null, - null, null, null, finalizer, null, null, null, new BaseConfigurationService(), null); - setEventFilter(eventFilter); - } - } - - private static class TestController extends Controller { - - public TestController(ControllerConfiguration configuration) { - super(null, configuration, MockKubernetesClient.client(TestCustomResource.class)); - } - - @SuppressWarnings("unchecked") - @Override - public EventSourceManager getEventSourceManager() { - return mock(EventSourceManager.class); - } - } - -} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java index 64f0993139..04e0bc0aff 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java @@ -11,6 +11,8 @@ import io.javaoperatorsdk.operator.TestUtils; import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; import io.javaoperatorsdk.operator.api.config.ResolvedControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.processing.Controller; import io.javaoperatorsdk.operator.processing.event.EventHandler; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; @@ -152,18 +154,21 @@ void genericFilterFiltersOutAddUpdateAndDeleteEvents() { @SuppressWarnings("unchecked") private static class TestController extends Controller { + private static final Reconciler reconciler = + (resource, context) -> UpdateControl.noUpdate(); + private final EventSourceManager eventSourceManager = mock(EventSourceManager.class); public TestController(OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, GenericFilter genericFilter) { - super(null, new TestConfiguration(true, onAddFilter, onUpdateFilter, genericFilter), + super(reconciler, new TestConfiguration(true, onAddFilter, onUpdateFilter, genericFilter), MockKubernetesClient.client(TestCustomResource.class)); } public TestController(boolean generationAware) { - super(null, new TestConfiguration(generationAware, null, null, null), + super(reconciler, new TestConfiguration(generationAware, null, null, null), MockKubernetesClient.client(TestCustomResource.class)); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java index 1dcd9df464..1659995877 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java @@ -4,38 +4,10 @@ import org.junit.jupiter.api.Test; -import io.javaoperatorsdk.operator.api.config.RetryConfiguration; - import static org.assertj.core.api.Assertions.assertThat; public class GenericRetryExecutionTest { - @Test - public void forFirstBackOffAlwaysReturnsInitialInterval() { - assertThat(getDefaultRetryExecution().nextDelay().get()) - .isEqualTo(RetryConfiguration.DEFAULT_INITIAL_INTERVAL); - } - - @Test - public void delayIsMultipliedEveryNextDelayCall() { - RetryExecution retryExecution = getDefaultRetryExecution(); - - Optional res = callNextDelayNTimes(retryExecution, 1); - assertThat(res.get()).isEqualTo(RetryConfiguration.DEFAULT_INITIAL_INTERVAL); - - res = retryExecution.nextDelay(); - assertThat(res.get()) - .isEqualTo((long) (RetryConfiguration.DEFAULT_INITIAL_INTERVAL - * RetryConfiguration.DEFAULT_MULTIPLIER)); - - res = retryExecution.nextDelay(); - assertThat(res.get()) - .isEqualTo( - (long) (RetryConfiguration.DEFAULT_INITIAL_INTERVAL - * RetryConfiguration.DEFAULT_MULTIPLIER - * RetryConfiguration.DEFAULT_MULTIPLIER)); - } - @Test public void noNextDelayIfMaxAttemptLimitReached() { RetryExecution retryExecution = diff --git a/operator-framework-junit5/pom.xml b/operator-framework-junit5/pom.xml index d7ca656556..5f4fa10644 100644 --- a/operator-framework-junit5/pom.xml +++ b/operator-framework-junit5/pom.xml @@ -5,18 +5,13 @@ java-operator-sdk io.javaoperatorsdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT 4.0.0 operator-framework-junit-5 Operator SDK - Framework - JUnit 5 extension - - 11 - 11 - - io.javaoperatorsdk diff --git a/operator-framework/pom.xml b/operator-framework/pom.xml index e1cad88810..8a08b3a4a8 100644 --- a/operator-framework/pom.xml +++ b/operator-framework/pom.xml @@ -5,7 +5,7 @@ java-operator-sdk io.javaoperatorsdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT 4.0.0 @@ -72,7 +72,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl test diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/CustomResourceFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/CustomResourceFilterIT.java deleted file mode 100644 index 6733abaa47..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/CustomResourceFilterIT.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.javaoperatorsdk.operator; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.sample.customfilter.CustomFilteringTestReconciler; -import io.javaoperatorsdk.operator.sample.customfilter.CustomFilteringTestResource; -import io.javaoperatorsdk.operator.sample.customfilter.CustomFilteringTestResourceSpec; - -import static org.assertj.core.api.Assertions.assertThat; - -class CustomResourceFilterIT { - - @RegisterExtension - LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new CustomFilteringTestReconciler()) - .build(); - - @Test - void doesCustomFiltering() throws InterruptedException { - var filtered1 = createTestResource("filtered1", true, false); - var filtered2 = createTestResource("filtered2", false, true); - var notFiltered = createTestResource("notfiltered", true, true); - operator.create(filtered1); - operator.create(filtered2); - operator.create(notFiltered); - - Thread.sleep(300); - - assertThat( - ((CustomFilteringTestReconciler) operator.getReconcilers().get(0)).getNumberOfExecutions()) - .isEqualTo(1); - } - - - CustomFilteringTestResource createTestResource(String name, boolean filter1, boolean filter2) { - CustomFilteringTestResource resource = new CustomFilteringTestResource(); - resource.setMetadata(new ObjectMeta()); - resource.getMetadata().setName(name); - resource.setSpec(new CustomFilteringTestResourceSpec()); - resource.getSpec().setFilter1(filter1); - resource.getSpec().setFilter2(filter2); - return resource; - } - -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java index 7452958b8c..a0cc9c5e8e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java @@ -34,7 +34,7 @@ class ExternalStateBulkIT { .build(); @Test - void reconcilesResourceWithPersistentState() throws InterruptedException { + void reconcilesResourceWithPersistentState() { var resource = operator.create(testResource()); assertResources(resource, INITIAL_TEST_DATA, INITIAL_BULK_SIZE); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java index d3e7f77fd5..224e9c487a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java @@ -1,7 +1,6 @@ package io.javaoperatorsdk.operator; import java.time.Duration; -import java.util.stream.IntStream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -9,54 +8,73 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap; import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceCustomResource; import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler; +import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceSpec; +import io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.*; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap.DATA_KEY; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap.getConfigMapName; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler.FIRST_CONFIG_MAP_ID; +import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler.SECOND_CONFIG_MAP_ID; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -class MultipleDependentResourceIT { +public class MultipleDependentResourceIT { + + public static final String CHANGED_VALUE = "changed value"; + public static final String INITIAL_VALUE = "initial value"; - public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource"; @RegisterExtension - LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() - .withReconciler(MultipleDependentResourceReconciler.class) - .waitForNamespaceDeletion(true) + .withReconciler(new MultipleDependentResourceReconciler()) .build(); @Test - void twoConfigMapsHaveBeenCreated() { - MultipleDependentResourceCustomResource customResource = createTestCustomResource(); - operator.create(customResource); - - var reconciler = operator.getReconcilerOfType(MultipleDependentResourceReconciler.class); - - await().pollDelay(Duration.ofMillis(300)) - .until(() -> reconciler.getNumberOfExecutions() <= 1); - - IntStream.of(MultipleDependentResourceReconciler.FIRST_CONFIG_MAP_ID, - MultipleDependentResourceReconciler.SECOND_CONFIG_MAP_ID).forEach(configMapId -> { - ConfigMap configMap = - operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId)); - assertThat(configMap).isNotNull(); - assertThat(configMap.getMetadata().getName()) - .isEqualTo(customResource.getConfigMapName(configMapId)); - assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY)) - .isEqualTo(String.valueOf(configMapId)); - }); - } + void handlesCRUDOperations() { + var res = extension.create(testResource()); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + }); + + res.getSpec().setValue(CHANGED_VALUE); + res = extension.replace(res); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); - public MultipleDependentResourceCustomResource createTestCustomResource() { - MultipleDependentResourceCustomResource resource = - new MultipleDependentResourceCustomResource(); - resource.setMetadata( - new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(operator.getNamespace()) - .build()); - return resource; + assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); + + extension.delete(res); + + await().timeout(Duration.ofSeconds(120)).untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); } + MultipleDependentResourceCustomResource testResource() { + var res = new MultipleDependentResourceCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName("test1") + .build()); + res.setSpec(new MultipleDependentResourceSpec()); + res.getSpec().setValue(INITIAL_VALUE); + + return res; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceWithNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceWithNoDiscriminatorIT.java new file mode 100644 index 0000000000..908cb58f79 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceWithNoDiscriminatorIT.java @@ -0,0 +1,65 @@ +package io.javaoperatorsdk.operator; + +import java.time.Duration; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceConfigMap; +import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceCustomResourceWithDiscriminator; +import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceWithDiscriminatorReconciler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class MultipleDependentResourceWithNoDiscriminatorIT { + + public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource"; + @RegisterExtension + LocallyRunOperatorExtension operator = + LocallyRunOperatorExtension.builder() + .withReconciler(MultipleDependentResourceWithDiscriminatorReconciler.class) + .waitForNamespaceDeletion(true) + .build(); + + @Test + void twoConfigMapsHaveBeenCreated() { + MultipleDependentResourceCustomResourceWithDiscriminator customResource = + createTestCustomResource(); + operator.create(customResource); + + var reconciler = + operator.getReconcilerOfType(MultipleDependentResourceWithDiscriminatorReconciler.class); + + await().pollDelay(Duration.ofMillis(300)) + .until(() -> reconciler.getNumberOfExecutions() <= 1); + + IntStream.of(MultipleDependentResourceWithDiscriminatorReconciler.FIRST_CONFIG_MAP_ID, + MultipleDependentResourceWithDiscriminatorReconciler.SECOND_CONFIG_MAP_ID) + .forEach(configMapId -> { + ConfigMap configMap = + operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId)); + assertThat(configMap).isNotNull(); + assertThat(configMap.getMetadata().getName()) + .isEqualTo(customResource.getConfigMapName(configMapId)); + assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY)) + .isEqualTo(String.valueOf(configMapId)); + }); + } + + public MultipleDependentResourceCustomResourceWithDiscriminator createTestCustomResource() { + MultipleDependentResourceCustomResourceWithDiscriminator resource = + new MultipleDependentResourceCustomResourceWithDiscriminator(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(operator.getNamespace()) + .build()); + return resource; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java new file mode 100644 index 0000000000..361aa099af --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java @@ -0,0 +1,81 @@ +package io.javaoperatorsdk.operator; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.*; + +import static io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class MultipleManagedDependentNoDiscriminatorIT { + + public static final String RESOURCE_NAME = "test1"; + public static final String INITIAL_VALUE = "initial_value"; + public static final String CHANGED_VALUE = "changed_value"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(new MultipleManagedDependentSameTypeNoDiscriminatorReconciler()) + .build(); + + @Test + void handlesCRUDOperations() { + var res = extension.create(testResource()); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + }); + + res.getSpec().setValue(CHANGED_VALUE); + res = extension.replace(res); + + await().untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); + + extension.delete(res); + + await().timeout(Duration.ofSeconds(60)).untilAsserted(() -> { + var cm1 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = extension.get(ConfigMap.class, + RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); + } + + MultipleManagedDependentNoDiscriminatorCustomResource testResource() { + var res = new MultipleManagedDependentNoDiscriminatorCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME) + .build()); + res.setSpec(new MultipleManagedDependentNoDiscriminatorSpec()); + res.getSpec().setValue(INITIAL_VALUE); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/NextReconciliationImminentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/NextReconciliationImminentIT.java new file mode 100644 index 0000000000..9f9b464a83 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/NextReconciliationImminentIT.java @@ -0,0 +1,66 @@ +package io.javaoperatorsdk.operator; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.nextreconciliationimminent.NextReconciliationImminentCustomResource; +import io.javaoperatorsdk.operator.sample.nextreconciliationimminent.NextReconciliationImminentReconciler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class NextReconciliationImminentIT { + + private static final Logger log = + LoggerFactory.getLogger(NextReconciliationImminentIT.class); + + public static final int WAIT_FOR_EVENT = 300; + public static final String TEST_RESOURCE_NAME = "test1"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(new NextReconciliationImminentReconciler()) + .build(); + + @Test + void skippingStatusUpdateWithNextReconciliationImminent() throws InterruptedException { + var resource = extension.create(testResource()); + + var reconciler = extension.getReconcilerOfType(NextReconciliationImminentReconciler.class); + await().untilAsserted(() -> assertThat(reconciler.isReconciliationWaiting()).isTrue()); + Thread.sleep(WAIT_FOR_EVENT); + + resource.getMetadata().getAnnotations().put("trigger", "" + System.currentTimeMillis()); + extension.replace(resource); + Thread.sleep(WAIT_FOR_EVENT); + log.info("Made change to trigger event"); + + reconciler.allowReconciliationToProceed(); + Thread.sleep(WAIT_FOR_EVENT); + // second event arrived + await().untilAsserted(() -> assertThat(reconciler.isReconciliationWaiting()).isTrue()); + reconciler.allowReconciliationToProceed(); + + await().pollDelay(Duration.ofMillis(WAIT_FOR_EVENT)).untilAsserted(() -> { + assertThat(extension.get(NextReconciliationImminentCustomResource.class, TEST_RESOURCE_NAME) + .getStatus().getUpdateNumber()).isEqualTo(1); + }); + } + + + NextReconciliationImminentCustomResource testResource() { + var res = new NextReconciliationImminentCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .build()); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java index 16f223ce38..fb202de390 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java @@ -13,12 +13,13 @@ import io.javaoperatorsdk.operator.sample.primaryindexer.PrimaryIndexerTestCustomResourceSpec; import io.javaoperatorsdk.operator.sample.primaryindexer.PrimaryIndexerTestReconciler; +import static io.javaoperatorsdk.operator.sample.primaryindexer.AbstractPrimaryIndexerTestReconciler.CONFIG_MAP_NAME; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; class PrimaryIndexerIT { - public static final String CONFIG_MAP_NAME = "common-config-map"; + public static final String RESOURCE_NAME1 = "test1"; public static final String RESOURCE_NAME2 = "test2"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java index eaa7e4410f..0e48b726e9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java @@ -14,6 +14,7 @@ import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler; import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentSpec; +import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapDependent.TEST_CONFIG_MAP_NAME; import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapReconcilePrecondition.DO_NOT_RECONCILE; import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; @@ -21,7 +22,7 @@ class PrimaryToSecondaryDependentIT { - public static final String TEST_CONFIG_MAP_NAME = "testconfigmap"; + public static final String TEST_CR_NAME = "test1"; public static final String TEST_DATA = "testData"; public diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchNotLockingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchNotLockingIT.java index 1746e5737d..0a82bed51a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchNotLockingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchNotLockingIT.java @@ -58,10 +58,12 @@ void valuesAreDeletedIfSetToNull() { assertThat(actual.getStatus().getMessage()).isEqualTo(MESSAGE); }); + // resource needs to be read again to we don't replace the with wrong managed fields + resource = operator.get(StatusPatchLockingCustomResource.class, TEST_RESOURCE_NAME); resource.getSpec().setMessageInStatus(false); operator.replace(resource); - await().untilAsserted(() -> { + await().timeout(Duration.ofMinutes(3)).untilAsserted(() -> { var actual = operator.get(StatusPatchLockingCustomResource.class, TEST_RESOURCE_NAME); assertThat(actual.getStatus()).isNotNull(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchSSAMigrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchSSAMigrationIT.java new file mode 100644 index 0000000000..01702a5400 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchSSAMigrationIT.java @@ -0,0 +1,155 @@ +package io.javaoperatorsdk.operator; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import io.fabric8.kubernetes.api.model.Namespace; +import io.fabric8.kubernetes.api.model.NamespaceBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.statuspatchnonlocking.StatusPatchLockingCustomResource; +import io.javaoperatorsdk.operator.sample.statuspatchnonlocking.StatusPatchLockingCustomResourceSpec; +import io.javaoperatorsdk.operator.sample.statuspatchnonlocking.StatusPatchLockingReconciler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class StatusPatchSSAMigrationIT { + + public static final String TEST_RESOURCE_NAME = "test"; + + private final KubernetesClient client = new KubernetesClientBuilder().build(); + private String testNamespace; + + @BeforeEach + void beforeEach(TestInfo testInfo) { + LocallyRunOperatorExtension.applyCrd(StatusPatchLockingCustomResource.class, + client); + testInfo.getTestMethod() + .ifPresent(method -> testNamespace = KubernetesResourceUtil.sanitizeName(method.getName())); + client.namespaces().resource(testNamespace(testNamespace)).create(); + } + + @AfterEach + void afterEach() { + client.namespaces().withName(testNamespace).delete(); + await().untilAsserted(() -> { + var ns = client.namespaces().withName(testNamespace).get(); + assertThat(ns).isNull(); + }); + client.close(); + } + + + @Test + void testMigratingToSSA() { + var operator = startOperator(false); + var testResource = client.resource(testResource()).create(); + + await().untilAsserted(() -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(1); + }); + operator.stop(); + + // start operator with SSA + operator = startOperator(true); + await().untilAsserted(() -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(2); + }); + + var actualResource = client.resource(testResource()).get(); + actualResource.getSpec().setMessageInStatus(false); + client.resource(actualResource).update(); + + await().untilAsserted(() -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + // !!! This is wrong, the message should be null, + // see issue in Kubernetes: https://github.com/kubernetes/kubernetes/issues/99003 + assertThat(res.getStatus().getMessage()).isNotNull(); + assertThat(res.getStatus().getValue()).isEqualTo(3); + }); + + client.resource(testResource()).delete(); + operator.stop(); + } + + @Test + void workaroundMigratingFromToSSA() { + var operator = startOperator(false); + var testResource = client.resource(testResource()).create(); + + await().untilAsserted(() -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(1); + }); + operator.stop(); + + // start operator with SSA + operator = startOperator(true); + await().untilAsserted(() -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(2); + }); + + var actualResource = client.resource(testResource()).get(); + actualResource.getSpec().setMessageInStatus(false); + // removing the managed field entry for former method works + actualResource.getMetadata().setManagedFields(actualResource.getMetadata().getManagedFields() + .stream().filter(r -> !r.getOperation().equals("Update") && r.getSubresource() != null) + .toList()); + client.resource(actualResource).update(); + + await().untilAsserted(() -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()).isNull(); + assertThat(res.getStatus().getValue()).isEqualTo(3); + }); + + client.resource(testResource()).delete(); + operator.stop(); + } + + + private Operator startOperator(boolean patchStatusWithSSA) { + var operator = new Operator(o -> o.withCloseClientOnStop(false) + .withUseSSAForResourceStatusPatch(patchStatusWithSSA)); + operator.register(new StatusPatchLockingReconciler(), + o -> o.settingNamespaces(testNamespace)); + + operator.start(); + return operator; + } + + StatusPatchLockingCustomResource testResource() { + StatusPatchLockingCustomResource res = new StatusPatchLockingCustomResource(); + res.setSpec(new StatusPatchLockingCustomResourceSpec()); + res.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(testNamespace) + .build()); + return res; + } + + private Namespace testNamespace(String name) { + return new NamespaceBuilder().withMetadata(new ObjectMetaBuilder() + .withName(name) + .build()).build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitCleanupIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitCleanupIT.java new file mode 100644 index 0000000000..b26bfdd443 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitCleanupIT.java @@ -0,0 +1,50 @@ +package io.javaoperatorsdk.operator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.workflowexplicitcleanup.WorkflowExplicitCleanupCustomResource; +import io.javaoperatorsdk.operator.sample.workflowexplicitcleanup.WorkflowExplicitCleanupReconciler; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class WorkflowExplicitCleanupIT { + + public static final String RESOURCE_NAME = "test1"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(WorkflowExplicitCleanupReconciler.class) + .build(); + + @Test + void workflowInvokedExplicitly() { + var res = extension.create(testResource()); + + await().untilAsserted(() -> { + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + }); + + extension.delete(res); + + // The ConfigMap is not garbage collected, this tests that even if the cleaner is not + // implemented the workflow cleanup still called even if there is explicit invocation + await().untilAsserted(() -> { + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + }); + } + + WorkflowExplicitCleanupCustomResource testResource() { + var res = new WorkflowExplicitCleanupCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME) + .build()); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitInvocationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitInvocationIT.java new file mode 100644 index 0000000000..dba08faba0 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/WorkflowExplicitInvocationIT.java @@ -0,0 +1,66 @@ +package io.javaoperatorsdk.operator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.sample.workflowexplicitinvocation.WorkflowExplicitInvocationCustomResource; +import io.javaoperatorsdk.operator.sample.workflowexplicitinvocation.WorkflowExplicitInvocationReconciler; +import io.javaoperatorsdk.operator.sample.workflowexplicitinvocation.WorkflowExplicitInvocationSpec; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class WorkflowExplicitInvocationIT { + + public static final String RESOURCE_NAME = "test1"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(WorkflowExplicitInvocationReconciler.class) + .build(); + + @Test + void workflowInvokedExplicitly() { + var res = extension.create(testResource()); + var reconciler = extension.getReconcilerOfType(WorkflowExplicitInvocationReconciler.class); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + }); + + reconciler.setInvokeWorkflow(true); + + // trigger reconciliation + res.getSpec().setValue("changed value"); + res = extension.replace(res); + + await().untilAsserted(() -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2); + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + }); + + extension.delete(res); + + // The ConfigMap is not garbage collected, this tests that even if the cleaner is not + // implemented the workflow cleanup still called even if there is explicit invocation + await().untilAsserted(() -> { + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + }); + } + + WorkflowExplicitInvocationCustomResource testResource() { + var res = new WorkflowExplicitInvocationCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME) + .build()); + res.setSpec(new WorkflowExplicitInvocationSpec()); + res.getSpec().setValue("initial value"); + return res; + } + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java index e107623347..81d69f7596 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java @@ -22,11 +22,7 @@ import io.javaoperatorsdk.operator.api.config.dependent.Configured; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationResolver; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.MaxReconciliationInterval; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; @@ -42,13 +38,7 @@ import io.javaoperatorsdk.operator.processing.retry.RetryExecution; import io.javaoperatorsdk.operator.sample.readonly.ReadOnlyDependent; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; class BaseConfigurationServiceTest { @@ -83,7 +73,8 @@ void defaultValuesShouldBeConsistent() { @SuppressWarnings("rawtypes") private KubernetesDependentResourceConfig extractDependentKubernetesResourceConfig( io.javaoperatorsdk.operator.api.config.ControllerConfiguration configuration, int index) { - final var spec = configuration.getDependentResources().get(index); + final var spec = + configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().get(index); return (KubernetesDependentResourceConfig) DependentResourceConfigurationResolver .configurationFor(spec, configuration); } @@ -92,11 +83,11 @@ private KubernetesDependentResourceConfig extractDependentKubernetesResourceConf @SuppressWarnings("rawtypes") void getDependentResources() { var configuration = configFor(new NoDepReconciler()); - var dependents = configuration.getDependentResources(); - assertTrue(dependents.isEmpty()); + var workflowSpec = configuration.getWorkflowSpec(); + assertTrue(workflowSpec.isEmpty()); configuration = configFor(new OneDepReconciler()); - dependents = configuration.getDependentResources(); + var dependents = configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertFalse(dependents.isEmpty()); assertEquals(1, dependents.size()); final var dependentResourceName = DependentResource.defaultNameFor(ReadOnlyDependent.class); @@ -113,7 +104,7 @@ void getDependentResources() { assertEquals(Set.of(OneDepReconciler.CONFIGURED_NS), config.namespaces()); configuration = configFor(new NamedDepReconciler()); - dependents = configuration.getDependentResources(); + dependents = configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertFalse(dependents.isEmpty()); assertEquals(1, dependents.size()); dependentSpec = findByName(dependents, NamedDepReconciler.NAME); @@ -152,7 +143,7 @@ void tryingToAddDuplicatedDependentsWithoutNameShouldFail() { @Test void addingDuplicatedDependentsWithNameShouldWork() { var config = configFor(new NamedDuplicatedDepReconciler()); - var dependents = config.getDependentResources(); + var dependents = config.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertEquals(2, dependents.size()); assertTrue(findByNameOptional(dependents, NamedDuplicatedDepReconciler.NAME).isPresent() && findByNameOptional(dependents, DependentResource.defaultNameFor(ReadOnlyDependent.class)) @@ -237,7 +228,9 @@ void configuringFromCustomAnnotationsShouldWork() { private static int getValue( io.javaoperatorsdk.operator.api.config.ControllerConfiguration configuration, int index) { return ((CustomConfig) DependentResourceConfigurationResolver - .configurationFor(configuration.getDependentResources().get(index), configuration)) + .configurationFor( + configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().get(index), + configuration)) .getValue(); } @@ -253,8 +246,8 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration(namespaces = OneDepReconciler.CONFIGURED_NS, - dependents = @Dependent(type = ReadOnlyDependent.class)) + @Workflow(dependents = @Dependent(type = ReadOnlyDependent.class)) + @ControllerConfiguration(namespaces = OneDepReconciler.CONFIGURED_NS) private static class OneDepReconciler implements Reconciler { private static final String CONFIGURED_NS = "foo"; @@ -265,8 +258,8 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration( - dependents = @Dependent(type = ReadOnlyDependent.class, name = NamedDepReconciler.NAME)) + @Workflow(dependents = @Dependent(type = ReadOnlyDependent.class, name = NamedDepReconciler.NAME)) + @ControllerConfiguration private static class NamedDepReconciler implements Reconciler { private static final String NAME = "foo"; @@ -277,11 +270,11 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration( - dependents = { - @Dependent(type = ReadOnlyDependent.class), - @Dependent(type = ReadOnlyDependent.class) - }) + @Workflow(dependents = { + @Dependent(type = ReadOnlyDependent.class), + @Dependent(type = ReadOnlyDependent.class) + }) + @ControllerConfiguration private static class DuplicatedDepReconciler implements Reconciler { @Override @@ -290,11 +283,11 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration( - dependents = { - @Dependent(type = ReadOnlyDependent.class, name = NamedDuplicatedDepReconciler.NAME), - @Dependent(type = ReadOnlyDependent.class) - }) + @Workflow(dependents = { + @Dependent(type = ReadOnlyDependent.class, name = NamedDuplicatedDepReconciler.NAME), + @Dependent(type = ReadOnlyDependent.class) + }) + @ControllerConfiguration private static class NamedDuplicatedDepReconciler implements Reconciler { private static final String NAME = "duplicated"; @@ -314,10 +307,11 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration(dependents = { + @Workflow(dependents = { @Dependent(type = SelectorReconciler.WithAnnotation.class), @Dependent(type = ReadOnlyDependent.class) }) + @ControllerConfiguration private static class SelectorReconciler implements Reconciler { @Override @@ -438,10 +432,11 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @ControllerConfiguration(dependents = { + @Workflow(dependents = { @Dependent(type = CustomAnnotatedDep.class), @Dependent(type = ChildCustomAnnotatedDep.class) }) + @ControllerConfiguration() private static class CustomAnnotationReconciler implements Reconciler { @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java index 3b2acd942e..95be38fc4d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentReconciler.java @@ -2,13 +2,11 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = @Dependent(type = CRUDConfigMapBulkDependentResource.class)) +@Workflow(dependents = @Dependent(type = CRUDConfigMapBulkDependentResource.class)) +@ControllerConfiguration public class ManagedBulkDependentReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java index aca78d5d25..aca1e98c88 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedBulkDependentWithReadyConditionReconciler.java @@ -2,15 +2,12 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult; -@ControllerConfiguration(dependents = @Dependent(readyPostcondition = SampleBulkCondition.class, +@Workflow(dependents = @Dependent(readyPostcondition = SampleBulkCondition.class, type = CRUDConfigMapBulkDependentResource.class)) +@ControllerConfiguration() public class ManagedBulkDependentWithReadyConditionReconciler implements Reconciler { @@ -22,8 +19,9 @@ public UpdateControl reconcile( Context context) throws Exception { numberOfExecutions.incrementAndGet(); - var ready = context.managedDependentResourceContext().getWorkflowReconcileResult() - .map(WorkflowReconcileResult::allDependentResourcesReady).orElseThrow(); + var ready = context.managedWorkflowAndDependentResourceContext().getWorkflowReconcileResult() + .allDependentResourcesReady(); + resource.setStatus(new BulkDependentTestStatus()); resource.getStatus().setReady(ready); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java index e759bdd200..db5ba60044 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/ManagedDeleterBulkReconciler.java @@ -1,13 +1,10 @@ package io.javaoperatorsdk.operator.sample.bulkdependent; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration( - dependents = @Dependent(type = ConfigMapDeleterBulkDependentResource.class)) +@Workflow(dependents = @Dependent(type = ConfigMapDeleterBulkDependentResource.class)) +@ControllerConfiguration public class ManagedDeleterBulkReconciler implements Reconciler { @Override public UpdateControl reconcile( diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java index 6af93232b4..ef07bb5520 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/StandaloneBulkDependentReconciler.java @@ -9,8 +9,7 @@ @ControllerConfiguration public class StandaloneBulkDependentReconciler - implements Reconciler, TestExecutionInfoProvider, - EventSourceInitializer { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -38,7 +37,7 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer + return EventSourceUtils .nameEventSources(dependent.initEventSource(context)); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java index 2543422d74..f11621e4c2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/external/ExternalBulkResourceReconciler.java @@ -1,13 +1,11 @@ package io.javaoperatorsdk.operator.sample.bulkdependent.external; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource; -@ControllerConfiguration(dependents = @Dependent(type = ExternalBulkDependentResource.class)) +@Workflow(dependents = @Dependent(type = ExternalBulkDependentResource.class)) +@ControllerConfiguration() public class ExternalBulkResourceReconciler implements Reconciler { @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/changenamespace/ChangeNamespaceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/changenamespace/ChangeNamespaceTestReconciler.java index 7d51f311e1..36a46c9da3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/changenamespace/ChangeNamespaceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/changenamespace/ChangeNamespaceTestReconciler.java @@ -13,8 +13,7 @@ @ControllerConfiguration public class ChangeNamespaceTestReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { private final ConcurrentHashMap numberOfResourceReconciliations = new ConcurrentHashMap<>(); @@ -27,7 +26,7 @@ public Map prepareEventSources( new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) .build(), context); - return EventSourceInitializer.nameEventSources(configMapES); + return EventSourceUtils.nameEventSources(configMapES); } @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java index 6be29c5092..c4bbb3c9f0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java @@ -6,7 +6,8 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) +@ControllerConfiguration public class CleanerForManagedDependentTestReconciler implements Reconciler, TestExecutionInfoProvider { 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 a6f5e00c96..eedb5ae70b 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 @@ -13,8 +13,7 @@ @ControllerConfiguration public class ClusterScopedCustomResourceReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { public static final String DATA_KEY = "data-key"; @@ -59,6 +58,6 @@ public Map prepareEventSources( .withSecondaryToPrimaryMapper(Mappers.fromOwnerReference(true)) .withLabelSelector(TEST_LABEL_KEY + "=" + TEST_LABEL_VALUE) .build(), context); - return EventSourceInitializer.nameEventSources(ies); + return EventSourceUtils.nameEventSources(ies); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/complexdependent/ComplexDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/complexdependent/ComplexDependentReconciler.java index da0aaf1060..81bcb7e153 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/complexdependent/ComplexDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/complexdependent/ComplexDependentReconciler.java @@ -15,25 +15,23 @@ import static io.javaoperatorsdk.operator.sample.complexdependent.ComplexDependentReconciler.SERVICE_EVENT_SOURCE_NAME; import static io.javaoperatorsdk.operator.sample.complexdependent.ComplexDependentReconciler.STATEFUL_SET_EVENT_SOURCE_NAME; -@ControllerConfiguration( - name = "project-operator", - dependents = { - @Dependent(name = "first-svc", type = FirstService.class, - useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), - @Dependent(name = "second-svc", type = SecondService.class, - useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), - @Dependent(name = "first", type = FirstStatefulSet.class, - useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, - dependsOn = {"first-svc"}, - readyPostcondition = StatefulSetReadyCondition.class), - @Dependent(name = "second", - type = SecondStatefulSet.class, - useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, - dependsOn = {"second-svc", "first"}, - readyPostcondition = StatefulSetReadyCondition.class), - }) -public class ComplexDependentReconciler implements Reconciler, - EventSourceInitializer { +@Workflow(dependents = { + @Dependent(name = "first-svc", type = FirstService.class, + useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), + @Dependent(name = "second-svc", type = SecondService.class, + useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), + @Dependent(name = "first", type = FirstStatefulSet.class, + useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, + dependsOn = {"first-svc"}, + readyPostcondition = StatefulSetReadyCondition.class), + @Dependent(name = "second", + type = SecondStatefulSet.class, + useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, + dependsOn = {"second-svc", "first"}, + readyPostcondition = StatefulSetReadyCondition.class), +}) +@ControllerConfiguration(name = "project-operator") +public class ComplexDependentReconciler implements Reconciler { public static final String SERVICE_EVENT_SOURCE_NAME = "serviceEventSource"; public static final String STATEFUL_SET_EVENT_SOURCE_NAME = "statefulSetEventSource"; @@ -42,8 +40,8 @@ public class ComplexDependentReconciler implements Reconciler reconcile( ComplexDependentCustomResource resource, Context context) throws Exception { - var ready = context.managedDependentResourceContext().getWorkflowReconcileResult() - .orElseThrow().allDependentResourcesReady(); + var ready = context.managedWorkflowAndDependentResourceContext().getWorkflowReconcileResult() + .allDependentResourcesReady(); var status = Objects.requireNonNullElseGet(resource.getStatus(), ComplexDependentStatus::new); status.setStatus(ready ? RECONCILE_STATUS.READY : RECONCILE_STATUS.NOT_READY); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java index 884b5a859d..49091783f8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java @@ -2,14 +2,12 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = ConfigMapDependentResource.class)}) +@ControllerConfiguration() public class CreateOnlyIfNotExistingDependentWithSSAReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java index ab0369d998..d59c87236c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java @@ -15,8 +15,7 @@ @ControllerConfiguration public class CreateUpdateEventFilterTestReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { private static final class DirectConfigMapDependentResource extends @@ -97,7 +96,7 @@ public Map prepareEventSources( informerEventSource = new InformerEventSource<>(informerConfiguration, context.getClient()); this.configMapDR.setEventSource(informerEventSource); - return EventSourceInitializer.nameEventSources(informerEventSource); + return EventSourceUtils.nameEventSources(informerEventSource); } public int getNumberOfExecutions() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestReconciler.java deleted file mode 100644 index 1e42e8e6e1..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestReconciler.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.javaoperatorsdk.operator.sample.customfilter; - -import java.util.concurrent.atomic.AtomicInteger; - -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; - -@ControllerConfiguration(eventFilters = {CustomFlagFilter.class, CustomFlagFilter2.class}) -public class CustomFilteringTestReconciler implements Reconciler { - - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - - @Override - public UpdateControl reconcile(CustomFilteringTestResource resource, - Context context) { - numberOfExecutions.incrementAndGet(); - return UpdateControl.noUpdate(); - } - - public int getNumberOfExecutions() { - return numberOfExecutions.get(); - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestResourceSpec.java deleted file mode 100644 index 8bb1f48054..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestResourceSpec.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.javaoperatorsdk.operator.sample.customfilter; - -public class CustomFilteringTestResourceSpec { - - private boolean filter1; - - private boolean filter2; - - public boolean isFilter1() { - return filter1; - } - - public CustomFilteringTestResourceSpec setFilter1(boolean filter1) { - this.filter1 = filter1; - return this; - } - - public boolean isFilter2() { - return filter2; - } - - public CustomFilteringTestResourceSpec setFilter2(boolean filter2) { - this.filter2 = filter2; - return this; - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFlagFilter.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFlagFilter.java deleted file mode 100644 index bba45a44ac..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFlagFilter.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.javaoperatorsdk.operator.sample.customfilter; - -import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; - -public class CustomFlagFilter implements ResourceEventFilter { - - @Override - public boolean acceptChange(Controller configuration, - CustomFilteringTestResource oldResource, CustomFilteringTestResource newResource) { - return newResource.getSpec().isFilter1(); - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFlagFilter2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFlagFilter2.java deleted file mode 100644 index ae6b5d684f..0000000000 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFlagFilter2.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.javaoperatorsdk.operator.sample.customfilter; - -import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; - -public class CustomFlagFilter2 implements ResourceEventFilter { - - @Override - public boolean acceptChange(Controller configuration, - CustomFilteringTestResource oldResource, CustomFilteringTestResource newResource) { - return newResource.getSpec().isFilter2(); - } -} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java index ec4a2c86b9..b8a3168a56 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java @@ -13,8 +13,9 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration(dependents = @Dependent( +@Workflow(dependents = @Dependent( type = DependentAnnotationSecondaryMapperReconciler.ConfigMapDependentResource.class)) +@ControllerConfiguration public class DependentAnnotationSecondaryMapperReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentcustommappingannotation/DependentCustomMappingReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentcustommappingannotation/DependentCustomMappingReconciler.java index 8c14f829ff..6ac2626111 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentcustommappingannotation/DependentCustomMappingReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentcustommappingannotation/DependentCustomMappingReconciler.java @@ -3,8 +3,8 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration( - dependents = {@Dependent(type = CustomMappingConfigMapDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = CustomMappingConfigMapDependentResource.class)}) +@ControllerConfiguration public class DependentCustomMappingReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java index de9ea20f4a..d858c34223 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java @@ -6,10 +6,10 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration( - dependents = { - @Dependent(type = ConfigMapDependentResource.class), - }) +@Workflow(dependents = { + @Dependent(type = ConfigMapDependentResource.class), +}) +@ControllerConfiguration public class DependentDifferentNamespaceReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentfilter/DependentFilterTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentfilter/DependentFilterTestReconciler.java index 114491d9b9..97ff1b5484 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentfilter/DependentFilterTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentfilter/DependentFilterTestReconciler.java @@ -5,8 +5,8 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(onUpdateFilter = UpdateFilter.class, - dependents = {@Dependent(type = FilteredDependentConfigMap.class)}) +@Workflow(dependents = {@Dependent(type = FilteredDependentConfigMap.class)}) +@ControllerConfiguration(onUpdateFilter = UpdateFilter.class) public class DependentFilterTestReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java index 4ce74c75eb..d8551c72e6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java @@ -6,11 +6,10 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration( - namespaces = Constants.WATCH_CURRENT_NAMESPACE, - dependents = { - @Dependent(type = ConfigMapDependentResource.class), - }) +@Workflow(dependents = { + @Dependent(type = ConfigMapDependentResource.class) +}) +@ControllerConfiguration(namespaces = Constants.WATCH_CURRENT_NAMESPACE) public class DependentOperationEventFilterCustomResourceTestReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentreinitialization/DependentReInitializationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentreinitialization/DependentReInitializationReconciler.java index a8e6a48e6b..65916d7a58 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentreinitialization/DependentReInitializationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentreinitialization/DependentReInitializationReconciler.java @@ -7,8 +7,7 @@ @ControllerConfiguration public class DependentReInitializationReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { private final ConfigMapDependentResource configMapDependentResource; @@ -27,7 +26,7 @@ public UpdateControl reconcile( @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSourcesFromDependentResource(context, + return EventSourceUtils.nameEventSourcesFromDependentResource(context, configMapDependentResource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentresourcecrossref/DependentResourceCrossRefReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentresourcecrossref/DependentResourceCrossRefReconciler.java index bb319741b3..0d6d63024f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentresourcecrossref/DependentResourceCrossRefReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentresourcecrossref/DependentResourceCrossRefReconciler.java @@ -14,11 +14,12 @@ import static io.javaoperatorsdk.operator.sample.dependentresourcecrossref.DependentResourceCrossRefReconciler.SECRET_NAME; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(name = SECRET_NAME, type = DependentResourceCrossRefReconciler.SecretDependentResource.class), @Dependent(type = DependentResourceCrossRefReconciler.ConfigMapDependentResource.class, dependsOn = SECRET_NAME)}) +@ControllerConfiguration public class DependentResourceCrossRefReconciler implements Reconciler, ErrorStatusHandler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentssa/DependentSSAReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentssa/DependentSSAReconciler.java index f1c11dea6d..44395ead5e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentssa/DependentSSAReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/dependentssa/DependentSSAReconciler.java @@ -11,8 +11,7 @@ @ControllerConfiguration public class DependentSSAReconciler - implements Reconciler, TestExecutionInfoProvider, - EventSourceInitializer { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -45,7 +44,7 @@ public int getNumberOfExecutions() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSourcesFromDependentResource(context, + return EventSourceUtils.nameEventSourcesFromDependentResource(context, ssaConfigMapDependent); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateDependentReconciler.java index 8755e7099c..9c2b019adc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateDependentReconciler.java @@ -11,11 +11,10 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration( - dependents = @Dependent(type = ExternalWithStateDependentResource.class)) +@Workflow(dependents = @Dependent(type = ExternalWithStateDependentResource.class)) +@ControllerConfiguration public class ExternalStateDependentReconciler implements Reconciler, - EventSourceInitializer, TestExecutionInfoProvider { public static final String ID_KEY = "id"; @@ -39,7 +38,7 @@ public Map prepareEventSources( EventSourceContext context) { var configMapEventSource = new InformerEventSource<>( InformerConfiguration.from(ConfigMap.class, context).build(), context); - return EventSourceInitializer.nameEventSources(configMapEventSource); + return EventSourceUtils.nameEventSources(configMapEventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java index 66c53c3971..a9c3bb5a28 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java @@ -20,10 +20,9 @@ import io.javaoperatorsdk.operator.support.ExternalResource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration() +@ControllerConfiguration public class ExternalStateReconciler implements Reconciler, Cleaner, - EventSourceInitializer, TestExecutionInfoProvider { public static final String ID_KEY = "id"; @@ -116,7 +115,7 @@ public Map prepareEventSources( return externalResource.map(Set::of).orElseGet(Collections::emptySet); }, context, Duration.ofMillis(300L), ExternalResource.class); - return EventSourceInitializer.nameEventSources(configMapEventSource, + return EventSourceUtils.nameEventSources(configMapEventSource, externalResourceEventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java index c25113b406..0965c49636 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java @@ -34,16 +34,26 @@ public ExternalWithStateDependentResource() { @SuppressWarnings("unchecked") public Set fetchResources( ExternalStateCustomResource primaryResource) { - Optional configMapOptional = - getExternalStateEventSource().getSecondaryResource(primaryResource); - - return configMapOptional.map(configMap -> { - var id = configMap.getData().get(ID_KEY); + return getResourceID(primaryResource).map(id -> { var externalResource = externalService.read(id); return externalResource.map(Set::of).orElseGet(Collections::emptySet); }).orElseGet(Collections::emptySet); } + @Override + protected Optional selectManagedSecondaryResource( + Set secondaryResources, + ExternalStateCustomResource primary, Context context) { + var id = getResourceID(primary); + return id.flatMap(k -> secondaryResources.stream().filter(e -> e.getId().equals(k)).findAny()); + } + + private Optional getResourceID(ExternalStateCustomResource primaryResource) { + Optional configMapOptional = + getExternalStateEventSource().getSecondaryResource(primaryResource); + return configMapOptional.map(cm -> cm.getData().get(ID_KEY)); + } + @Override protected ExternalResource desired(ExternalStateCustomResource primary, Context context) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java index ebc1655c38..a69feabb71 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java @@ -5,22 +5,16 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration( - dependents = @Dependent(type = BulkDependentResourceExternalWithState.class)) +@Workflow(dependents = @Dependent(type = BulkDependentResourceExternalWithState.class)) +@ControllerConfiguration public class ExternalStateBulkDependentReconciler implements Reconciler, - EventSourceInitializer, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -43,7 +37,7 @@ public Map prepareEventSources( EventSourceContext context) { var configMapEventSource = new InformerEventSource<>( InformerConfiguration.from(ConfigMap.class, context).build(), context); - return EventSourceInitializer.nameEventSources(configMapEventSource); + return EventSourceUtils.nameEventSources(configMapEventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/filter/FilterTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/filter/FilterTestReconciler.java index ab5c9b7400..541e220f4a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/filter/FilterTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/filter/FilterTestReconciler.java @@ -12,8 +12,7 @@ @ControllerConfiguration(onUpdateFilter = UpdateFilter.class) public class FilterTestReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { public static final String CONFIG_MAP_FILTER_VALUE = "config_map_skip_this"; public static final String CUSTOM_RESOURCE_FILTER_VALUE = "custom_resource_skip_this"; @@ -59,6 +58,6 @@ public Map prepareEventSources( .equals(CONFIG_MAP_FILTER_VALUE)) .build(), context); - return EventSourceInitializer.nameEventSources(configMapES); + return EventSourceUtils.nameEventSources(configMapES); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java index 64651ec23e..bab120cfac 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java @@ -3,8 +3,8 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration( - dependents = {@Dependent(type = ConfigMapGenericKubernetesDependent.class)}) +@Workflow(dependents = {@Dependent(type = ConfigMapGenericKubernetesDependent.class)}) +@ControllerConfiguration public class GenericKubernetesDependentManagedReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java index 1969ad8f2a..1cb1372abe 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java @@ -7,8 +7,7 @@ @ControllerConfiguration public class GenericKubernetesDependentStandaloneReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { private final ConfigMapGenericKubernetesDependent dependent = new ConfigMapGenericKubernetesDependent(); @@ -28,6 +27,6 @@ public UpdateControl reconci @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSources(dependent.eventSource(context).orElseThrow()); + return EventSourceUtils.nameEventSources(dependent.eventSource(context).orElseThrow()); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java index 45be0281f6..2b967fa62c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/generickubernetesresource/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java @@ -14,8 +14,7 @@ @ControllerConfiguration public class GenericKubernetesResourceHandlingReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { public static final String VERSION = "v1"; @@ -72,6 +71,6 @@ public Map prepareEventSources( new GroupVersionKind("", VERSION, KIND), context).build(), context); - return EventSourceInitializer.nameEventSources(informerEventSource); + return EventSourceUtils.nameEventSources(informerEventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java index 4acda2feee..927f7e8efd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/indexdiscriminator/IndexDiscriminatorTestReconciler.java @@ -16,7 +16,7 @@ public class IndexDiscriminatorTestReconciler implements Reconciler, Cleaner, - TestExecutionInfoProvider, EventSourceInitializer { + TestExecutionInfoProvider { public static final String FIRST_CONFIG_MAP_SUFFIX_1 = "-1"; public static final String FIRST_CONFIG_MAP_SUFFIX_2 = "-2"; @@ -81,7 +81,7 @@ public Map prepareEventSources( secondDependentResourceConfigMap .setResourceDiscriminator( new TestIndexDiscriminator(CONFIG_MAP_INDEX_2, FIRST_CONFIG_MAP_SUFFIX_2)); - return EventSourceInitializer.nameEventSources(eventSource); + return EventSourceUtils.nameEventSources(eventSource); } public static String configMapKey(ConfigMap configMap) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informereventsource/InformerEventSourceTestCustomReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informereventsource/InformerEventSourceTestCustomReconciler.java index bf92550542..d9a44cb027 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informereventsource/InformerEventSourceTestCustomReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informereventsource/InformerEventSourceTestCustomReconciler.java @@ -9,12 +9,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; 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; @@ -25,8 +20,7 @@ */ @ControllerConfiguration public class InformerEventSourceTestCustomReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { private static final Logger LOGGER = LoggerFactory.getLogger(InformerEventSourceTestCustomReconciler.class); @@ -46,7 +40,7 @@ public Map prepareEventSources( .withSecondaryToPrimaryMapper(Mappers.fromAnnotation(RELATED_RESOURCE_NAME)) .build(); - return EventSourceInitializer + return EventSourceUtils .nameEventSources(new InformerEventSource<>(config, context)); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java index f71f243c79..8b0511e486 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java @@ -6,19 +6,16 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.client.KubernetesClient; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; +@Workflow(dependents = @Dependent( + name = InformerRelatedBehaviorTestReconciler.CONFIG_MAP_DEPENDENT_RESOURCE, + type = ConfigMapDependentResource.class)) @ControllerConfiguration( - name = InformerRelatedBehaviorTestReconciler.INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER, - dependents = @Dependent( - name = InformerRelatedBehaviorTestReconciler.CONFIG_MAP_DEPENDENT_RESOURCE, - type = ConfigMapDependentResource.class)) + name = InformerRelatedBehaviorTestReconciler.INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER) public class InformerRelatedBehaviorTestReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java index fcb2192539..7a033800af 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java @@ -16,7 +16,6 @@ @ControllerConfiguration public class DependentGarbageCollectionTestReconciler implements Reconciler, - EventSourceInitializer, ErrorStatusHandler { private KubernetesClient kubernetesClient; @@ -31,7 +30,7 @@ public DependentGarbageCollectionTestReconciler() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSourcesFromDependentResource(context, + return EventSourceUtils.nameEventSourcesFromDependentResource(context, configMapDependent); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java index 8ef1035e9b..2fa2c6213b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java @@ -7,11 +7,12 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.dependent.workflow.KubernetesResourceDeletedCondition; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(name = "ConfigMap", type = ConfigMapDependent.class), @Dependent(type = SecretDependent.class, dependsOn = "ConfigMap", deletePostcondition = KubernetesResourceDeletedCondition.class) }) +@ControllerConfiguration public class ManagedDependentDefaultDeleteConditionReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java index 5c2e9974b5..39ed7f9f0b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceConfigMap.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.sample.multipledependentresource; -import java.util.HashMap; import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; @@ -12,9 +11,9 @@ public class MultipleDependentResourceConfigMap extends CRUDKubernetesDependentResource { public static final String DATA_KEY = "key"; - private final int value; + private final String value; - public MultipleDependentResourceConfigMap(int value) { + public MultipleDependentResourceConfigMap(String value) { super(ConfigMap.class); this.value = value; } @@ -22,15 +21,17 @@ public MultipleDependentResourceConfigMap(int value) { @Override protected ConfigMap desired(MultipleDependentResourceCustomResource primary, Context context) { - Map data = new HashMap<>(); - data.put(DATA_KEY, String.valueOf(value)); return new ConfigMapBuilder() .withNewMetadata() - .withName(primary.getConfigMapName(value)) + .withName(getConfigMapName(value)) .withNamespace(primary.getMetadata().getNamespace()) .endMetadata() - .withData(data) + .withData(Map.of(DATA_KEY, primary.getSpec().getValue())) .build(); } + + public static String getConfigMapName(String id) { + return "configmap" + id; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java index 55f0d60f2d..60ba494e8a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java @@ -3,19 +3,13 @@ import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.model.annotation.Group; -import io.fabric8.kubernetes.model.annotation.Kind; import io.fabric8.kubernetes.model.annotation.ShortNames; import io.fabric8.kubernetes.model.annotation.Version; @Group("sample.javaoperatorsdk") @Version("v1") -@Kind("MultipleDependentResourceCustomResource") @ShortNames("mdr") public class MultipleDependentResourceCustomResource - extends CustomResource + extends CustomResource implements Namespaced { - - public String getConfigMapName(int id) { - return "configmap" + id; - } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java index 2dc7f6490f..4befd18d87 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java @@ -1,60 +1,37 @@ package io.javaoperatorsdk.operator.sample.multipledependentresource; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.*; -import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; @ControllerConfiguration public class MultipleDependentResourceReconciler - implements Reconciler, - TestExecutionInfoProvider, EventSourceInitializer { + implements Reconciler { - public static final int FIRST_CONFIG_MAP_ID = 1; - public static final int SECOND_CONFIG_MAP_ID = 2; - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + public static final String FIRST_CONFIG_MAP_ID = "1"; + public static final String SECOND_CONFIG_MAP_ID = "2"; private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; private final MultipleDependentResourceConfigMap secondDependentResourceConfigMap; public MultipleDependentResourceReconciler() { firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); - secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); - - firstDependentResourceConfigMap - .setResourceDiscriminator( - new ResourceIDMatcherDiscriminator<>( - p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), - p.getMetadata().getNamespace()))); - secondDependentResourceConfigMap - .setResourceDiscriminator( - new ResourceIDMatcherDiscriminator<>( - p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), - p.getMetadata().getNamespace()))); } @Override public UpdateControl reconcile( MultipleDependentResourceCustomResource resource, Context context) { - numberOfExecutions.getAndIncrement(); firstDependentResourceConfigMap.reconcile(resource, context); secondDependentResourceConfigMap.reconcile(resource, context); return UpdateControl.noUpdate(); } - - public int getNumberOfExecutions() { - return numberOfExecutions.get(); - } - @Override public Map prepareEventSources( EventSourceContext context) { @@ -64,6 +41,6 @@ public Map prepareEventSources( firstDependentResourceConfigMap.configureWith(eventSource); secondDependentResourceConfigMap.configureWith(eventSource); - return EventSourceInitializer.nameEventSources(eventSource); + return EventSourceUtils.nameEventSources(eventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceSpec.java new file mode 100644 index 0000000000..def3fa9088 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceSpec.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresource; + +public class MultipleDependentResourceSpec { + + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceConfigMap.java new file mode 100644 index 0000000000..661e8e54be --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceConfigMap.java @@ -0,0 +1,37 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; + +public class MultipleDependentResourceConfigMap + extends + CRUDKubernetesDependentResource { + + public static final String DATA_KEY = "key"; + private final int value; + + public MultipleDependentResourceConfigMap(int value) { + super(ConfigMap.class); + this.value = value; + } + + @Override + protected ConfigMap desired(MultipleDependentResourceCustomResourceWithDiscriminator primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, String.valueOf(value)); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getConfigMapName(value)) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceCustomResourceWithDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceCustomResourceWithDiscriminator.java new file mode 100644 index 0000000000..7491d5e3ae --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceCustomResourceWithDiscriminator.java @@ -0,0 +1,19 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("mdwd") +public class MultipleDependentResourceCustomResourceWithDiscriminator + extends CustomResource + implements Namespaced { + + public String getConfigMapName(int id) { + return "configmap" + id; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorReconciler.java new file mode 100644 index 0000000000..aae9d23cc7 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorReconciler.java @@ -0,0 +1,68 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@ControllerConfiguration +public class MultipleDependentResourceWithDiscriminatorReconciler + implements Reconciler, + TestExecutionInfoProvider { + + public static final int FIRST_CONFIG_MAP_ID = 1; + public static final int SECOND_CONFIG_MAP_ID = 2; + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap; + private final MultipleDependentResourceConfigMap secondDependentResourceConfigMap; + + public MultipleDependentResourceWithDiscriminatorReconciler() { + firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID); + secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID); + + firstDependentResourceConfigMap + .setResourceDiscriminator( + new ResourceIDMatcherDiscriminator<>( + p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID), + p.getMetadata().getNamespace()))); + secondDependentResourceConfigMap + .setResourceDiscriminator( + new ResourceIDMatcherDiscriminator<>( + p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID), + p.getMetadata().getNamespace()))); + } + + @Override + public UpdateControl reconcile( + MultipleDependentResourceCustomResourceWithDiscriminator resource, + Context context) { + numberOfExecutions.getAndIncrement(); + firstDependentResourceConfigMap.reconcile(resource, context); + secondDependentResourceConfigMap.reconcile(resource, context); + return UpdateControl.noUpdate(); + } + + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + InformerEventSource eventSource = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + firstDependentResourceConfigMap.configureWith(eventSource); + secondDependentResourceConfigMap.configureWith(eventSource); + + return EventSourceUtils.nameEventSources(eventSource); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorStatus.java new file mode 100644 index 0000000000..84543b8a12 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresourcewithdiscriminator/MultipleDependentResourceWithDiscriminatorStatus.java @@ -0,0 +1,5 @@ +package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator; + +public class MultipleDependentResourceWithDiscriminatorStatus { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java index 81c2308eb5..29b0db91c9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java @@ -6,17 +6,17 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(name = MultipleManagedDependentResourceMultiInformerReconciler.CONFIG_MAP_1_DR, type = MultipleManagedDependentResourceMultiInformerConfigMap1.class), @Dependent(name = MultipleManagedDependentResourceMultiInformerReconciler.CONFIG_MAP_2_DR, type = MultipleManagedDependentResourceMultiInformerConfigMap2.class) }) +@ControllerConfiguration public class MultipleManagedDependentResourceMultiInformerReconciler implements Reconciler, TestExecutionInfoProvider { - public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; public static final String DATA_KEY = "key"; public static final String CONFIG_MAP_1_DR = "ConfigMap1"; public static final String CONFIG_MAP_2_DR = "ConfigMap2"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap1.java new file mode 100644 index 0000000000..6d0bc303df --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap1.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +@KubernetesDependent +public class MultipleManagedDependentNoDiscriminatorConfigMap1 + extends + CRUDKubernetesDependentResource { + + public static final String NAME_SUFFIX = "-1"; + + public MultipleManagedDependentNoDiscriminatorConfigMap1() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY, + primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + NAME_SUFFIX) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java new file mode 100644 index 0000000000..937291d83d --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; + +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.DATA_KEY; + +@KubernetesDependent +public class MultipleManagedDependentNoDiscriminatorConfigMap2 + extends + CRUDKubernetesDependentResource { + + public static final String NAME_SUFFIX = "-2"; + + public MultipleManagedDependentNoDiscriminatorConfigMap2() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary, + Context context) { + Map data = new HashMap<>(); + data.put(DATA_KEY, primary.getSpec().getValue()); + + return new ConfigMapBuilder() + .withNewMetadata() + .withName(primary.getMetadata().getName() + NAME_SUFFIX) + .withNamespace(primary.getMetadata().getNamespace()) + .endMetadata() + .withData(data) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java new file mode 100644 index 0000000000..611d96f74e --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java @@ -0,0 +1,16 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("mnd") +public class MultipleManagedDependentNoDiscriminatorCustomResource + extends CustomResource + implements Namespaced { + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorSpec.java new file mode 100644 index 0000000000..4ccc1d84f4 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +public class MultipleManagedDependentNoDiscriminatorSpec { + + private String value; + + public String getValue() { + return value; + } + + public MultipleManagedDependentNoDiscriminatorSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java new file mode 100644 index 0000000000..0e446454da --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java @@ -0,0 +1,57 @@ +package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; + +@Workflow(dependents = { + @Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap1.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), + @Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap2.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) +}) +@ControllerConfiguration +public class MultipleManagedDependentSameTypeNoDiscriminatorReconciler + implements Reconciler, + TestExecutionInfoProvider { + + public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; + public static final String DATA_KEY = "key"; + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + public MultipleManagedDependentSameTypeNoDiscriminatorReconciler() {} + + @Override + public UpdateControl reconcile( + MultipleManagedDependentNoDiscriminatorCustomResource resource, + Context context) { + numberOfExecutions.getAndIncrement(); + + return UpdateControl.noUpdate(); + } + + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + InformerEventSource ies = + new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .build(), context); + + return Map.of(CONFIG_MAP_EVENT_SOURCE, ies); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java index 2d9b4f3ee9..8651f13fac 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java @@ -13,16 +13,16 @@ import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = MultipleManagedDependentResourceConfigMap1.class, useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), @Dependent(type = MultipleManagedDependentResourceConfigMap2.class, useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) }) +@ControllerConfiguration public class MultipleManagedDependentResourceReconciler implements Reconciler, - TestExecutionInfoProvider, - EventSourceInitializer { + TestExecutionInfoProvider { public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; public static final String DATA_KEY = "key"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java index 349409ec73..1e7577302b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -17,16 +17,16 @@ import static io.javaoperatorsdk.operator.sample.multiplemanagedexternaldependenttype.MultipleManagedExternalDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = ExternalDependentResource1.class, useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), @Dependent(type = ExternalDependentResource2.class, useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) }) +@ControllerConfiguration() public class MultipleManagedExternalDependentResourceReconciler implements Reconciler, - TestExecutionInfoProvider, - EventSourceInitializer { + TestExecutionInfoProvider { public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java index 8f4ed834aa..1c8e1a1c60 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java @@ -16,8 +16,7 @@ @ControllerConfiguration public class MultipleSecondaryEventSourceReconciler - implements Reconciler, TestExecutionInfoProvider, - EventSourceInitializer { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -76,7 +75,7 @@ public Map prepareEventSources( }).build(); InformerEventSource configMapEventSource = new InformerEventSource<>(config, context); - return EventSourceInitializer.nameEventSources(configMapEventSource); + return EventSourceUtils.nameEventSources(configMapEventSource); } ConfigMap configMap(String name, MultipleSecondaryEventSourceCustomResource resource) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipleupdateondependent/MultipleOwnerDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipleupdateondependent/MultipleOwnerDependentReconciler.java index c1f1262414..763f136c8d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipleupdateondependent/MultipleOwnerDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipleupdateondependent/MultipleOwnerDependentReconciler.java @@ -2,16 +2,14 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = MultipleOwnerDependentConfigMap.class) }) +@ControllerConfiguration() public class MultipleOwnerDependentReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentCustomResource.java new file mode 100644 index 0000000000..fba4242925 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentCustomResource.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.sample.nextreconciliationimminent; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("nri") +public class NextReconciliationImminentCustomResource + extends CustomResource + implements Namespaced { + + + +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentReconciler.java new file mode 100644 index 0000000000..be3ad70ee8 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentReconciler.java @@ -0,0 +1,58 @@ +package io.javaoperatorsdk.operator.sample.nextreconciliationimminent; + +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; + +@ControllerConfiguration(generationAwareEventProcessing = false) +public class NextReconciliationImminentReconciler + implements Reconciler { + + private static final Logger log = + LoggerFactory.getLogger(NextReconciliationImminentReconciler.class); + + private final SynchronousQueue queue = new SynchronousQueue<>(); + private volatile boolean reconciliationWaiting = false; + + @Override + public UpdateControl reconcile( + NextReconciliationImminentCustomResource resource, + Context context) throws InterruptedException { + log.info("started reconciliation"); + reconciliationWaiting = true; + // wait long enough to get manually allowed + queue.poll(120, TimeUnit.SECONDS); + log.info("Continue after wait"); + reconciliationWaiting = false; + + if (context.isNextReconciliationImminent()) { + return UpdateControl.noUpdate(); + } else { + if (resource.getStatus() == null) { + resource.setStatus(new NextReconciliationImminentStatus()); + } + resource.getStatus().setUpdateNumber(resource.getStatus().getUpdateNumber() + 1); + log.info("Patching status"); + return UpdateControl.patchStatus(resource); + } + } + + public void allowReconciliationToProceed() { + try { + queue.put(true); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public boolean isReconciliationWaiting() { + return reconciliationWaiting; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentStatus.java new file mode 100644 index 0000000000..ee4528af7a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/nextreconciliationimminent/NextReconciliationImminentStatus.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.sample.nextreconciliationimminent; + +public class NextReconciliationImminentStatus { + + private int updateNumber; + + public int getUpdateNumber() { + return updateNumber; + } + + public void setUpdateNumber(int updateNumber) { + this.updateNumber = updateNumber; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/OrderedManagedDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/OrderedManagedDependentTestReconciler.java index f7172ca44d..5f8595a131 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/OrderedManagedDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/orderedmanageddependent/OrderedManagedDependentTestReconciler.java @@ -5,20 +5,16 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Constants; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; +@Workflow(dependents = { + @Dependent(type = ConfigMapDependentResource1.class, name = "cm1"), + @Dependent(type = ConfigMapDependentResource2.class, dependsOn = "cm1") +}) @ControllerConfiguration( - namespaces = Constants.WATCH_CURRENT_NAMESPACE, - dependents = { - @Dependent(type = ConfigMapDependentResource1.class, name = "cm1"), - @Dependent(type = ConfigMapDependentResource2.class, dependsOn = "cm1") - }) + namespaces = Constants.WATCH_CURRENT_NAMESPACE) public class OrderedManagedDependentTestReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index 81d8773986..6915d89e40 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -12,8 +12,7 @@ @ControllerConfiguration public class PerResourcePollingEventSourceTestReconciler - implements Reconciler, - EventSourceInitializer { + implements Reconciler { public static final int POLL_PERIOD = 100; private final Map numberOfExecutions = new ConcurrentHashMap<>(); @@ -38,7 +37,7 @@ public Map prepareEventSources( return Set.of(UUID.randomUUID().toString()); }, context, Duration.ofMillis(POLL_PERIOD), String.class); - return EventSourceInitializer.nameEventSources(eventSource); + return EventSourceUtils.nameEventSources(eventSource); } public int getNumberOfExecutions(String name) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java index 3b8fc43bfb..0fcdc8e39d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java @@ -15,6 +15,8 @@ public class AbstractPrimaryIndexerTestReconciler implements Reconciler { + public static final String CONFIG_MAP_NAME = "common-config-map"; + private final Map numberOfExecutions = new ConcurrentHashMap<>(); protected static final String CONFIG_MAP_RELATION_INDEXER = "cm-indexer"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java index 89b2a43700..513cc2cb5d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java @@ -5,9 +5,13 @@ import java.util.stream.Collectors; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.Workflow; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -15,8 +19,9 @@ import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -@ControllerConfiguration(dependents = @Dependent( +@Workflow(dependents = @Dependent( type = DependentPrimaryIndexerTestReconciler.ReadOnlyConfigMapDependent.class)) +@ControllerConfiguration public class DependentPrimaryIndexerTestReconciler extends AbstractPrimaryIndexerTestReconciler implements Reconciler { @@ -30,6 +35,17 @@ public ReadOnlyConfigMapDependent() { super(ConfigMap.class); } + @Override + protected ConfigMap desired(PrimaryIndexerTestCustomResource primary, + Context context) { + return new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(CONFIG_MAP_NAME) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + } + @Override public Set toPrimaryResourceIDs(ConfigMap resource) { return cache.byIndex(CONFIG_MAP_RELATION_INDEXER, resource.getMetadata().getName()) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/PrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/PrimaryIndexerTestReconciler.java index 6890a3c8c4..bc05a171cf 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/PrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/PrimaryIndexerTestReconciler.java @@ -7,15 +7,14 @@ import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceUtils; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; @ControllerConfiguration public class PrimaryIndexerTestReconciler - extends AbstractPrimaryIndexerTestReconciler implements - EventSourceInitializer { + extends AbstractPrimaryIndexerTestReconciler { @Override public Map prepareEventSources( @@ -36,7 +35,7 @@ public Map prepareEventSources( .collect(Collectors.toSet())) .build(); - return EventSourceInitializer + return EventSourceUtils .nameEventSources(new InformerEventSource<>(informerConfiguration, context)); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondary/JobReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondary/JobReconciler.java index ace158360a..f2f975f248 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondary/JobReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondary/JobReconciler.java @@ -20,7 +20,7 @@ */ @ControllerConfiguration() public class JobReconciler - implements Reconciler, EventSourceInitializer, ErrorStatusHandler { + implements Reconciler, ErrorStatusHandler { private static final String JOB_CLUSTER_INDEX = "job-cluster-index"; @@ -79,7 +79,7 @@ public Map prepareEventSources(EventSourceContext cont primary.getSpec().getClusterName(), primary.getMetadata().getNamespace()))); } - return EventSourceInitializer + return EventSourceUtils .nameEventSources(new InformerEventSource<>(informerConfiguration.build(), context)); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java index d08bc2131f..364e9088f7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java @@ -1,12 +1,28 @@ package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; public class ConfigMapDependent extends KubernetesDependentResource { + public static final String TEST_CONFIG_MAP_NAME = "testconfigmap"; + public ConfigMapDependent() { super(ConfigMap.class); } + + @Override + protected ConfigMap desired(PrimaryToSecondaryDependentCustomResource primary, + Context context) { + return new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(TEST_CONFIG_MAP_NAME) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java index c51111b206..3283c76a93 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java @@ -24,14 +24,14 @@ * Note that this is usually just used with read only resources. So it has limited usage, one reason * to use it is to have nice condition on that resource within a workflow. */ -@ControllerConfiguration(dependents = {@Dependent(type = ConfigMapDependent.class, +@Workflow(dependents = {@Dependent(type = ConfigMapDependent.class, name = CONFIG_MAP, reconcilePrecondition = ConfigMapReconcilePrecondition.class, useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), @Dependent(type = SecretDependent.class, dependsOn = CONFIG_MAP)}) +@ControllerConfiguration() public class PrimaryToSecondaryDependentReconciler - implements Reconciler, TestExecutionInfoProvider, - EventSourceInitializer { + implements Reconciler, TestExecutionInfoProvider { public static final String DATA_KEY = "data"; public static final String CONFIG_MAP = "ConfigMap"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/restart/RestartTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/restart/RestartTestReconciler.java index decd9b597b..e7daf5b2eb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/restart/RestartTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/restart/RestartTestReconciler.java @@ -2,15 +2,12 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration( - dependents = @Dependent(type = ConfigMapDependentResource.class)) +@Workflow(dependents = @Dependent(type = ConfigMapDependentResource.class)) +@ControllerConfiguration public class RestartTestReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java index 64e81e7c31..0746de2897 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java @@ -2,13 +2,11 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = {@Dependent(type = ServiceDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = ServiceDependentResource.class)}) +@ControllerConfiguration public class ServiceStrictMatcherTestReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/specialresourcesdependent/SpecialResourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/specialresourcesdependent/SpecialResourceTestReconciler.java index 5fa7d778b3..b36b6f31a2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/specialresourcesdependent/SpecialResourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/specialresourcesdependent/SpecialResourceTestReconciler.java @@ -6,11 +6,10 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration( - namespaces = Constants.WATCH_CURRENT_NAMESPACE, - dependents = { - @Dependent(type = ServiceAccountDependentResource.class), - }) +@Workflow(dependents = { + @Dependent(type = ServiceAccountDependentResource.class), +}) +@ControllerConfiguration(namespaces = Constants.WATCH_CURRENT_NAMESPACE) public class SpecialResourceTestReconciler implements Reconciler, TestExecutionInfoProvider { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java index a513133670..e0cdf50c96 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/SSALegacyMatcherReconciler.java @@ -2,13 +2,11 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = {@Dependent(type = ServiceDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = ServiceDependentResource.class)}) +@ControllerConfiguration public class SSALegacyMatcherReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java index a1f5f6faf0..e0699093de 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/ssalegacymatcher/ServiceDependentResource.java @@ -39,8 +39,10 @@ protected Service desired(SSALegacyMatcherCustomResource primary, @Override public Result match(Service actualResource, SSALegacyMatcherCustomResource primary, Context context) { + var desired = desired(primary, context); + return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, - true, false, false); + false, false); } // override just to check the exec count diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java index 77fcf0b85d..97c2435e3f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/standalonedependent/StandaloneDependentTestReconciler.java @@ -7,14 +7,20 @@ import io.fabric8.kubernetes.client.KubernetesClientException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.StandaloneDependentResourceIT; -import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler; +import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceUtils; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.event.source.EventSource; @ControllerConfiguration public class StandaloneDependentTestReconciler implements Reconciler, - EventSourceInitializer, ErrorStatusHandler { private volatile boolean errorOccurred = false; @@ -27,7 +33,7 @@ public StandaloneDependentTestReconciler() { @Override public Map prepareEventSources( EventSourceContext context) { - return EventSourceInitializer.nameEventSourcesFromDependentResource(context, + return EventSourceUtils.nameEventSourcesFromDependentResource(context, deploymentDependent); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java index c884619227..3b30acfc5c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java @@ -1,13 +1,10 @@ package io.javaoperatorsdk.operator.sample.statefulsetdesiredsanitizer; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration( - dependents = {@Dependent(type = StatefulSetDesiredSanitizerDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = StatefulSetDesiredSanitizerDependentResource.class)}) +@ControllerConfiguration public class StatefulSetDesiredSanitizerReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statuspatchnonlocking/StatusPatchLockingCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statuspatchnonlocking/StatusPatchLockingCustomResource.java index 4d77259999..93ec97884d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statuspatchnonlocking/StatusPatchLockingCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/statuspatchnonlocking/StatusPatchLockingCustomResource.java @@ -3,14 +3,12 @@ import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.model.annotation.Group; -import io.fabric8.kubernetes.model.annotation.Kind; import io.fabric8.kubernetes.model.annotation.ShortNames; import io.fabric8.kubernetes.model.annotation.Version; @Group("sample.javaoperatorsdk") @Version("v1") -@Kind("StatusUpdateLockingCustomResource") -@ShortNames("sul") +@ShortNames("spl") public class StatusPatchLockingCustomResource extends CustomResource diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java index fd63a2cb12..9cc4a3e9d6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java @@ -2,13 +2,11 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = {@Dependent(type = UnmodifiablePartConfigMapDependent.class)}) +@Workflow(dependents = {@Dependent(type = UnmodifiablePartConfigMapDependent.class)}) +@ControllerConfiguration public class UnmodifiableDependentPartReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java index a103e53162..d373fe7d26 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java @@ -21,7 +21,7 @@ public UnmodifiablePartConfigMapDependent() { @Override protected ConfigMap desired(UnmodifiableDependentPartCustomResource primary, Context context) { - var actual = getSecondaryResource(primary, context); + var actual = context.getSecondaryResource(ConfigMap.class); ConfigMap res = new ConfigMapBuilder() .withMetadata(new ObjectMetaBuilder() .withName(primary.getMetadata().getName()) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java index 3f2fba15c5..6a30f3d9f4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java @@ -3,10 +3,11 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = ConfigMapDependentResource.class, activationCondition = TestActivcationCondition.class), }) +@ControllerConfiguration public class WorkflowActivationCleanupReconciler implements Reconciler, Cleaner { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcondition/WorkflowActivationConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcondition/WorkflowActivationConditionReconciler.java index 33db3043ba..8669c24cb7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcondition/WorkflowActivationConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowactivationcondition/WorkflowActivationConditionReconciler.java @@ -3,11 +3,12 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = ConfigMapDependentResource.class), @Dependent(type = RouteDependentResource.class, activationCondition = IsOpenShiftCondition.class) }) +@ControllerConfiguration public class WorkflowActivationConditionReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowallfeature/WorkflowAllFeatureReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowallfeature/WorkflowAllFeatureReconciler.java index 2c25d13924..f5c14d6f96 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowallfeature/WorkflowAllFeatureReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowallfeature/WorkflowAllFeatureReconciler.java @@ -7,7 +7,7 @@ import static io.javaoperatorsdk.operator.sample.workflowallfeature.WorkflowAllFeatureReconciler.DEPLOYMENT_NAME; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(name = DEPLOYMENT_NAME, type = DeploymentDependentResource.class, readyPostcondition = DeploymentReadyCondition.class), @Dependent(type = ConfigMapDependentResource.class, @@ -15,6 +15,7 @@ deletePostcondition = ConfigMapDeletePostCondition.class, dependsOn = DEPLOYMENT_NAME) }) +@ControllerConfiguration public class WorkflowAllFeatureReconciler implements Reconciler, Cleaner { @@ -34,8 +35,8 @@ public UpdateControl reconcile( } resource.getStatus() .setReady( - context.managedDependentResourceContext() - .getWorkflowReconcileResult().orElseThrow() + context.managedWorkflowAndDependentResourceContext() + .getWorkflowReconcileResult() .allDependentResourcesReady()); return UpdateControl.patchStatus(resource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/ConfigMapDependent.java new file mode 100644 index 0000000000..91bf73906f --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/ConfigMapDependent.java @@ -0,0 +1,29 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitcleanup; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; + +public class ConfigMapDependent extends + CRUDNoGCKubernetesDependentResource { + + public ConfigMapDependent() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(WorkflowExplicitCleanupCustomResource primary, + Context context) { + return new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withData(Map.of("key", "val")) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java similarity index 65% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java index dec7b6c40a..b2057a54dd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/customfilter/CustomFilteringTestResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.sample.customfilter; +package io.javaoperatorsdk.operator.sample.workflowexplicitcleanup; import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; @@ -8,8 +8,8 @@ @Group("sample.javaoperatorsdk") @Version("v1") -@ShortNames("cft") -public class CustomFilteringTestResource - extends CustomResource +@ShortNames("wec") +public class WorkflowExplicitCleanupCustomResource + extends CustomResource implements Namespaced { } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java new file mode 100644 index 0000000000..128bb9629c --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java @@ -0,0 +1,32 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitcleanup; + +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; + +@Workflow(explicitInvocation = true, + dependents = @Dependent(type = ConfigMapDependent.class)) +@ControllerConfiguration +public class WorkflowExplicitCleanupReconciler + implements Reconciler, + Cleaner { + + @Override + public UpdateControl reconcile( + WorkflowExplicitCleanupCustomResource resource, + Context context) { + + context.managedWorkflowAndDependentResourceContext().reconcileManagedWorkflow(); + + return UpdateControl.noUpdate(); + } + + @Override + public DeleteControl cleanup(WorkflowExplicitCleanupCustomResource resource, + Context context) { + + context.managedWorkflowAndDependentResourceContext().cleanupManageWorkflow(); + // this can be checked + // context.managedWorkflowAndDependentResourceContext().getWorkflowCleanupResult() + return DeleteControl.defaultDelete(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupSpec.java new file mode 100644 index 0000000000..d8da8797f5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitcleanup/WorkflowExplicitCleanupSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitcleanup; + +public class WorkflowExplicitCleanupSpec { + + private String value; + + public String getValue() { + return value; + } + + public WorkflowExplicitCleanupSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/ConfigMapDependent.java new file mode 100644 index 0000000000..e26fcfcf11 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/ConfigMapDependent.java @@ -0,0 +1,29 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitinvocation; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; + +public class ConfigMapDependent extends + CRUDNoGCKubernetesDependentResource { + + public ConfigMapDependent() { + super(ConfigMap.class); + } + + @Override + protected ConfigMap desired(WorkflowExplicitInvocationCustomResource primary, + Context context) { + return new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withData(Map.of("key", primary.getSpec().getValue())) + .build(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java new file mode 100644 index 0000000000..827a17ddaf --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitinvocation; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("wei") +public class WorkflowExplicitInvocationCustomResource + extends CustomResource + implements Namespaced { +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java new file mode 100644 index 0000000000..dc7bce4296 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java @@ -0,0 +1,39 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitinvocation; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; + +@Workflow(explicitInvocation = true, + dependents = @Dependent(type = ConfigMapDependent.class)) +@ControllerConfiguration +public class WorkflowExplicitInvocationReconciler + implements Reconciler { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + private volatile boolean invokeWorkflow = false; + + @Override + public UpdateControl reconcile( + WorkflowExplicitInvocationCustomResource resource, + Context context) { + + numberOfExecutions.addAndGet(1); + if (invokeWorkflow) { + context.managedWorkflowAndDependentResourceContext().reconcileManagedWorkflow(); + } + + + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } + + public void setInvokeWorkflow(boolean invokeWorkflow) { + this.invokeWorkflow = invokeWorkflow; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationSpec.java new file mode 100644 index 0000000000..2112d348e2 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowexplicitinvocation/WorkflowExplicitInvocationSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.sample.workflowexplicitinvocation; + +public class WorkflowExplicitInvocationSpec { + + private String value; + + public String getValue() { + return value; + } + + public WorkflowExplicitInvocationSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java index 8277e7f8e7..aeb4403f7c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java @@ -2,17 +2,15 @@ import java.util.concurrent.atomic.AtomicInteger; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@ControllerConfiguration(dependents = { +@Workflow(dependents = { @Dependent(type = ConfigMapDependentResource.class, activationCondition = ActivationCondition.class), @Dependent(type = SecretDependentResource.class) }) +@ControllerConfiguration public class WorkflowMultipleActivationReconciler implements Reconciler { diff --git a/pom.xml b/pom.xml index c23fb0b3c6..7f2f91804e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk java-operator-sdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT Operator SDK for Java Java SDK for implementing Kubernetes operators pom @@ -29,24 +29,24 @@ - scm:git:git://github.com/java-operator-sdk/java-operator-sdk.git - scm:git:git@github.com/java-operator-sdk/java-operator-sdk.git + scm:git:git://github.com/operator-framework/java-operator-sdk.git + scm:git:git@github.com/operator-framework/java-operator-sdk.git https://github.com/operator-framework/java-operator-sdk/tree/main UTF-8 - 11 + 17 ${java.version} ${java.version} java-operator-sdk https://sonarcloud.io - okhttp + jdk 5.10.1 6.10.0 - 1.7.36 - 2.23.0 + 2.0.12 + 2.23.1 5.11.0 3.14.0 0.21.0 @@ -77,6 +77,7 @@ 2.23.0 1.0 1.9.0 + 3.4.1 @@ -166,7 +167,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl ${log4j.version} diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml index 648417c8fb..bd9b593ec7 100644 --- a/sample-operators/leader-election/pom.xml +++ b/sample-operators/leader-election/pom.xml @@ -7,7 +7,7 @@ io.javaoperatorsdk sample-operators - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT sample-leader-election @@ -15,12 +15,6 @@ An E2E test for leader election jar - - 11 - 11 - 3.4.1 - - @@ -40,7 +34,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl org.takes @@ -71,7 +65,7 @@ ${jib-maven-plugin.version} - gcr.io/distroless/java:11 + gcr.io/distroless/java17-debian11 leader-election-operator diff --git a/sample-operators/mysql-schema/pom.xml b/sample-operators/mysql-schema/pom.xml index a9b43df1e1..2a766e3e7c 100644 --- a/sample-operators/mysql-schema/pom.xml +++ b/sample-operators/mysql-schema/pom.xml @@ -7,7 +7,7 @@ io.javaoperatorsdk sample-operators - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT sample-mysql-schema-operator @@ -15,12 +15,6 @@ Provisions Schemas in a MySQL database jar - - 11 - 11 - 3.4.1 - - @@ -59,7 +53,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl org.junit.jupiter @@ -98,7 +92,7 @@ ${jib-maven-plugin.version} - gcr.io/distroless/java:11 + gcr.io/distroless/java17-debian11 mysql-schema-operator diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java index 95db43b228..1a4b704591 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java @@ -4,12 +4,7 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.Secret; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler; -import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.sample.dependent.SchemaDependentResource; import io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource; @@ -19,12 +14,12 @@ import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_USERNAME; import static java.lang.String.format; -@ControllerConfiguration( - dependents = { - @Dependent(type = SecretDependentResource.class, name = SecretDependentResource.NAME), - @Dependent(type = SchemaDependentResource.class, name = SchemaDependentResource.NAME, - dependsOn = SecretDependentResource.NAME) - }) +@Workflow(dependents = { + @Dependent(type = SecretDependentResource.class, name = SecretDependentResource.NAME), + @Dependent(type = SchemaDependentResource.class, name = SchemaDependentResource.NAME, + dependsOn = SecretDependentResource.NAME) +}) +@ControllerConfiguration public class MySQLSchemaReconciler implements Reconciler, ErrorStatusHandler { diff --git a/sample-operators/pom.xml b/sample-operators/pom.xml index e383d1e038..07895b8b94 100644 --- a/sample-operators/pom.xml +++ b/sample-operators/pom.xml @@ -7,17 +7,13 @@ io.javaoperatorsdk java-operator-sdk - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT sample-operators Operator SDK - Samples pom - - 3.1.4 - - tomcat-operator webpage diff --git a/sample-operators/tomcat-operator/pom.xml b/sample-operators/tomcat-operator/pom.xml index 2311d22d57..eadcbca885 100644 --- a/sample-operators/tomcat-operator/pom.xml +++ b/sample-operators/tomcat-operator/pom.xml @@ -7,7 +7,7 @@ io.javaoperatorsdk sample-operators - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT sample-tomcat-operator @@ -15,12 +15,6 @@ Provisions Tomcat Pods and deploys Webapplications in them jar - - 11 - 11 - 3.4.1 - - @@ -55,7 +49,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl org.takes @@ -100,7 +94,7 @@ ${jib-maven-plugin.version} - gcr.io/distroless/java:11 + gcr.io/distroless/java17-debian11 tomcat-operator diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java index de4a63431b..796da31d5d 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java @@ -7,21 +7,18 @@ import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentStatus; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; /** * Runs a specified number of Tomcat app server Pods. It uses a Deployment to create the Pods. Also * creates a Service over which the Pods can be accessed. */ -@ControllerConfiguration( - dependents = { - @Dependent(type = DeploymentDependentResource.class), - @Dependent(type = ServiceDependentResource.class) - }) +@Workflow(dependents = { + @Dependent(type = DeploymentDependentResource.class), + @Dependent(type = ServiceDependentResource.class) +}) +@ControllerConfiguration public class TomcatReconciler implements Reconciler { private final Logger log = LoggerFactory.getLogger(getClass()); diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java index 5b87d23aac..0329350fdb 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java @@ -20,14 +20,7 @@ import io.fabric8.kubernetes.client.dsl.ExecListener; import io.fabric8.kubernetes.client.dsl.ExecWatch; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.Cleaner; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper; @@ -35,7 +28,7 @@ @ControllerConfiguration public class WebappReconciler - implements Reconciler, Cleaner, EventSourceInitializer { + implements Reconciler, Cleaner { private static final Logger log = LoggerFactory.getLogger(WebappReconciler.class); @@ -66,7 +59,7 @@ public Map prepareEventSources(EventSourceContext c (Webapp primary) -> Set.of(new ResourceID(primary.getSpec().getTomcat(), primary.getMetadata().getNamespace()))) .build(); - return EventSourceInitializer + return EventSourceUtils .nameEventSources(new InformerEventSource<>(configuration, context)); } diff --git a/sample-operators/webpage/pom.xml b/sample-operators/webpage/pom.xml index 4ba8eb8991..8578d57c32 100644 --- a/sample-operators/webpage/pom.xml +++ b/sample-operators/webpage/pom.xml @@ -7,7 +7,7 @@ io.javaoperatorsdk sample-operators - 4.8.1-SNAPSHOT + 5.0.0-SNAPSHOT sample-webpage-operator @@ -15,12 +15,6 @@ Provisions an nginx Webserver based on a CRD with give html jar - - 11 - 11 - 3.4.1 - - @@ -40,7 +34,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl org.takes @@ -71,7 +65,7 @@ ${jib-maven-plugin.version} - gcr.io/distroless/java:11 + gcr.io/distroless/java17-debian11 webpage-operator diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 8494af5402..3d3b583659 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -26,7 +26,7 @@ labelSelector = WebPageDependentsWorkflowReconciler.DEPENDENT_RESOURCE_LABEL_SELECTOR) @SuppressWarnings("unused") public class WebPageDependentsWorkflowReconciler - implements Reconciler, ErrorStatusHandler, EventSourceInitializer { + implements Reconciler, ErrorStatusHandler { public static final String DEPENDENT_RESOURCE_LABEL_SELECTOR = "!low-level"; @@ -49,7 +49,7 @@ public WebPageDependentsWorkflowReconciler(KubernetesClient kubernetesClient) { @Override public Map prepareEventSources(EventSourceContext context) { - return EventSourceInitializer.nameEventSourcesFromDependentResource(context, configMapDR, + return EventSourceUtils.nameEventSourcesFromDependentResource(context, configMapDR, deploymentDR, serviceDR, ingressDR); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java index d370cd3315..44149aed4d 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java @@ -13,14 +13,14 @@ /** * Shows how to implement a reconciler with managed dependent resources. */ -@ControllerConfiguration( - dependents = { - @Dependent(type = ConfigMapDependentResource.class), - @Dependent(type = DeploymentDependentResource.class), - @Dependent(type = ServiceDependentResource.class), - @Dependent(type = IngressDependentResource.class, - reconcilePrecondition = ExposedIngressCondition.class) - }) +@Workflow(dependents = { + @Dependent(type = ConfigMapDependentResource.class), + @Dependent(type = DeploymentDependentResource.class), + @Dependent(type = ServiceDependentResource.class), + @Dependent(type = IngressDependentResource.class, + reconcilePrecondition = ExposedIngressCondition.class) +}) +@ControllerConfiguration public class WebPageManagedDependentsReconciler implements Reconciler, ErrorStatusHandler, Cleaner { diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java index 4be2da11c7..df6ba7ddb8 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java @@ -8,46 +8,28 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.*; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.Replaceable; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; -import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler; -import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.api.reconciler.EventSourceInitializer; -import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.processing.event.rate.RateLimited; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.sample.customresource.WebPage; -import static io.javaoperatorsdk.operator.sample.Utils.configMapName; -import static io.javaoperatorsdk.operator.sample.Utils.createStatus; -import static io.javaoperatorsdk.operator.sample.Utils.deploymentName; -import static io.javaoperatorsdk.operator.sample.Utils.handleError; -import static io.javaoperatorsdk.operator.sample.Utils.isValidHtml; -import static io.javaoperatorsdk.operator.sample.Utils.makeDesiredIngress; -import static io.javaoperatorsdk.operator.sample.Utils.serviceName; -import static io.javaoperatorsdk.operator.sample.Utils.setInvalidHtmlErrorMessage; -import static io.javaoperatorsdk.operator.sample.Utils.simulateErrorIfRequested; +import static io.javaoperatorsdk.operator.sample.Utils.*; import static io.javaoperatorsdk.operator.sample.WebPageManagedDependentsReconciler.SELECTOR; /** Shows how to implement reconciler using the low level api directly. */ @RateLimited(maxReconciliations = 2, within = 3) @ControllerConfiguration public class WebPageReconciler - implements Reconciler, ErrorStatusHandler, EventSourceInitializer { + implements Reconciler, ErrorStatusHandler { public static final String INDEX_HTML = "index.html"; @@ -77,7 +59,7 @@ public Map prepareEventSources(EventSourceContext new InformerEventSource<>(InformerConfiguration.from(Ingress.class, context) .withLabelSelector(SELECTOR) .build(), context); - return EventSourceInitializer.nameEventSources(configMapEventSource, deploymentEventSource, + return EventSourceUtils.nameEventSources(configMapEventSource, deploymentEventSource, serviceEventSource, ingressEventSource); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java index 1799d72cea..4e4fa22b6b 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java @@ -3,53 +3,70 @@ import java.util.Arrays; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import io.fabric8.kubernetes.api.model.ConfigMap; -import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler; +import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceUtils; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfigBuilder; import io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow; import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowBuilder; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.sample.customresource.WebPage; -import io.javaoperatorsdk.operator.sample.dependentresource.*; +import io.javaoperatorsdk.operator.sample.dependentresource.ConfigMapDependentResource; +import io.javaoperatorsdk.operator.sample.dependentresource.DeploymentDependentResource; +import io.javaoperatorsdk.operator.sample.dependentresource.ExposedIngressCondition; +import io.javaoperatorsdk.operator.sample.dependentresource.IngressDependentResource; +import io.javaoperatorsdk.operator.sample.dependentresource.ServiceDependentResource; import static io.javaoperatorsdk.operator.sample.Utils.*; import static io.javaoperatorsdk.operator.sample.WebPageManagedDependentsReconciler.SELECTOR; /** - * Shows how to implement reconciler using standalone dependent resources. + * Shows how to implement reconciler using standalone dependent resources and workflows. */ @ControllerConfiguration public class WebPageStandaloneDependentsReconciler - implements Reconciler, ErrorStatusHandler, EventSourceInitializer { - - private static final Logger log = - LoggerFactory.getLogger(WebPageStandaloneDependentsReconciler.class); + implements Reconciler, ErrorStatusHandler { - private Workflow workflow; + private final Workflow workflow; public WebPageStandaloneDependentsReconciler() { + // initialize the workflow workflow = createDependentResourcesAndWorkflow(); } @Override public Map prepareEventSources(EventSourceContext context) { - return EventSourceInitializer.eventSourcesFromWorkflow(context, workflow); + // initializes the dependents' event sources from the given context + return EventSourceUtils.eventSourcesFromWorkflow(context, workflow); } @Override public UpdateControl reconcile(WebPage webPage, Context context) throws Exception { + // for testing purposes simulateErrorIfRequested(webPage); + // validate the html page and update the status with an error message if it isn't valid if (!isValidHtml(webPage)) { return UpdateControl.patchStatus(setInvalidHtmlErrorMessage(webPage)); } + // Explicitly reconcile the dependent resources. + // Calling the workflow reconciliation explicitly allows control over the workflow customization + // but also *when* dependents are reconciled (as opposed to before the main reconciler's + // reconcile method in the managed case). + // With the default configuration, this will throw an exception if one of the dependents + // couldn't be properly reconciled workflow.reconcile(webPage, context); + // retrieve the name of the ConfigMap secondary resource to update the status if everything went + // well webPage.setStatus( createStatus( context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); @@ -62,25 +79,37 @@ public ErrorStatusUpdateControl updateErrorStatus( return handleError(resource, e); } + /** + * Initializes the dependent resources and connect them in the context of a {@link Workflow} + * + * @return the {@link Workflow} that will reconcile automatically secondary resources + */ @SuppressWarnings({"unchecked", "rawtypes"}) private Workflow createDependentResourcesAndWorkflow() { + // create the dependent resources var configMapDR = new ConfigMapDependentResource(); var deploymentDR = new DeploymentDependentResource(); var serviceDR = new ServiceDependentResource(); var ingressDR = new IngressDependentResource(); + // configure them with our label selector Arrays.asList(configMapDR, deploymentDR, serviceDR, ingressDR) .forEach(dr -> dr.configureWith(new KubernetesDependentResourceConfigBuilder() .withLabelSelector(SELECTOR + "=true").build())); + // connect the dependent resources into a workflow, configuring them as we go + // Note the method call order is significant and configuration applies to the dependent being + // configured as defined by the method call order (in this example, the reconcile pre-condition + // that is added applies to the Ingress dependent) return new WorkflowBuilder() .addDependentResource(configMapDR) .addDependentResource(deploymentDR) .addDependentResource(serviceDR) .addDependentResource(ingressDR) + // prevent the Ingress from being created based on the linked condition (here: only if the + // `exposed` flag is set in the primary resource), delete the Ingress if it already exists + // and the condition becomes false .withReconcilePrecondition(new ExposedIngressCondition()) .build(); } - - }