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/baseapi/ConcurrencyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java
index 80fb09095d..e1139695b0 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,13 @@
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/builtinresourcecleaner/BuiltInResourceCleanerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java
index 9d914d080c..6e3a7a2721 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,13 @@
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..0e4948dd9f 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,14 @@
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..b3e753443a 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,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 = "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/clusterscopedresource/ClusterScopedResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java
index 4c92fbada7..27de87c62d 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,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 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/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java
index 2d9a7db573..261f6681c2 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,20 @@
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/deployment/KubernetesResourceStatusUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java
index 5fe9bfa939..7f3b5019a6 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,21 @@
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/errorstatushandler/ErrorStatusHandlerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java
index b888143d2d..a78b00eb8c 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,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 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..a6b64bb512 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,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 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..0cfd447620 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,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 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..a6b2687c58 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,19 @@
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..620dc31a6c 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,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 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..cf29212504 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,13 @@
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..4cf676acd9 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,12 @@
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..75fe13d292 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,13 @@
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..eea3b105c7 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,20 @@
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..5a0a5e9277 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,18 @@
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..87ca4e8f39 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,19 @@
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/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java
index 81cfa7fd07..4fa5f59d6d 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,19 @@
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..eac4a1d4f9 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,18 @@
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..a5cc049065 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,14 @@
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..db2fd5ab56 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,18 @@
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..f4f3e0bf3c 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,12 @@
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..b4f86bf701 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,15 @@
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..1f7ebe00c6 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,15 @@
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..5dad4d6d64 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,18 @@
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..d07e3daeff 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,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.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..344df7d66d 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,19 @@
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/ratelimit/RateLimitIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java
index 8b04fd0095..3f50fd334d 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,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 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..d746747186 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,13 @@
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..2835ab0353 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,20 @@
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..5f916f3b9d 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,20 @@
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/statuscache/StatusPatchCacheIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheIT.java
index 9d0b923056..b3ac2ba753 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,19 @@
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/statusupdatelocking/StatusUpdateLockingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java
index ad51d82059..56e2b9aa26 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,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 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..84293520ba 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,13 @@
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/dependent/bulkdependent/managed/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java
index 61b7f62a24..b9630182eb 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,18 @@
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..b6742f32c4 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,13 @@
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..a5bc6abbed 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,17 @@
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..f6d99df44f 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,19 @@
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..276cdb2214 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,19 @@
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/dependentdifferentnamespace/DependentDifferentNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java
index c02bac5a5d..dbffc4cdf6 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,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 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..4ea2a82ce4 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,15 @@
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..efc5fc467b 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,19 @@
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/dependentssa/DependentSSAMatchingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java
index 8ab686b1b8..0c44348006 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,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 (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/externalstate/ExternalStateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java
index bae36431b5..985bafd541 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,15 @@
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/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java
index 93fc34fbf0..7395dacbb6 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,18 @@
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/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java
index 8e08c4e739..941c43d067 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,22 @@
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..6552543f94 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,14 @@
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/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java
index a835b5a255..76d6a03f44 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,13 @@
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/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java
index 11a080a8bf..06155d761c 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,14 @@
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..426c6d7c72 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,19 @@
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..cb0345fc70 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,18 @@
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..cc57e92036 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,13 @@
* 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/standalonedependent/StandaloneDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java
index 91fbd64a19..796d6ce7e5 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,15 @@
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..5110b68c1c 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,18 @@
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..330705611b 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,12 @@
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/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java
index 482d2e5091..3e9c1c7b0e 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,19 @@
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..07b3aafdd7 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,19 @@
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..a89dce4ab2 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,18 @@
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..3bd0da8bc1 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,19 @@
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..c38e37cf84 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,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 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..1567ef13af 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,20 @@
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/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java
index eaa799300f..801d92bacf 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,19 @@
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/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 extends TypeElement> 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