Skip to content

docs: new structure of docs #2737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 21, 2025
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
2 changes: 1 addition & 1 deletion docs/content/en/blog/_index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Blog
menu: {main: {weight: 30}}
menu: {main: {weight: 2}}
---

This is the **blog** section. It has two categories: News and Releases.
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/blog/news/_index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
title: Posts
weight: 20
weight: 220
---
2 changes: 1 addition & 1 deletion docs/content/en/blog/releases/_index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
title: Releases
weight: 20
weight: 230
---
2 changes: 1 addition & 1 deletion docs/content/en/community/_index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Community
menu: {main: {weight: 40}}
menu: {main: {weight: 3}}
---

<!--add blocks of content here to add more sections to the community page -->
4 changes: 2 additions & 2 deletions docs/content/en/docs/_index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: Documentation
linkTitle: Docs
menu: {main: {weight: 20}}
weight: 20
menu: {main: {weight: 1}}
weight: 1
---


2 changes: 1 addition & 1 deletion docs/content/en/docs/contributing/_index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Contributing To Java Operator SDK
weight: 100
weight: 110
---

First of all, we'd like to thank you for considering contributing to the project! We really
Expand Down
4 changes: 4 additions & 0 deletions docs/content/en/docs/documentation/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: Documentation
weight: 40
---
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
---
title: Architecture and Internals
weight: 90
weight: 85
---


This document gives an overview of the internal structure and components of Java Operator SDK core,
in order to make it easier for developers to understand and contribute to it. This document is
not intended to be a comprehensive reference, rather an introduction to the core concepts and we
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
---
title: Configuring JOSDK
layout: docs
permalink: /docs/configuration
title: Configurations
weight: 55
---

# Configuration options

The Java Operator SDK (JOSDK) provides several abstractions that work great out of the
box. However, while we strive to cover the most common cases with the default behavior, we also
recognize that that default behavior is not always what any given user might want for their
Expand Down Expand Up @@ -52,6 +49,68 @@ operator.register(reconciler, configOverrider ->
configOverrider.withFinalizer("my-nifty-operator/finalizer").withLabelSelector("foo=bar"));
```

## Dynamically Changing Target Namespaces

A controller can be configured to watch a specific set of namespaces in addition of the
namespace in which it is currently deployed or the whole cluster. The framework supports
dynamically changing the list of these namespaces while the operator is running.
When a reconciler is registered, an instance of
[`RegisteredController`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ec37025a15046d8f409c77616110024bf32c3416/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java#L5)
is returned, providing access to the methods allowing users to change watched namespaces as the
operator is running.

A typical scenario would probably involve extracting the list of target namespaces from a
`ConfigMap` or some other input but this part is out of the scope of the framework since this is
use-case specific. For example, reacting to changes to a `ConfigMap` would probably involve
registering an associated `Informer` and then calling the `changeNamespaces` method on
`RegisteredController`.

```java

public static void main(String[] args) {
KubernetesClient client = new DefaultKubernetesClient();
Operator operator = new Operator(client);
RegisteredController registeredController = operator.register(new WebPageReconciler(client));
operator.installShutdownHook();
operator.start();

// call registeredController further while operator is running
}

```

If watched namespaces change for a controller, it might be desirable to propagate these changes to
`InformerEventSources` associated with the controller. In order to express this,
`InformerEventSource` implementations interested in following such changes need to be
configured appropriately so that the `followControllerNamespaceChanges` method returns `true`:

```java

@ControllerConfiguration
public class MyReconciler implements Reconciler<TestCustomResource> {

@Override
public Map<String, EventSource> prepareEventSources(
EventSourceContext<ChangeNamespaceTestCustomResource> context) {

InformerEventSource<ConfigMap, TestCustomResource> configMapES =
new InformerEventSource<>(InformerEventSourceConfiguration.from(ConfigMap.class, TestCustomResource.class)
.withNamespacesInheritedFromController(context)
.build(), context);

return EventSourceUtils.nameEventSources(configMapES);
}

}
```

As seen in the above code snippet, the informer will have the initial namespaces inherited from
controller, but also will adjust the target namespaces if it changes for the controller.

See also
the [integration test](https://github.com/operator-framework/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace)
for this feature.

## DependentResource-level configuration

`DependentResource` implementations can implement the `DependentResourceConfigurator` interface
Expand All @@ -61,7 +120,7 @@ provides specific support for the `KubernetesDependentResource`, which can be co
`KubernetesDependentResourceConfig` instance, which is then passed to the `configureWith` method
implementation.

TODO: still subject to change / uniformization
TODO

## EventSource-level configuration

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Dependent resources and workflows
weight: 70
---

Dependent resources and workflows are features sometimes referenced as higher
level abstractions. These two related concepts provides an abstraction
over reconciliation of a single resource (Dependent resource) and the
orchestration of such resources (Workflows).
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Dependent Resources
weight: 60
title: Dependent resources
weight: 75
---

## Motivations and Goals
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Workflows
weight: 70
weight: 80
---

## Overview
Expand Down
115 changes: 115 additions & 0 deletions docs/content/en/docs/documentation/error-handling-retries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
title: Error handling and retries
weight: 46
---

## Automatic Retries on Error

JOSDK will schedule an automatic retry of the reconciliation whenever an exception is thrown by
your `Reconciler`. The retry is behavior is configurable but a default implementation is provided
covering most of the typical use-cases, see
[GenericRetry](https://github.com/java-operator-sdk/java-operator-sdk/blob/master/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java)
.

```java
GenericRetry.defaultLimitedExponentialRetry()
.setInitialInterval(5000)
.setIntervalMultiplier(1.5D)
.setMaxAttempts(5);
```

You can also configure the default retry behavior using the `@GradualRetry` annotation.

It is possible to provide a custom implementation using the `retry` field of the
`@ControllerConfiguration` annotation and specifying the class of your custom implementation.
Note that this class will need to provide an accessible no-arg constructor for automated
instantiation. Additionally, your implementation can be automatically configured from an
annotation that you can provide by having your `Retry` implementation implement the
`AnnotationConfigurable` interface, parameterized with your annotation type. See the
`GenericRetry` implementation for more details.

Information about the current retry state is accessible from
the [Context](https://github.com/java-operator-sdk/java-operator-sdk/blob/master/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/Context.java)
object. Of note, particularly interesting is the `isLastAttempt` method, which could allow your
`Reconciler` to implement a different behavior based on this status, by setting an error message
in your resource' status, for example, when attempting a last retry.

Note, though, that reaching the retry limit won't prevent new events to be processed. New
reconciliations will happen for new events as usual. However, if an error also occurs that
would normally trigger a retry, the SDK won't schedule one at this point since the retry limit
is already reached.

A successful execution resets the retry state.

### Setting Error Status After Last Retry Attempt

In order to facilitate error reporting, `Reconciler` can implement the
[ErrorStatusHandler](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ErrorStatusHandler.java)
interface:

```java
public interface ErrorStatusHandler<P extends HasMetadata> {

ErrorStatusUpdateControl<P> updateErrorStatus(P resource, Context<P> context, Exception e);

}
```

The `updateErrorStatus` method is called in case an exception is thrown from the `Reconciler`. It is
also called even if no retry policy is configured, just after the reconciler execution.
`RetryInfo.getAttemptCount()` is zero after the first reconciliation attempt, since it is not a
result of a retry (regardless of whether a retry policy is configured or not).

`ErrorStatusUpdateControl` is used to tell the SDK what to do and how to perform the status
update on the primary resource, always performed as a status sub-resource request. Note that
this update request will also produce an event, and will result in a reconciliation if the
controller is not generation aware.

This feature is only available for the `reconcile` method of the `Reconciler` interface, since
there should not be updates to resource that have been marked for deletion.

Retry can be skipped in cases of unrecoverable errors:

```java
ErrorStatusUpdateControl.patchStatus(customResource).withNoRetry();
```

### Correctness and Automatic Retries

While it is possible to deactivate automatic retries, this is not desirable, unless for very
specific reasons. Errors naturally occur, whether it be transient network errors or conflicts
when a given resource is handled by a `Reconciler` but is modified at the same time by a user in
a different process. Automatic retries handle these cases nicely and will usually result in a
successful reconciliation.

## Retry and Rescheduling and Event Handling Common Behavior

Retry, reschedule and standard event processing form a relatively complex system, each of these
functionalities interacting with the others. In the following, we describe the interplay of
these features:

1. A successful execution resets a retry and the rescheduled executions which were present before
the reconciliation. However, a new rescheduling can be instructed from the reconciliation
outcome (`UpdateControl` or `DeleteControl`).

For example, if a reconciliation had previously been re-scheduled after some amount of time, but an event triggered
the reconciliation (or cleanup) in the mean time, the scheduled execution would be automatically cancelled, i.e.
re-scheduling a reconciliation does not guarantee that one will occur exactly at that time, it simply guarantees that
one reconciliation will occur at that time at the latest, triggering one if no event from the cluster triggered one.
Of course, it's always possible to re-schedule a new reconciliation at the end of that "automatic" reconciliation.

Similarly, if a retry was scheduled, any event from the cluster triggering a successful execution in the mean time
would cancel the scheduled retry (because there's now no point in retrying something that already succeeded)

2. In case an exception happened, a retry is initiated. However, if an event is received
meanwhile, it will be reconciled instantly, and this execution won't count as a retry attempt.
3. If the retry limit is reached (so no more automatic retry would happen), but a new event
received, the reconciliation will still happen, but won't reset the retry, and will still be
marked as the last attempt in the retry info. The point (1) still holds, but in case of an
error, no retry will happen.

The thing to keep in mind when it comes to retrying or rescheduling is that JOSDK tries to avoid unnecessary work. When
you reschedule an operation, you instruct JOSDK to perform that operation at the latest by the end of the rescheduling
delay. If something occurred on the cluster that triggers that particular operation (reconciliation or cleanup), then
JOSDK considers that there's no point in attempting that operation again at the end of the specified delay since there
is now no point to do so anymore. The same idea also applies to retries.
Loading