diff --git a/.github/workflows/hugo.yaml b/.github/workflows/hugo.yaml index 2c0a63d50d..50a2d83182 100644 --- a/.github/workflows/hugo.yaml +++ b/.github/workflows/hugo.yaml @@ -45,6 +45,16 @@ jobs: with: submodules: recursive fetch-depth: 0 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build test-index-processor and generate test documentation + run: | + ./mvnw clean install -DskipTests -pl test-index-processor + ./mvnw process-test-classes -DskipTests -pl operator-framework - name: Setup Pages id: pages uses: actions/configure-pages@v5 diff --git a/operator-framework/pom.xml b/operator-framework/pom.xml index 1cf2a0aa9e..6dc9549be0 100644 --- a/operator-framework/pom.xml +++ b/operator-framework/pom.xml @@ -84,6 +84,13 @@ kube-api-test-client-inject test + + io.javaoperatorsdk + test-index-processor + ${project.version} + test + true + @@ -106,6 +113,23 @@ + + + default-testCompile + + testCompile + + test-compile + + + + io.javaoperatorsdk + test-index-processor + ${project.version} + + + + @@ -138,6 +162,50 @@ org.apache.maven.plugins maven-surefire-plugin + + org.apache.maven.plugins + maven-resources-plugin + + + copy-samples-to-docs + + copy-resources + + process-test-classes + + ${project.basedir}/../docs/content/en/docs/testindex + + + ${project.build.directory}/generated-test-sources/test-annotations + + samples.md + + false + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + rename-samples-to-index + + run + + process-test-classes + + + + + + + + diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java index 9153ae4ff5..62f5508731 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java @@ -12,6 +12,7 @@ import io.fabric8.kubernetes.model.annotation.Group; import io.fabric8.kubernetes.model.annotation.Kind; import io.fabric8.kubernetes.model.annotation.Version; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; @@ -21,6 +22,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Custom CRD Mapping in Test Extension", + description = + """ + Demonstrates how to manually specify and apply Custom Resource Definitions (CRDs) in \ + integration tests using the LocallyRunOperatorExtension. This test verifies that CRDs \ + can be loaded from specified file paths and properly registered with the Kubernetes API \ + server during test execution. + """) public class CRDMappingInTestExtensionIT { private final KubernetesClient client = new KubernetesClientBuilder().build(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java index 80fb09095d..3a336bfb6b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.baseapi.simple.TestCustomResource; import io.javaoperatorsdk.operator.baseapi.simple.TestReconciler; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -18,6 +19,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Concurrent Reconciliation of Multiple Resources", + description = + """ + Demonstrates the operator's ability to handle concurrent reconciliation of multiple \ + resources. The test creates, updates, and deletes many resources simultaneously to \ + verify proper handling of concurrent operations, ensuring thread safety and correct \ + resource state management under load. + """) class ConcurrencyIT { public static final int NUMBER_OF_RESOURCES_CREATED = 50; public static final int NUMBER_OF_RESOURCES_DELETED = 30; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java index 18a107d9b2..f018ba44e7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java @@ -9,12 +9,22 @@ import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; 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; +@Sample( + tldr = "Operator Startup with Informer Errors", + description = + """ + Demonstrates that the operator can start successfully even when informers encounter \ + errors during startup, such as insufficient access rights. By setting \ + stopOnInformerErrorDuringStartup to false, the operator gracefully handles permission \ + errors and continues initialization, allowing it to operate with partial access. + """) class InformerErrorHandlerStartIT { /** Test showcases that the operator starts even if there is no access right for some resource. */ @Test diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java index 51b1f4b3d5..4ff7e9c295 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java @@ -8,6 +8,7 @@ import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; @@ -21,6 +22,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +@Sample( + tldr = "Leader Election with Insufficient Permissions", + description = + """ + Verifies that the operator fails gracefully when leader election is configured but \ + the service account lacks permissions to access lease resources. This test ensures \ + proper error handling and messaging when RBAC permissions are insufficient for \ + leader election functionality. + """) class LeaderElectionPermissionIT { KubernetesClient adminClient = new KubernetesClientBuilder().build(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java index 9d914d080c..94ee5325e3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.Service; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.dependent.standalonedependent.StandaloneDependentResourceIT; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -15,6 +16,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Cleanup handler for built-in Kubernetes resources", + description = + """ + Demonstrates how to implement cleanup handlers (finalizers) for built-in Kubernetes \ + resources like Service and Pod. These resources don't use generation the same way \ + as custom resources, so this sample shows the proper approach to handle their \ + lifecycle and cleanup logic. + """) class BuiltInResourceCleanerIT { private static final Logger log = LoggerFactory.getLogger(BuiltInResourceCleanerIT.class); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java index 67f65c64ca..478f351d94 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java @@ -14,6 +14,7 @@ import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.RegisteredController; import io.javaoperatorsdk.operator.api.reconciler.Constants; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -21,6 +22,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Dynamically Changing Watched Namespaces", + description = + """ + Demonstrates how to dynamically change the set of namespaces that an operator watches at \ + runtime. This feature allows operators to add or remove namespaces from their watch \ + list, including switching between specific namespaces and watching all namespaces. \ + The test verifies that resources in newly added namespaces are reconciled and \ + resources in removed namespaces are no longer watched. + """) class ChangeNamespaceIT { public static final String TEST_RESOURCE_NAME_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java index 6ccb34f0e3..03fa506d07 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java @@ -4,11 +4,23 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Implementing Cleanup Logic with Cleaner Interface", + description = + """ + Demonstrates how to implement cleanup logic for custom resources using the Cleaner \ + interface. When a reconciler implements Cleaner, the framework automatically adds a \ + finalizer to resources and calls the cleanup method when the resource is deleted. \ + This pattern is useful for cleaning up external resources or performing custom \ + deletion logic. The test verifies finalizer handling, cleanup execution, and the \ + ability to reschedule cleanup operations. + """) class CleanerForReconcilerIT { public static final String TEST_RESOURCE_NAME = "cleaner-for-reconciler-test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java index 19ef6df9a0..0993f76c8f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java @@ -6,12 +6,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.cleanupconflict.CleanupConflictReconciler.WAIT_TIME; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Cleanup Finalizer Removal Without Conflicts", + description = + """ + Tests that finalizers are removed correctly during cleanup without causing conflicts, \ + even when multiple finalizers are present and removed concurrently. This verifies the \ + operator's ability to handle finalizer updates safely during resource deletion. + """) class CleanupConflictIT { private static final String ADDITIONAL_FINALIZER = "javaoperatorsdk.io/additionalfinalizer"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java index 4c92fbada7..2a690035da 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java @@ -7,12 +7,22 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Cluster-scoped resource reconciliation", + description = + """ + Demonstrates how to reconcile cluster-scoped custom resources (non-namespaced). This \ + test shows CRUD operations on cluster-scoped resources and verifies that \ + dependent resources are created, updated, and properly cleaned up when the \ + primary resource is deleted. + """) class ClusterScopedResourceIT { public static final String TEST_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java index da166ce2c1..d83c1b1b79 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java @@ -6,12 +6,22 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Concurrent Finalizer Removal by Multiple Reconcilers", + description = + """ + Demonstrates safe concurrent finalizer removal when multiple reconcilers manage the \ + same resource with different finalizers. Tests that finalizers can be removed \ + concurrently without conflicts or race conditions, ensuring proper cleanup even when \ + multiple controllers are involved. + """) class ConcurrentFinalizerRemovalIT { private static final Logger log = LoggerFactory.getLogger(ConcurrentFinalizerRemovalIT.class); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java index 2d9a7db573..38cdb8726b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java @@ -7,12 +7,22 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.createupdateeventfilter.CreateUpdateEventFilterTestReconciler.CONFIG_MAP_TEST_DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Event filtering for create and update operations", + description = + """ + Shows how to configure event filters on informer event sources to control which create and \ + update events trigger reconciliation. This is useful for preventing unnecessary \ + reconciliation loops when dependent resources are modified by the controller \ + itself. + """) class CreateUpdateInformerEventSourceEventFilterIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java index b5554493ee..90ba1d9cb1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java @@ -3,8 +3,17 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Event Filtering with Previous Annotation Disabled", + description = + """ + Tests event filtering behavior when the previous annotation feature for dependent \ + resources is disabled. Verifies that update events are properly received and handled \ + even without the annotation tracking mechanism that compares previous resource states. + """) class PreviousAnnotationDisabledIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java index 5fe9bfa939..fc6ee36387 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java @@ -17,12 +17,23 @@ import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentSpec; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.deployment.DeploymentReconciler.STATUS_MESSAGE; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Reconciling Non-Custom Kubernetes Resources with Status Updates", + description = + """ + Demonstrates how to reconcile standard Kubernetes resources (like Deployments) instead of \ + custom resources, and how to update their status subresource. This pattern is useful when \ + building operators that manage native Kubernetes resources rather than custom resource \ + definitions. The test verifies that the operator can watch, reconcile, and update the \ + status of a Deployment resource. + """) class KubernetesResourceStatusUpdateIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java index 5c05850b4a..c9e1fbc713 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java @@ -8,11 +8,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Dynamic Generic Event Source Registration", + description = + """ + Demonstrates dynamic registration of generic event sources during runtime. The test \ + verifies that event sources can be dynamically added to a reconciler and properly \ + trigger reconciliation when the associated resources change, enabling flexible event \ + source management. + """) class DynamicGenericEventSourceRegistrationIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java index b888143d2d..9ee3df10e1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java @@ -6,12 +6,22 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Error Status Handler for Failed Reconciliations", + description = + """ + Demonstrates how to implement error status handlers that update resource status when \ + reconciliations fail. The test verifies that error messages are properly recorded in the \ + resource status after each failed retry attempt. This provides visibility into \ + reconciliation failures and helps with debugging operator issues. + """) class ErrorStatusHandlerIT { public static final int MAX_RETRY_ATTEMPTS = 3; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java index eaa881709b..185b6c08f0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java @@ -6,12 +6,22 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.TestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Custom Event Source for Periodic Reconciliation", + description = + """ + Demonstrates how to implement custom event sources that trigger reconciliation on a \ + periodic basis. The test verifies that reconciliations are triggered at regular intervals \ + by a timer-based event source. This enables operators to perform periodic checks or \ + updates independent of resource changes. + """) class EventSourceIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java index 1e51ad5ab0..6cb24473ab 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java @@ -6,12 +6,22 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.filter.FilterTestReconciler.CONFIG_MAP_FILTER_VALUE; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Filtering Events for Primary and Secondary Resources", + description = + """ + Demonstrates how to implement event filters for both primary custom resources and \ + secondary dependent resources. The test verifies that resource updates matching specific \ + filter criteria are ignored and don't trigger reconciliation. This helps reduce \ + unnecessary reconciliation executions and improve operator efficiency. + """) class FilterIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java index 3b2fccfe59..44d8c7fabb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java @@ -3,10 +3,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.generickubernetesresource.GenericKubernetesDependentSpec; import io.javaoperatorsdk.operator.dependent.generickubernetesresource.GenericKubernetesDependentTestBase; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Working with GenericKubernetesResource for Dynamic Resource Types", + description = + """ + Demonstrates how to use GenericKubernetesResource to work with Kubernetes resources \ + dynamically without requiring compile-time type definitions. This approach is useful when \ + building operators that need to manage arbitrary Kubernetes resources or when the resource \ + types are not known at compile time. The test shows how to handle generic resources as \ + dependent resources in a reconciler. + """) public class GenericKubernetesResourceHandlingIT extends GenericKubernetesDependentTestBase { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java index 4f499b256b..a16a6cafc5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java @@ -6,12 +6,22 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.gracefulstop.GracefulStopTestReconciler.RECONCILER_SLEEP; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Graceful Operator Shutdown with Reconciliation Timeout", + description = + """ + Demonstrates how to configure graceful shutdown behavior with reconciliation termination \ + timeouts. The test verifies that in-progress reconciliations are allowed to complete when \ + the operator stops. This ensures clean shutdown without interrupting ongoing \ + reconciliation work. + """) public class GracefulStopIT { public static final String TEST_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java index c1a857c22c..3b442666d5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java @@ -8,6 +8,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.informereventsource.InformerEventSourceTestCustomReconciler.MISSING_CONFIG_MAP; @@ -17,6 +18,15 @@ import static org.assertj.core.api.Assertions.fail; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Using Informer Event Source to Watch Secondary Resources", + description = + """ + Demonstrates how to use InformerEventSource to watch changes in secondary resources \ + (ConfigMaps) and trigger reconciliation when those resources are created, updated, or \ + deleted. The test verifies that the reconciler responds to ConfigMap changes and updates \ + the primary resource status accordingly. + """) class InformerEventSourceIT { public static final String RESOURCE_NAME = "informertestcr"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java index 3cd5ddccbc..9d27c5d2d9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java @@ -10,6 +10,7 @@ import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; @@ -17,6 +18,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Watching resources in a remote Kubernetes cluster", + description = + """ + Demonstrates how to configure an informer event source to watch resources in a different \ + Kubernetes cluster from where the operator is running. This enables multi-cluster \ + scenarios where an operator in one cluster manages resources in another cluster. + """) @EnableKubeAPIServer class InformerRemoteClusterIT { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java index b073bee248..b6820459f2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.labelselector.LabelSelectorTestReconciler.LABEL_KEY; @@ -15,6 +16,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Label Selector for Custom Resource Filtering", + description = + """ + Demonstrates how to configure label selectors to filter which custom resources an \ + operator watches. The test verifies that only resources with matching labels trigger \ + reconciliation. This allows operators to selectively manage a subset of custom resources \ + based on their labels. + """) class LabelSelectorIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java index f6194439e2..f35b2380a4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java @@ -14,12 +14,22 @@ import io.fabric8.kubernetes.api.model.coordination.v1.LeaseSpecBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Leader election with namespace change handling", + description = + """ + Tests that when an operator is not elected as leader, changing the watched namespaces does \ + not start processing. This ensures that only the leader operator actively \ + reconciles resources, preventing conflicts in multi-instance deployments with \ + leader election. + """) public class LeaderElectionChangeNamespaceIT { public static final String LEASE_NAME = "nschangelease"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java index f90fda5c5e..55836c5617 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java @@ -4,11 +4,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Manually managing observedGeneration in status", + description = + """ + Shows how to manually track and update the observedGeneration field in status to indicate \ + which generation of the resource spec has been successfully processed. This is useful for \ + providing clear feedback to users about reconciliation progress. + """) public class ManualObservedGenerationIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java index af7cdbe8ec..9ecf50741c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java @@ -6,11 +6,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Maximum Reconciliation Interval Configuration", + description = + """ + Demonstrates how to configure a maximum interval for periodic reconciliation triggers. \ + The test verifies that reconciliation is automatically triggered at the configured \ + interval even when there are no resource changes, enabling periodic validation and drift \ + detection. + """) class MaxIntervalIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java index bc55fa6035..40659a5793 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java @@ -6,11 +6,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Maximum Reconciliation Interval After Retry", + description = + """ + Tests that reconciliation is repeatedly triggered based on the maximum interval setting \ + even after retries. This ensures periodic reconciliation continues at the configured \ + maximum interval, maintaining eventual consistency regardless of retry attempts. + """) class MaxIntervalAfterRetryIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java index 81cfa7fd07..23fe9c815a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java @@ -4,11 +4,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Multiple reconcilers for the same resource type", + description = + """ + Demonstrates how to register multiple reconcilers for the same custom resource type, with \ + each reconciler handling different resources based on label selectors or other \ + criteria. This enables different processing logic for different subsets of the same \ + resource type. + """) public class MultipleReconcilerSameTypeIT { public static final String TEST_RESOURCE_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java index 18a937040f..776054e893 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java @@ -7,10 +7,20 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Managing Multiple Secondary Event Sources", + description = + """ + Demonstrates how to configure and use multiple secondary event sources for a single \ + reconciler. The test verifies that the reconciler is triggered by changes to different \ + secondary resources and handles events from multiple sources correctly, including periodic \ + event sources. + """) class MultipleSecondaryEventSourceIT { public static final String TEST_RESOURCE_NAME = "testresource"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java index 9e0c6eb1fb..11b26e6ae3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java @@ -13,6 +13,7 @@ import io.fabric8.kubernetes.client.WatcherException; import io.fabric8.kubernetes.client.informers.SharedIndexInformer; import io.fabric8.kubernetes.client.utils.Serialization; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.api.config.InformerStoppedHandler; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -21,6 +22,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Handling Multiple CRD Versions", + description = + """ + Demonstrates how to work with Custom Resource Definitions that have multiple API \ + versions. The test shows how to configure multiple reconcilers for different versions of \ + the same CRD, handle version-specific schemas, and deal with incompatible version \ + conversions. It also demonstrates error handling through InformerStoppedHandler when \ + deserialization fails due to schema incompatibilities between versions. + """) class MultiVersionCRDIT { private static final Logger log = LoggerFactory.getLogger(MultiVersionCRDIT.class); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java index 03bd6b0a7a..ca21390259 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java @@ -8,11 +8,20 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Skipping status updates when next reconciliation is imminent", + description = + """ + Shows how to use the nextReconciliationImminent flag to skip status updates when another \ + reconciliation event is already pending. This optimization prevents unnecessary \ + status patch operations when rapid consecutive reconciliations occur. + """) public class NextReconciliationImminentIT { private static final Logger log = LoggerFactory.getLogger(NextReconciliationImminentIT.class); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java index a835dd2de6..301b91fea1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.TestUtils; @@ -15,6 +16,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Patching resource and status without Server-Side Apply", + description = + """ + Demonstrates how to patch both the primary resource metadata/spec and status subresource \ + using traditional JSON merge patch instead of Server-Side Apply. This shows the \ + legacy approach for updating resources when SSA is disabled. + """) class PatchResourceAndStatusNoSSAIT { @RegisterExtension LocallyRunOperatorExtension extension = diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java index f395706092..7c251c91fa 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java @@ -1,7 +1,17 @@ package io.javaoperatorsdk.operator.baseapi.patchresourcewithssa; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +@Sample( + tldr = "Patching resource and status with Server-Side Apply", + description = + """ + Demonstrates how to use Server-Side Apply (SSA) to patch both the primary resource and its \ + status subresource. SSA provides better conflict resolution and field management \ + tracking compared to traditional merge patches, making it the recommended approach \ + for resource updates. + """) public class PatchResourceAndStatusWithSSAIT extends PatchWithSSAITBase { @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java index d512665d06..e8ca8ffc57 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java @@ -1,7 +1,17 @@ package io.javaoperatorsdk.operator.baseapi.patchresourcewithssa; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +@Sample( + tldr = "Patching Resources with Server-Side Apply (SSA)", + description = + """ + Demonstrates how to use Server-Side Apply (SSA) for patching primary resources in \ + Kubernetes. The test verifies that the reconciler can patch resources using SSA, which \ + provides better conflict resolution and field management compared to traditional update \ + approaches, including proper handling of managed fields. + """) public class PatchResourceWithSSAIT extends PatchWithSSAITBase { @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java index 20f89e0d39..f2fa6f0064 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java @@ -4,11 +4,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Per-resource polling event source implementation", + description = + """ + Shows how to implement a per-resource polling event source where each primary resource has \ + its own polling schedule to fetch external state. This is useful for integrating \ + with external systems that don't support event-driven notifications. + """) class PerResourcePollingEventSourceIT { public static final String NAME_1 = "name1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java index 9063ef0fcb..784fe0a579 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java @@ -7,12 +7,23 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.primaryindexer.AbstractPrimaryIndexerTestReconciler.CONFIG_MAP_NAME; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Using Primary Indexer for Secondary Resource Mapping", + description = + """ + Demonstrates how to use primary indexers to efficiently map secondary resources back to \ + their primary resources. When a secondary resource (like a ConfigMap) changes, the primary \ + indexer allows the framework to determine which primary resources should be reconciled. \ + This pattern enables efficient one-to-many and many-to-many relationships between primary \ + and secondary resources without polling or full scans. + """) public class PrimaryIndexerIT { public static final String RESOURCE_NAME1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java index d82c24a55f..778745cca2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java @@ -6,11 +6,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Primary to Secondary Resource Mapping", + description = + """ + Demonstrates many-to-one mapping between primary and secondary resources where multiple \ + primary resources can reference the same secondary resource. The test verifies that \ + changes in the secondary resource trigger reconciliation of all related primary resources, \ + enabling shared resource patterns. + """) class PrimaryToSecondaryIT { public static final String CLUSTER_NAME = "cluster1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java index 84e6910b35..c27ebeb75f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.primarytosecondary.PrimaryToSecondaryIT.cluster; @@ -14,6 +15,15 @@ * The intention with this IT is to show the use cases why the PrimaryToSecondary Mapper is needed, * and the situation when it is not working. */ +@Sample( + tldr = "Issues When Primary-to-Secondary Mapper Is Missing", + description = + """ + Demonstrates the problems that occur when accessing secondary resources without a \ + proper PrimaryToSecondaryMapper configured. The test shows that accessing secondary \ + resources through the context fails without the mapper, while direct cache access works \ + as a workaround, highlighting the importance of proper mapper configuration. + """) class PrimaryToSecondaryMissingIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java index 8b04fd0095..ed0a595a96 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java @@ -9,12 +9,21 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.ratelimit.RateLimitReconciler.REFRESH_PERIOD; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Rate Limiting Reconciliation Executions", + description = + """ + Demonstrates how to implement rate limiting to control how frequently reconciliations \ + execute. The test shows that multiple rapid resource updates are batched and executed at a \ + controlled rate. This prevents overwhelming the system when resources change frequently. + """) class RateLimitIT { private static final Logger log = LoggerFactory.getLogger(RateLimitIT.class); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java index 410d34a390..ef91272f70 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import io.javaoperatorsdk.operator.support.TestUtils; @@ -13,6 +14,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Automatic Retry for Failed Reconciliations", + description = + """ + Demonstrates how to configure automatic retry logic for reconciliations that fail \ + temporarily. The test shows that failed executions are automatically retried with \ + configurable intervals and max attempts. After a specified number of retries, the \ + reconciliation succeeds and updates the resource status accordingly. + """) class RetryIT { public static final int RETRY_INTERVAL = 150; public static final int MAX_RETRY_ATTEMPTS = 5; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java index f57a70201f..07cc458713 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java @@ -3,12 +3,22 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import static io.javaoperatorsdk.operator.baseapi.retry.RetryIT.createTestCustomResource; import static org.assertj.core.api.Assertions.assertThat; +@Sample( + tldr = "Maximum Retry Attempts Configuration", + description = + """ + Demonstrates how to configure a maximum number of retry attempts for failed \ + reconciliations. The test verifies that the operator stops retrying after reaching the \ + configured maximum attempts. This prevents infinite retry loops when reconciliations \ + consistently fail. + """) class RetryMaxAttemptIT { public static final int MAX_RETRY_ATTEMPTS = 3; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java index cbd8de4459..c744830f5d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java @@ -7,12 +7,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.TestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Basic reconciler execution", + description = + """ + Demonstrates the basic reconciler execution flow including resource creation, status \ + updates, and cleanup. This test verifies that a reconciler can create dependent resources \ + (ConfigMap), update status, and properly handle cleanup when resources are deleted. + """) class ReconcilerExecutorIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/finalizer/SSAFinalizerIssueIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/finalizer/SSAFinalizerIssueIT.java index a830675518..25cd6fbd91 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/finalizer/SSAFinalizerIssueIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/finalizer/SSAFinalizerIssueIT.java @@ -8,11 +8,21 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.dsl.base.PatchContext; import io.fabric8.kubernetes.client.dsl.base.PatchType; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Server-Side Apply Finalizer Field Manager Issue", + description = + """ + Demonstrates a potential issue with Server-Side Apply (SSA) when adding finalizers. \ + When a resource is created with the same field manager used by the controller, adding \ + a finalizer can unexpectedly remove other spec fields, showcasing field manager \ + ownership conflicts in SSA. + """) class SSAFinalizerIssueIT { public static final String TEST_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/specupdate/SSASpecUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/specupdate/SSASpecUpdateIT.java index 9cccd32e3c..af4c668884 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/specupdate/SSASpecUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ssaissue/specupdate/SSASpecUpdateIT.java @@ -4,11 +4,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Server-Side Apply Finalizer Removal on Spec Update", + description = + """ + Demonstrates an issue with Server-Side Apply (SSA) where updating the resource spec \ + without explicitly including the finalizer causes the finalizer to be removed. This \ + highlights the importance of including all desired fields when using SSA to avoid \ + unintended field removal. + """) class SSASpecUpdateIT { public static final String TEST_RESOURCE_NAME = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/startsecondaryaccess/StartupSecondaryAccessIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/startsecondaryaccess/StartupSecondaryAccessIT.java index 61fc40803c..157569530d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/startsecondaryaccess/StartupSecondaryAccessIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/startsecondaryaccess/StartupSecondaryAccessIT.java @@ -7,6 +7,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.startsecondaryaccess.StartupSecondaryAccessReconciler.LABEL_KEY; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Accessing Secondary Resources During Operator Startup", + description = + """ + Verifies that reconcilers can properly access all secondary resources during operator \ + startup, even when a large number of secondary resources exist. The test ensures that \ + the informer cache is fully synchronized before reconciliation begins, allowing access \ + to all related resources. + """) class StartupSecondaryAccessIT { public static final int SECONDARY_NUMBER = 200; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheIT.java index 9d0b923056..f6e91f80dc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheIT.java @@ -6,11 +6,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Status patch caching for consistency", + description = + """ + Demonstrates how the framework caches status patches to ensure consistency when status is \ + updated frequently. The cache guarantees that status values are monotonically \ + increasing and always reflect the most recent state, even with rapid successive \ + updates. + """) public class StatusPatchCacheIT { public static final String TEST_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java index 4913b900a0..8cb06adbed 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.statuspatchnonlocking.StatusPatchLockingReconciler.MESSAGE; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Status Patching Without Optimistic Locking for Non-SSA", + description = + """ + Tests status update behavior when not using Server-Side Apply (SSA), verifying that \ + optimistic locking is not enforced on status patches. The test also demonstrates proper \ + field deletion when values are set to null, ensuring correct status management without \ + SSA optimistic locking. + """) class StatusPatchNotLockingForNonSSAIT { public static final String TEST_RESOURCE_NAME = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java index a301e9f61a..44158fdd8e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java @@ -11,12 +11,22 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Migrating Status Patching from Non-SSA to SSA", + description = + """ + Demonstrates the process and challenges of migrating status patching from traditional \ + update methods to Server-Side Apply (SSA). Tests show a known Kubernetes issue where \ + field deletion doesn't work correctly during migration, and provides a workaround by \ + removing managed field entries from the previous update method. + """) public class StatusPatchSSAMigrationIT { public static final String TEST_RESOURCE_NAME = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java index ad51d82059..b745f82bc6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java @@ -7,12 +7,22 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.statusupdatelocking.StatusUpdateLockingReconciler.WAIT_TIME; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Status Update Locking and Concurrency Control", + description = + """ + Demonstrates how the framework handles concurrent status updates and ensures no \ + optimistic locking conflicts occur when updating status subresources. The test verifies \ + that status updates can proceed independently of spec updates without causing version \ + conflicts or requiring retries. + """) class StatusUpdateLockingIT { public static final String TEST_RESOURCE_NAME = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java index 7544e3b791..b74c6dc78d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.TestUtils; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Status Subresource Updates", + description = + """ + Demonstrates how to properly update the status subresource of custom resources. The test \ + verifies that status updates are handled correctly without triggering unnecessary \ + reconciliations, and that concurrent spec and status updates are managed properly with \ + optimistic locking and retry mechanisms. + """) class SubResourceUpdateIT { public static final int WAIT_AFTER_EXECUTION = 500; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java index 735255c54c..2435e71fdd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java @@ -5,6 +5,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.baseapi.unmodifiabledependentpart.UnmodifiablePartConfigMapDependent.ACTUAL_DATA_KEY; @@ -12,6 +13,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Unmodifiable Parts in Dependent Resources", + description = + """ + Demonstrates how to preserve certain parts of a dependent resource from being modified \ + during updates while allowing other parts to change. This test shows that initial data \ + can be marked as unmodifiable and will remain unchanged even when the primary resource \ + spec is updated, enabling partial update control. + """) public class UnmodifiableDependentPartIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java index b2a2b463ba..34cbedf310 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java @@ -4,11 +4,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Update Status in Cleanup and Reschedule", + description = + """ + Tests the ability to update resource status during cleanup and reschedule the cleanup \ + operation. This demonstrates that cleanup methods can perform status updates and request \ + to be called again after a delay, enabling multi-step cleanup processes with status \ + tracking. + """) public class UpdateStatusInCleanupAndRescheduleIT { public static final String TEST_RESOURCE = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java index 108607283b..5952396fc1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java @@ -2,9 +2,19 @@ import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.bulkdependent.managed.ManagedDeleterBulkReconciler; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Bulk Dependent Resource Deleter Implementation", + description = + """ + Demonstrates implementation of a bulk dependent resource with custom deleter logic. \ + This test extends BulkDependentTestBase to verify that bulk dependent resources can \ + implement custom deletion strategies, managing multiple resources efficiently during \ + cleanup operations. + """) public class BulkDependentDeleterIT extends BulkDependentTestBase { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java index eb3a6e7368..f9027c089d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestCustomResource; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -13,6 +14,15 @@ import static org.assertj.core.api.Assertions.*; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Bulk Dependent Resources with Ready Conditions", + description = + """ + Tests bulk dependent resources with preconditions that control when reconciliation \ + occurs. This demonstrates using ready conditions to ensure bulk operations only execute \ + when the primary resource is in the appropriate state, coordinating complex multi-resource \ + management. + """) class BulkDependentWithConditionIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java index 4f7d425729..3080beb001 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java @@ -3,12 +3,22 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestBase.*; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Managing External Bulk Resources", + description = + """ + Demonstrates managing multiple external resources (non-Kubernetes) using bulk dependent \ + resources. This pattern allows operators to manage a variable number of external resources \ + based on primary resource specifications, handling creation, updates, and deletion of \ + external resources at scale. + """) class BulkExternalDependentIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java index 61b7f62a24..7353887ef4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java @@ -2,9 +2,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestBase; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Bulk Dependent Resources with Managed Workflow", + description = + """ + Demonstrates how to manage bulk dependent resources using the managed workflow approach. \ + This test extends the base bulk dependent test to show how multiple instances of \ + the same type of dependent resource can be created and managed together. The \ + managed workflow handles the orchestration of creating, updating, and deleting \ + multiple dependent resources based on the primary resource specification. + """) public class ManagedBulkDependentIT extends BulkDependentTestBase { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java index 422360b7b2..c2524f635c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java @@ -7,6 +7,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestCustomResource; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestSpec; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Read-Only Bulk Dependent Resources", + description = + """ + Demonstrates how to use read-only bulk dependent resources to observe and react to \ + multiple existing resources without managing them. This test shows how an operator \ + can monitor a collection of resources created externally and update the custom \ + resource status based on their state, without creating or modifying them. + """) public class ReadOnlyBulkDependentIT { public static final int EXPECTED_NUMBER_OF_RESOURCES = 2; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java index a74102db0d..3c2a12a680 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java @@ -2,9 +2,19 @@ import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestBase; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Standalone Bulk Dependent Resources", + description = + """ + Demonstrates how to use standalone bulk dependent resources to manage multiple \ + resources of the same type efficiently. This test shows how bulk operations can be \ + performed on a collection of resources without individual reconciliation cycles, \ + improving performance when managing many similar resources. + """) class StandaloneBulkDependentIT extends BulkDependentTestBase { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java index b0b716e8e2..37d82c313c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java @@ -4,11 +4,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Cleanup handlers for managed dependent resources", + description = + """ + Shows how to implement cleanup logic for managed dependent resources using the Cleaner \ + interface. The framework automatically adds finalizers and invokes the cleanup \ + method when the primary resource is deleted, ensuring proper cleanup of dependent \ + resources. + """) class CleanerForManagedDependentResourcesOnlyIT { public static final String TEST_RESOURCE_NAME = "cleaner-for-reconciler-test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java index 3c41bae977..1d405a788d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java @@ -10,11 +10,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Create-Only Dependent Resources with Server-Side Apply", + description = + """ + Demonstrates how to configure a dependent resource that is only created if it doesn't \ + exist, using Server-Side Apply (SSA). This test shows that when a resource already \ + exists, the dependent resource implementation will not modify it, preserving any \ + external changes. + """) class CreateOnlyIfNotExistingDependentWithSSAIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java index 466837de4e..5a4862bebf 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java @@ -7,6 +7,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.processing.event.source.informer.Mappers.DEFAULT_ANNOTATION_FOR_NAME; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Annotation-Based Secondary Resource Mapping for Dependents", + description = + """ + Demonstrates using annotations instead of owner references to map secondary resources \ + to primary resources in dependent resources. This approach is useful when owner references \ + cannot be used (e.g., cross-namespace or cluster-scoped relationships), using special \ + annotations to establish the relationship. + """) class DependentAnnotationSecondaryMapperIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java index 42f365884f..d9a1525d0c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java @@ -5,6 +5,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.dependentcustommappingannotation.CustomMappingConfigMapDependentResource.CUSTOM_NAMESPACE_KEY; @@ -12,6 +13,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Custom Annotation Keys for Resource Mapping", + description = + """ + Tests custom annotation-based mapping for dependent resources using configurable \ + annotation keys instead of the default ones. This allows developers to customize which \ + annotations are used to establish relationships between primary and secondary resources, \ + providing flexibility for different naming conventions or avoiding conflicts. + """) class DependentCustomMappingAnnotationIT { public static final String INITIAL_VALUE = "initial value"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java index c02bac5a5d..fbddee518f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java @@ -5,12 +5,23 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.dependentdifferentnamespace.ConfigMapDependentResource.KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Dependent Resources in Different Namespaces", + description = + """ + Demonstrates how to manage dependent resources in a namespace different from the primary \ + resource. This test shows how to configure dependent resources to be created in a \ + specific namespace rather than inheriting the namespace from the primary resource. \ + The test verifies full CRUD operations for a ConfigMap that lives in a different \ + namespace than the custom resource that manages it. + """) class DependentDifferentNamespaceIT { public static final String TEST_1 = "different-ns-test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java index bc7b578d7f..c988ef2741 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java @@ -8,6 +8,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.dependentfilter.DependentFilterTestReconciler.CM_VALUE_KEY; @@ -15,6 +16,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Filtering Reconciliation Triggers from Dependent Resources", + description = + """ + Demonstrates how to filter events from dependent resources to prevent unnecessary \ + reconciliation triggers. This test shows how to configure filters on dependent \ + resources so that only specific changes trigger a reconciliation of the primary \ + resource. The test verifies that updates to filtered fields in the dependent \ + resource do not cause the reconciler to execute, improving efficiency and avoiding \ + reconciliation loops. + """) class DependentFilterIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java index e9ddc6cd6e..60e2670099 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java @@ -7,11 +7,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Event filtering for dependent resource operations", + description = + """ + Demonstrates how to configure event filters on dependent resources to prevent \ + reconciliation loops. When a dependent resource is created or updated by the \ + controller, the filter prevents those events from triggering unnecessary \ + reconciliations. + """) class DependentOperationEventFilterIT { public static final String TEST = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java index 270b89a6a5..760f354007 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java @@ -4,9 +4,19 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Reusing Dependent Resource Instances Across Tests", + description = + """ + Demonstrates that dependent resource instances can be safely reused across multiple \ + operator start/stop cycles. This is particularly useful in CDI-managed environments \ + like Quarkus, where dependent resources are managed as beans and should be reusable \ + across test executions. + """) class DependentReInitializationIT { /** diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java index 1b71c79448..740cd81e1a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java @@ -8,11 +8,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Dependent Resources with Cross-References", + description = + """ + Tests dependent resources that reference each other, creating interdependencies between \ + multiple secondary resources. The test verifies that resources with circular or \ + cross-references can be safely created, managed, and deleted without causing issues, \ + even under concurrent operations with multiple primary resources. + """) class DependentResourceCrossRefIT { public static final String TEST_RESOURCE_NAME = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java index 8ab686b1b8..3fab17116f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java @@ -11,11 +11,23 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.dsl.base.PatchContext; import io.fabric8.kubernetes.client.dsl.base.PatchType; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Server-Side Apply (SSA) with Dependent Resources", + description = + """ + Demonstrates how to use Server-Side Apply (SSA) with dependent resources and field manager \ + matching. This test shows how SSA allows multiple controllers to manage different \ + fields of the same resource without conflicts. The test verifies that changes made \ + by different field managers are properly isolated, and that the operator only \ + updates its own fields when changes occur, preserving fields managed by other \ + controllers. + """) public class DependentSSAMatchingIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java index 0d354febdf..18f7421293 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java @@ -11,12 +11,22 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Migrating Dependent Resources from Legacy to SSA", + description = + """ + Demonstrates migrating dependent resource management from legacy update methods to \ + Server-Side Apply (SSA). Tests show bidirectional migration scenarios and field manager \ + handling, including using the default fabric8 field manager to avoid creating duplicate \ + managed field entries during migration. + """) class DependentSSAMigrationIT { public static final String FABRIC8_CLIENT_DEFAULT_FIELD_MANAGER = "fabric8-kubernetes-client"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java index 87e588673d..23f19a2143 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java @@ -2,8 +2,18 @@ import org.junit.jupiter.api.extension.RegisterExtension; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "External State Tracking in Dependent Resources", + description = + """ + Demonstrates managing dependent resources with external state that needs to be tracked \ + independently of Kubernetes resources. This pattern allows operators to maintain state \ + information for external systems or resources, ensuring proper reconciliation even when \ + the external state differs from the desired Kubernetes resource state. + """) public class ExternalStateDependentIT extends ExternalStateTestBase { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java index bae36431b5..e4c8bab47c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java @@ -5,6 +5,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.ExternalIDGenServiceMock; @@ -12,6 +13,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Managing External Resources with Persistent State", + description = + """ + Demonstrates how to manage external resources (outside of Kubernetes) while maintaining \ + their state in Kubernetes resources. This test shows a pattern for reconciling \ + external systems by storing external resource identifiers in a ConfigMap. The test \ + verifies that external resources can be created, updated, and deleted in \ + coordination with Kubernetes resources, with the ConfigMap serving as a state store \ + for external resource IDs. + """) class ExternalStateIT { private static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java index b2714fab47..d9c7693d70 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java @@ -6,12 +6,22 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.ExternalIDGenServiceMock; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Bulk External State Management with Persistent State", + description = + """ + Demonstrates managing multiple external resources with persistent state tracking using \ + bulk dependent resources. This combines external state management with bulk operations, \ + allowing operators to track and reconcile a variable number of external resources with \ + persistent state that survives operator restarts. + """) class ExternalStateBulkIT { private static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java index 93fc34fbf0..416843c723 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java @@ -3,10 +3,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.generickubernetesresource.GenericKubernetesDependentSpec; import io.javaoperatorsdk.operator.dependent.generickubernetesresource.GenericKubernetesDependentTestBase; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Generic Kubernetes Dependent Resource (Managed)", + description = + """ + Demonstrates how to use GenericKubernetesResource as a managed dependent resource. This \ + test shows how to work with generic Kubernetes resources that don't have a specific \ + Java model class, allowing the operator to manage any Kubernetes resource type \ + dynamically. + """) public class GenericKubernetesDependentManagedIT extends GenericKubernetesDependentTestBase { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java index 9afdec5474..97ae13ae2a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java @@ -3,10 +3,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.generickubernetesresource.GenericKubernetesDependentSpec; import io.javaoperatorsdk.operator.dependent.generickubernetesresource.GenericKubernetesDependentTestBase; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Generic Kubernetes Resource as Standalone Dependent", + description = + """ + Tests using GenericKubernetesResource as a standalone dependent resource. This approach \ + allows operators to manage arbitrary Kubernetes resources without requiring specific Java \ + classes for each resource type, providing flexibility for managing various resource types \ + dynamically. + """) public class GenericKubernetesDependentStandaloneIT extends GenericKubernetesDependentTestBase { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java index 8e08c4e739..0ee286a65a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java @@ -7,12 +7,24 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.IntegrationTestConstants; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Kubernetes Native Garbage Collection for Dependent Resources", + description = + """ + Demonstrates how to leverage Kubernetes native garbage collection for dependent resources \ + using owner references. This test shows how dependent resources are automatically \ + cleaned up by Kubernetes when the owner resource is deleted, and how to \ + conditionally create or delete dependent resources based on the primary resource \ + state. Owner references ensure that dependent resources don't outlive their \ + owners. + """) class KubernetesDependentGarbageCollectionIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java index a7dfefdaa8..962dfaa959 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java @@ -7,6 +7,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.multipledependentresource.MultipleDependentResourceConfigMap.DATA_KEY; @@ -16,6 +17,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Managing Multiple Dependent Resources", + description = + """ + Demonstrates how to manage multiple dependent resources from a single reconciler. This \ + test shows how a single custom resource can create, update, and delete multiple \ + ConfigMaps (or other Kubernetes resources) as dependents. The test verifies that \ + all dependent resources are created together, updated together when the primary \ + resource changes, and properly cleaned up when the primary resource is deleted. + """) public class MultipleDependentResourceIT { public static final String CHANGED_VALUE = "changed value"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java index 8be51ebf1a..4817078b7c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java @@ -8,11 +8,20 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Multiple Dependents of Same Type Without Discriminator", + description = + """ + Demonstrates managing multiple dependent resources of the same type (ConfigMaps) without \ + using discriminators. The framework uses resource names to differentiate between them, \ + simplifying configuration when distinct names are sufficient for identification. + """) class MultipleDependentResourceWithNoDiscriminatorIT { public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java index 5ba6d56be3..0d4e40e5ee 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Multiple Managed Dependents of Same Type with Multi-Informer", + description = + """ + Tests managing multiple dependent resources of the same type using separate informers \ + for each. This approach allows for independent event handling and caching for resources \ + of the same type, useful when different caching strategies or event filtering is needed \ + for different instances. + """) class MultipleDependentSameTypeMultiInformerIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java index 1ed1da56f9..715b4763b5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java @@ -7,12 +7,22 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.multipledrsametypenodiscriminator.MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Multiple Managed Dependents of Same Type Without Discriminator", + description = + """ + Demonstrates managing multiple managed dependent resources of the same type without \ + explicit discriminators. The test verifies complete CRUD operations on multiple ConfigMaps, \ + showing that resource names alone can differentiate between dependents when a \ + discriminator is not needed. + """) public class MultipleManagedDependentNoDiscriminatorIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java index a835b5a255..4958f84ff1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS; @@ -14,6 +15,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Managing Multiple Dependent Resources of the Same Type", + description = + """ + Demonstrates how to manage multiple dependent resources of the same type from a single \ + reconciler. This test shows how multiple ConfigMaps with the same type can be \ + created, updated, and deleted as dependent resources of a custom resource, \ + verifying proper CRUD operations and garbage collection. + """) class MultipleManagedDependentSameTypeIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java index a8c1f889d0..cf39c66cc7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.dependent.multiplemanageddependentsametype.MultipleManagedDependentResourceSpec; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.support.ExternalServiceMock; @@ -11,6 +12,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Multiple Managed External Dependents of Same Type", + description = + """ + Tests managing multiple external (non-Kubernetes) dependent resources of the same type. \ + This demonstrates that operators can manage multiple instances of external resources \ + simultaneously, handling their lifecycle including creation, updates, and deletion. + """) class MultipleManagedExternalDependentSameTypeIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java index d5a704ca0c..975e30ecf3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java @@ -7,11 +7,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Dependent Resource Shared by Multiple Owners", + description = + """ + Demonstrates a dependent resource (ConfigMap) that is managed by multiple primary \ + resources simultaneously. Tests verify that updates from any owner trigger proper \ + reconciliation, owner references are correctly maintained, and the shared resource \ + properly aggregates data from all owners. + """) class MultiOwnerDependentTriggeringIT { public static final String VALUE_1 = "value1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java index 137e2ba663..375327376c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java @@ -7,11 +7,21 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Blocking Previous Annotation for Specific Resource Types", + description = + """ + Tests the previous annotation blocklist feature, which prevents storing previous resource \ + state annotations for specific resource types like Deployments. This optimization avoids \ + unnecessary reconciliation loops for resources that have server-side mutations, improving \ + performance and stability. + """) class PrevAnnotationBlockReconcilerIT { public static final String TEST_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerIT.java index 16bb0d46a7..774a446188 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerIT.java @@ -1,8 +1,18 @@ package io.javaoperatorsdk.operator.dependent.primaryindexer; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.baseapi.primaryindexer.PrimaryIndexerIT; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +@Sample( + tldr = "Primary Resource Indexer with Dependent Resources", + description = + """ + Extends PrimaryIndexerIT to test primary resource indexing functionality with dependent \ + resources. Demonstrates how custom indexes on primary resources can be used to efficiently \ + query and access resources within dependent resource implementations, enabling performant \ + lookups. + """) public class DependentPrimaryIndexerIT extends PrimaryIndexerIT { protected LocallyRunOperatorExtension buildOperator() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java index 11a080a8bf..212fb1e3de 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.primarytosecondaydependent.ConfigMapDependent.TEST_CONFIG_MAP_NAME; @@ -17,6 +18,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Primary to Secondary Dependent Resource", + description = + """ + Demonstrates how to configure dependencies between dependent resources where one \ + dependent resource (secondary) depends on another dependent resource (primary). \ + This test shows how a Secret's creation can be conditioned on the state of a \ + ConfigMap, illustrating the use of reconcile preconditions and dependent resource \ + chaining. + """) class PrimaryToSecondaryDependentIT { public static final String TEST_CR_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java index b8adb562dd..32923d0843 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java @@ -3,12 +3,21 @@ import org.junit.jupiter.api.*; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Operator restart and state recovery", + description = + """ + Tests that an operator can be stopped and restarted while maintaining correct behavior. \ + After restart, the operator should resume processing existing resources without \ + losing track of their state, demonstrating proper state recovery and persistence. + """) class OperatorRestartIT { private static final Operator operator = new Operator(o -> o.withCloseClientOnStop(false)); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java index 72edd4ae40..4742a5d621 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java @@ -6,11 +6,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Strict matching for Service resources", + description = + """ + Shows how to use a strict matcher for Service dependent resources that correctly handles \ + Service-specific fields. This prevents unnecessary updates when Kubernetes adds \ + default values or modifies certain fields, avoiding reconciliation loops. + """) public class ServiceStrictMatcherIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java index 3d62512bd7..58265b22b5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java @@ -5,6 +5,7 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.ServiceAccount; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.dependent.specialresourcesdependent.SpecialResourceSpec.CHANGED_VALUE; @@ -16,6 +17,15 @@ * Test for resources that are somehow special, currently mostly to cover the approach to handle * resources without spec. Not all the resources added here. */ +@Sample( + tldr = "Handling special Kubernetes resources without spec", + description = + """ + Demonstrates how to handle special built-in Kubernetes resources like ServiceAccount that \ + don't have a spec field. These resources require different handling approaches \ + since their configuration is stored directly in the resource body rather than in a \ + spec section. + """) public class SpecialResourcesDependentIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java index 63afd73d1a..42cf9cf8e7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java @@ -5,11 +5,21 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Service; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Using Legacy Resource Matcher with SSA", + description = + """ + Demonstrates using the legacy resource matcher with Server-Side Apply (SSA). The legacy \ + matcher provides backward compatibility for matching logic while using SSA for updates, \ + ensuring that resource comparisons work correctly even when migrating from traditional \ + update methods to SSA. + """) public class SSAWithLegacyMatcherIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java index 91fbd64a19..9e8f383b84 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.api.config.*; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; @@ -16,6 +17,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Standalone Dependent Resources", + description = + """ + Demonstrates how to use standalone dependent resources that are managed independently \ + without explicit workflow configuration. This test shows how dependent resources \ + can be created and managed programmatically, with the dependent resource handling \ + CRUD operations on a Kubernetes Deployment. The test verifies both creation and \ + update scenarios, including cache updates when the dependent resource state \ + changes. + """) public class StandaloneDependentResourceIT { public static final String DEPENDENT_TEST_NAME = "dependent-test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java index f54708a9ea..fd51afb4e0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java @@ -7,11 +7,20 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.apps.StatefulSet; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Sanitizing StatefulSet desired state for SSA", + description = + """ + Shows how to properly sanitize StatefulSet resources before using Server-Side Apply. \ + StatefulSets have immutable fields and server-managed fields that need to be \ + removed from the desired state to prevent conflicts and unnecessary updates. + """) public class StatefulSetDesiredSanitizerIT { public static final String TEST_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java index 4af8467c5c..8baed835b1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java @@ -9,6 +9,7 @@ import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.fabric8.kubernetes.client.readiness.Readiness; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.workflow.complexdependent.dependent.FirstService; import io.javaoperatorsdk.operator.workflow.complexdependent.dependent.FirstStatefulSet; @@ -18,6 +19,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Complex Workflow with Multiple Dependents", + description = + """ + Demonstrates a complex workflow with multiple dependent resources (StatefulSets and \ + Services) that have dependencies on each other. This test shows how to orchestrate \ + the reconciliation of interconnected dependent resources in a specific order. + """) class ComplexWorkflowIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java index 1793cceefa..a6a77d8a6e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java @@ -8,11 +8,21 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Workflow Activation Based on CRD Presence", + description = + """ + Tests workflow activation conditions that depend on the presence of specific Custom \ + Resource Definitions (CRDs). Dependent resources are only created when their corresponding \ + CRDs exist in the cluster, allowing operators to gracefully handle optional dependencies \ + and multi-cluster scenarios. + """) public class CRDPresentActivationConditionIT { public static final String TEST_1 = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java index 0de986ccf9..156ba5b7fd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java @@ -4,11 +4,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Workflow Functions on Vanilla Kubernetes Despite Inactive Resources", + description = + """ + Verifies that workflows function correctly on vanilla Kubernetes even when they include \ + resources that are not available on the platform (like OpenShift Routes). The operator \ + successfully reconciles by skipping inactive dependents based on activation conditions, \ + demonstrating platform-agnostic operator design. + """) public class WorkflowActivationConditionIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java index 482d2e5091..1c382247b8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java @@ -9,11 +9,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Managed Dependent Delete Condition", + description = + """ + Demonstrates how to use delete conditions to control when dependent resources can be \ + deleted. This test shows how the primary resource deletion can be blocked until \ + dependent resources are properly cleaned up, ensuring graceful shutdown and \ + preventing orphaned resources. + """) public class ManagedDependentDeleteConditionIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java index 2360660adc..4845dfe476 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java @@ -6,11 +6,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Multiple Dependents with Activation Conditions", + description = + """ + Demonstrates how to use activation conditions with multiple dependent resources. This test \ + shows how different dependent resources can be dynamically enabled or disabled \ + based on runtime conditions, allowing flexible workflow behavior that adapts to \ + changing requirements. + """) public class MultipleDependentWithActivationIT { public static final String INITIAL_VALUE = "initial_value"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java index ff25911a8c..56ff94d1f1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java @@ -6,11 +6,20 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Ordered Managed Dependent Resources", + description = + """ + Demonstrates how to control the order of reconciliation for managed dependent resources. \ + This test verifies that dependent resources are reconciled in a specific sequence, \ + ensuring proper orchestration when dependencies have ordering requirements. + """) class OrderedManagedDependentIT { @RegisterExtension diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java index 80bbf22d40..3c366877fd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java @@ -8,12 +8,21 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Workflow Activation Cleanup", + description = + """ + Demonstrates how workflow cleanup is handled when activation conditions are involved. This \ + test verifies that resources are properly cleaned up on operator startup even when \ + marked for deletion, ensuring no orphaned resources remain after restarts. + """) public class WorkflowActivationCleanupIT { private final KubernetesClient client = new KubernetesClientBuilder().build(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java index a5b5b23fe3..108a7d4a54 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java @@ -5,12 +5,22 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.workflow.workflowactivationcondition.ConfigMapDependentResource.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Workflow Activation Condition", + description = + """ + Demonstrates how to use activation conditions to conditionally enable or disable parts of \ + a workflow. This test shows how the workflow can adapt to different environments \ + (e.g., vanilla Kubernetes vs. OpenShift) by activating only the relevant dependent \ + resources based on runtime conditions. + """) public class WorkflowActivationConditionIT { public static final String TEST_RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java index 93551dcf43..9cf798033a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java @@ -9,12 +9,22 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.workflow.workflowallfeature.ConfigMapDependentResource.READY_TO_DELETE_ANNOTATION; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Comprehensive workflow with reconcile and delete conditions", + description = + """ + Demonstrates a complete workflow implementation including reconcile conditions, delete \ + conditions, and ready conditions. Shows how to control when dependent resources are \ + created or deleted based on conditions, and how to coordinate dependencies that \ + must wait for others to be ready. + """) public class WorkflowAllFeatureIT { public static final String RESOURCE_NAME = "test"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java index b6d4969fd3..a0227de13b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java @@ -5,11 +5,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Explicit Workflow Cleanup Invocation", + description = + """ + Tests explicit workflow cleanup invocation, demonstrating that workflow cleanup is called \ + even when using explicit workflow invocation mode. This ensures that dependent resources \ + are properly cleaned up during deletion regardless of how the workflow is invoked, \ + maintaining consistent cleanup behavior. + """) public class WorkflowExplicitCleanupIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java index eaa799300f..8027750346 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java @@ -7,11 +7,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Workflow Explicit Invocation", + description = + """ + Demonstrates how to explicitly control when a workflow is invoked rather than having it \ + run automatically on every reconciliation. This test shows how to programmatically \ + trigger workflow execution and how cleanup is still performed even with explicit \ + invocation. + """) public class WorkflowExplicitInvocationIT { public static final String RESOURCE_NAME = "test1"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java index 0d5f3b60f1..cd40d2c08a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java @@ -8,12 +8,22 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static io.javaoperatorsdk.operator.workflow.workflowactivationcondition.ConfigMapDependentResource.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Dynamic Workflow Activation and Deactivation", + description = + """ + Tests dynamic activation and deactivation of workflow dependents based on changing \ + conditions. Demonstrates that dependents can be conditionally activated or deactivated \ + during the resource lifecycle, with proper cleanup and recreation, and verifies that \ + inactive dependents don't trigger reconciliation or maintain informers. + """) public class WorkflowMultipleActivationIT { public static final String INITIAL_DATA = "initial data"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java index 2f5b7246c6..926d7d7622 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java @@ -4,11 +4,21 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.annotation.Sample; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +@Sample( + tldr = "Silent Workflow Exception Handling in Reconciler", + description = + """ + Demonstrates handling workflow exceptions silently within the reconciler rather than \ + propagating them. Tests verify that exceptions from dependent resources during both \ + reconciliation and cleanup are captured in the result object, allowing custom error \ + handling logic without failing the entire reconciliation. + """) public class WorkflowSilentExceptionHandlingIT { @RegisterExtension diff --git a/pom.xml b/pom.xml index 9931eefb7f..c2e6ae8443 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ sample-operators caffeine-bounded-cache-support bootstrapper-maven-plugin + test-index-processor diff --git a/test-index-processor/pom.xml b/test-index-processor/pom.xml new file mode 100644 index 0000000000..dc3e84ab3e --- /dev/null +++ b/test-index-processor/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + io.javaoperatorsdk + java-operator-sdk + 5.1.6-SNAPSHOT + + + test-index-processor + Test Index Annotation Processor + Annotation processor for generating integration test index documentation + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -proc:none + + + + + diff --git a/test-index-processor/src/main/java/io/javaoperatorsdk/annotation/Sample.java b/test-index-processor/src/main/java/io/javaoperatorsdk/annotation/Sample.java new file mode 100644 index 0000000000..3bfb1a4e79 --- /dev/null +++ b/test-index-processor/src/main/java/io/javaoperatorsdk/annotation/Sample.java @@ -0,0 +1,29 @@ +package io.javaoperatorsdk.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark integration tests as samples for documentation generation. Tests annotated + * with @Sample will be included in the generated samples.md documentation file. + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface Sample { + + /** + * A short title describing the test sample. + * + * @return the short title + */ + String tldr(); + + /** + * A detailed description of what the test does and demonstrates. + * + * @return the detailed description + */ + String description(); +} diff --git a/test-index-processor/src/main/java/io/javaoperatorsdk/processor/SampleProcessor.java b/test-index-processor/src/main/java/io/javaoperatorsdk/processor/SampleProcessor.java new file mode 100644 index 0000000000..d59e7bf687 --- /dev/null +++ b/test-index-processor/src/main/java/io/javaoperatorsdk/processor/SampleProcessor.java @@ -0,0 +1,143 @@ +package io.javaoperatorsdk.processor; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +import io.javaoperatorsdk.annotation.Sample; + +/** + * Annotation processor that generates a samples.md file containing documentation for all + * integration tests annotated with @Sample. + */ +@SupportedAnnotationTypes("io.javaoperatorsdk.annotation.Sample") +@SupportedSourceVersion(SourceVersion.RELEASE_17) +public class SampleProcessor extends AbstractProcessor { + + private final List samples = new ArrayList<>(); + private boolean processed = false; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (processed) { + return true; + } + + // Collect all @Sample annotated elements + for (Element element : roundEnv.getElementsAnnotatedWith(Sample.class)) { + if (element instanceof TypeElement) { + TypeElement typeElement = (TypeElement) element; + Sample sample = element.getAnnotation(Sample.class); + + String className = typeElement.getQualifiedName().toString(); + String simpleName = typeElement.getSimpleName().toString(); + String tldr = sample.tldr(); + String description = sample.description(); + + samples.add(new SampleInfo(className, simpleName, tldr, description)); + } + } + + // Generate the markdown file in the last round + if (roundEnv.processingOver() && !samples.isEmpty()) { + generateMarkdownFile(); + processed = true; + } + + return true; + } + + private void generateMarkdownFile() { + try { + // Sort samples by class name for consistent ordering + samples.sort(Comparator.comparing(s -> s.className)); + + // Create the samples.md file in the source output location + FileObject file = + processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "", "samples.md"); + + try (BufferedWriter writer = new BufferedWriter(file.openWriter())) { + writer.write("---\n"); + writer.write("title: Integration Test Index\n"); + writer.write("weight: 105\n"); + writer.write("---\n\n"); + writer.write( + "This document provides an index of all integration tests annotated with @Sample.\n\n" + + "These server also as sample for various use cases. " + + "Your are encouraged to improve both the tests and/or descriptions.\n\n"); + + // Generate table of contents + writer.write("## Contents\n\n"); + for (SampleInfo sample : samples) { + String anchor = sample.simpleName.toLowerCase(); + writer.write("- [" + sample.tldr + "](#" + anchor + ")\n"); + } + writer.write("\n---\n\n"); + + // Generate individual test sections + for (SampleInfo sample : samples) { + writer.write("## " + sample.simpleName + "\n\n"); + writer.write("**" + sample.tldr + "**\n\n"); + writer.write(sample.description + "\n\n"); + writer.write("**Package:** " + getGitHubPackageLink(sample.className) + "\n\n"); + writer.write("---\n\n"); + } + } + + processingEnv + .getMessager() + .printMessage( + Diagnostic.Kind.NOTE, "Generated samples.md with " + samples.size() + " samples"); + } catch (IOException e) { + processingEnv + .getMessager() + .printMessage(Diagnostic.Kind.ERROR, "Failed to generate samples.md: " + e.getMessage()); + } + } + + private String getGitHubPackageLink(String className) { + // Extract package name by removing the simple class name + int lastDot = className.lastIndexOf('.'); + if (lastDot == -1) { + return "[root package]"; + } + + String packageName = className.substring(0, lastDot); + String packagePath = packageName.replace('.', '/'); + + // GitHub repository base URL + String baseUrl = "https://github.com/operator-framework/java-operator-sdk/tree/main"; + String sourcePath = "operator-framework/src/test/java"; + + String githubUrl = baseUrl + "/" + sourcePath + "/" + packagePath; + return "[" + packageName + "](" + githubUrl + ")"; + } + + private static class SampleInfo { + final String className; + final String simpleName; + final String tldr; + final String description; + + SampleInfo(String className, String simpleName, String tldr, String description) { + this.className = className; + this.simpleName = simpleName; + this.tldr = tldr; + this.description = description; + } + } +} diff --git a/test-index-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/test-index-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..a9fb2eb851 --- /dev/null +++ b/test-index-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +io.javaoperatorsdk.processor.SampleProcessor