Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 68 additions & 0 deletions operator-framework/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@
<artifactId>kube-api-test-client-inject</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>test-index-processor</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
</dependencies>

<build>
Expand All @@ -106,6 +113,23 @@
</compilerArgs>
</configuration>
</execution>
<!-- Enable annotation processing for test compilation to generate samples.md -->
<execution>
<id>default-testCompile</id>
<goals>
<goal>testCompile</goal>
</goals>
<phase>test-compile</phase>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>test-index-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
Expand Down Expand Up @@ -138,6 +162,50 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-samples-to-docs</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<outputDirectory>${project.basedir}/../docs/content/en/docs/testindex</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}/generated-test-sources/test-annotations</directory>
<includes>
<include>samples.md</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>rename-samples-to-index</id>
<goals>
<goal>run</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<target>
<move failonerror="false" file="${project.basedir}/../docs/content/en/docs/testindex/samples.md" tofile="${project.basedir}/../docs/content/en/docs/testindex/_index.md"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Kind;
import io.fabric8.kubernetes.model.annotation.Version;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
Expand All @@ -21,6 +22,15 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Custom CRD Mapping in Test Extension",
description =
"""
Demonstrates how to manually specify and apply Custom Resource Definitions (CRDs) in \
integration tests using the LocallyRunOperatorExtension. This test verifies that CRDs \
can be loaded from specified file paths and properly registered with the Kubernetes API \
server during test execution.
""")
public class CRDMappingInTestExtensionIT {
private final KubernetesClient client = new KubernetesClientBuilder().build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,6 +19,15 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Concurrent Reconciliation of Multiple Resources",
description =
"""
Demonstrates the operator's ability to handle concurrent reconciliation of multiple \
resources. The test creates, updates, and deletes many resources simultaneously to \
verify proper handling of concurrent operations, ensuring thread safety and correct \
resource state management under load.
""")
class ConcurrencyIT {
public static final int NUMBER_OF_RESOURCES_CREATED = 50;
public static final int NUMBER_OF_RESOURCES_DELETED = 30;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.Operator;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;

@Sample(
tldr = "Operator Startup with Informer Errors",
description =
"""
Demonstrates that the operator can start successfully even when informers encounter \
errors during startup, such as insufficient access rights. By setting \
stopOnInformerErrorDuringStartup to false, the operator gracefully handles permission \
errors and continues initialization, allowing it to operate with partial access.
""")
class InformerErrorHandlerStartIT {
/** Test showcases that the operator starts even if there is no access right for some resource. */
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.Operator;
import io.javaoperatorsdk.operator.OperatorException;
import io.javaoperatorsdk.operator.ReconcilerUtils;
Expand All @@ -21,6 +22,15 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

@Sample(
tldr = "Leader Election with Insufficient Permissions",
description =
"""
Verifies that the operator fails gracefully when leader election is configured but \
the service account lacks permissions to access lease resources. This test ensures \
proper error handling and messaging when RBAC permissions are insufficient for \
leader election functionality.
""")
class LeaderElectionPermissionIT {

KubernetesClient adminClient = new KubernetesClientBuilder().build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@
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;

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@
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;

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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Implementing Cleanup Logic with Cleaner Interface",
description =
"""
Demonstrates how to implement cleanup logic for custom resources using the Cleaner \
interface. When a reconciler implements Cleaner, the framework automatically adds a \
finalizer to resources and calls the cleanup method when the resource is deleted. \
This pattern is useful for cleaning up external resources or performing custom \
deletion logic. The test verifies finalizer handling, cleanup execution, and the \
ability to reschedule cleanup operations.
""")
class CleanerForReconcilerIT {

public static final String TEST_RESOURCE_NAME = "cleaner-for-reconciler-test1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;

import static io.javaoperatorsdk.operator.baseapi.cleanupconflict.CleanupConflictReconciler.WAIT_TIME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Cleanup Finalizer Removal Without Conflicts",
description =
"""
Tests that finalizers are removed correctly during cleanup without causing conflicts, \
even when multiple finalizers are present and removed concurrently. This verifies the \
operator's ability to handle finalizer updates safely during resource deletion.
""")
class CleanupConflictIT {

private static final String ADDITIONAL_FINALIZER = "javaoperatorsdk.io/additionalfinalizer";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;

import static io.javaoperatorsdk.operator.IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Cluster-scoped resource reconciliation",
description =
"""
Demonstrates how to reconcile cluster-scoped custom resources (non-namespaced). This \
test shows CRUD operations on cluster-scoped resources and verifies that \
dependent resources are created, updated, and properly cleaned up when the \
primary resource is deleted.
""")
class ClusterScopedResourceIT {

public static final String TEST_NAME = "test1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@
import org.slf4j.LoggerFactory;

import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Concurrent Finalizer Removal by Multiple Reconcilers",
description =
"""
Demonstrates safe concurrent finalizer removal when multiple reconcilers manage the \
same resource with different finalizers. Tests that finalizers can be removed \
concurrently without conflicts or race conditions, ensuring proper cleanup even when \
multiple controllers are involved.
""")
class ConcurrentFinalizerRemovalIT {

private static final Logger log = LoggerFactory.getLogger(ConcurrentFinalizerRemovalIT.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;

import static io.javaoperatorsdk.operator.baseapi.createupdateeventfilter.CreateUpdateEventFilterTestReconciler.CONFIG_MAP_TEST_DATA_KEY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

@Sample(
tldr = "Event filtering for create and update operations",
description =
"""
Shows how to configure event filters on informer event sources to control which create and \
update events trigger reconciliation. This is useful for preventing unnecessary \
reconciliation loops when dependent resources are modified by the controller \
itself.
""")
class CreateUpdateInformerEventSourceEventFilterIT {

@RegisterExtension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.javaoperatorsdk.annotation.Sample;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;

@Sample(
tldr = "Event Filtering with Previous Annotation Disabled",
description =
"""
Tests event filtering behavior when the previous annotation feature for dependent \
resources is disabled. Verifies that update events are properly received and handled \
even without the annotation tracking mechanism that compares previous resource states.
""")
class PreviousAnnotationDisabledIT {

@RegisterExtension
Expand Down
Loading