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
2 changes: 1 addition & 1 deletion docs/content/en/docs/documentation/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This section contains detailed documentation for all Java Operator SDK features

- **[Eventing](eventing/)** - Understanding the event-driven model
- **[Accessing Resources in Caches](working-with-es-caches/)** - How to access resources in caches
- **[Observability](observability/)** - Monitoring and debugging your operators
- **[Operations](operations/)** - Helm chart, metrics, logging, configurations, leader election
- **[Other Features](features/)** - Additional capabilities and integrations

Each guide includes practical examples and best practices to help you build robust, production-ready operators.
2 changes: 1 addition & 1 deletion docs/content/en/docs/documentation/architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Architecture and Internals
weight: 85
weight: 90
---

This document provides an overview of the Java Operator SDK's internal structure and components to help developers understand and contribute to the project. While not a comprehensive reference, it introduces core concepts that should make other components easier to understand.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Dependent resources and workflows
weight: 70
weight: 80
---

Dependent resources and workflows are features sometimes referenced as higher
Expand Down
8 changes: 0 additions & 8 deletions docs/content/en/docs/documentation/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ public class DeploymentReconciler
}
```

## Leader Election

Operators are typically deployed with a single active instance. However, you can deploy multiple instances where only one (the "leader") processes events. This is achieved through "leader election."

While all instances run and start their event sources to populate caches, only the leader processes events. If the leader crashes, other instances are already warmed up and ready to take over when a new leader is elected.

See sample configuration in the [E2E test](https://github.com/java-operator-sdk/java-operator-sdk/blob/8865302ac0346ee31f2d7b348997ec2913d5922b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java#L21-L23).

## Automatic CRD Generation

**Note:** This feature is provided by the [Fabric8 Kubernetes Client](https://github.com/fabric8io/kubernetes-client), not JOSDK itself.
Expand Down
6 changes: 6 additions & 0 deletions docs/content/en/docs/documentation/operations/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Operations
weight: 70
---

This section covers operations-related features for running and managing operators in production.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Configurations
weight: 55
weight: 71
---

The Java Operator SDK (JOSDK) provides abstractions that work great out of the box. However, we recognize that default behavior isn't always suitable for every use case. Numerous configuration options help you tailor the framework to your specific needs.
Expand Down
116 changes: 116 additions & 0 deletions docs/content/en/docs/documentation/operations/helm-chart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
title: Generic Helm Chart
weight: 76
---

A generic, reusable Helm chart for deploying Java operators built with JOSDK is available at
[`helm/generic-helm-chart`](https://github.com/java-operator-sdk/java-operator-sdk/tree/main/helm/generic-helm-chart).

It is intended as a **template for operator developers** — a starting point that covers common deployment
patterns so you don't have to write a chart from scratch. The chart is maintained on a **best-effort basis**.
Contributions are more than welcome.

The chart is used in the
[`metrics-processing` sample operator E2E test](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/metrics-processing/src/test/java/io/javaoperatorsdk/operator/sample/metrics/MetricsHandlingE2E.java)
to deploy the operator to a cluster via Helm.

## What the Chart Provides

- **Deployment** with security defaults (non-root user, read-only filesystem, no privilege escalation)
- **Dynamic RBAC** (ClusterRole, ClusterRoleBinding, ServiceAccount) — permissions are generated automatically
from the primary and secondary resources you declare in `values.yaml`
- **ConfigMap** for operator configuration (`config.yaml`) and logging (`log4j2.xml`), mounted at `/config`
- **Leader election** support (opt-in)
- **Extensibility** via extra containers, init containers, volumes, and environment variables

## Key Configuration

The most important values to set when adapting the chart for your operator:

```yaml
image:
repository: my-operator-image # required
tag: "latest"

# Custom resources your operator reconciles
primaryResources:
- apiGroup: "sample.javaoperatorsdk"
resources:
- myresources

# Kubernetes resources your operator manages
secondaryResources:
- apiGroup: ""
resources:
- configmaps
- services
```

Primary resources get read/watch/patch permissions and status sub-resource access.
Secondary resources get full CRUD permissions. Default verbs can be overridden per resource entry.

### Operator Environment

The chart injects `OPERATOR_NAMESPACE` automatically. You can optionally set `WATCH_NAMESPACE` to
restrict the operator to a single namespace, and add arbitrary environment variables:

```yaml
operator:
watchNamespace: "" # empty = all namespaces
env:
- name: MY_CUSTOM_VAR
value: "some-value"
```

### Resource Defaults

```yaml
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
```

See the full
[`values.yaml`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/helm/generic-helm-chart/values.yaml)
for all available options.

## Usage Example

A working example of how to use the chart can be found in the metrics-processing sample operator's
[`helm-values.yaml`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/metrics-processing/src/test/resources/helm-values.yaml):

```yaml
image:
repository: metrics-processing-operator
pullPolicy: Never
tag: "latest"

nameOverride: "metrics-processing-operator"

resources: {}

primaryResources:
- apiGroup: "sample.javaoperatorsdk"
resources:
- metricshandlingcustomresource1s
- metricshandlingcustomresource2s
```

Install with:

```shell
helm install my-operator ./helm/generic-helm-chart -f my-values.yaml --namespace my-ns
```

## Testing the Chart

The chart includes unit tests using the [helm-unittest](https://github.com/helm-unittest/helm-unittest) plugin.
Run them with:

```shell
./helm/run-tests.sh
```
72 changes: 72 additions & 0 deletions docs/content/en/docs/documentation/operations/leader-election.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: Leader Election
weight: 74
---

When running multiple replicas of an operator for high availability, leader election ensures that
only one instance actively reconciles resources at a time. JOSDK uses Kubernetes
[Lease](https://kubernetes.io/docs/concepts/architecture/leases/) objects for leader election.

## Enabling Leader Election

### Programmatic Configuration

```java
var operator = new Operator(o -> o.withLeaderElectionConfiguration(
new LeaderElectionConfiguration("my-operator-lease", "operator-namespace")));
```

Or using the builder for full control:

```java
import static io.javaoperatorsdk.operator.api.config.LeaderElectionConfigurationBuilder.aLeaderElectionConfiguration;

var config = aLeaderElectionConfiguration("my-operator-lease")
.withLeaseNamespace("operator-namespace")
.withIdentity(System.getenv("POD_NAME"))
.withLeaseDuration(Duration.ofSeconds(15))
.withRenewDeadline(Duration.ofSeconds(10))
.withRetryPeriod(Duration.ofSeconds(2))
.build();

var operator = new Operator(o -> o.withLeaderElectionConfiguration(config));
```

### External Configuration

Leader election can also be configured via properties (e.g. environment variables or a config file).

See details under [configurations](configuration.md) page.

## How It Works

1. When leader election is enabled, the operator starts but **does not process events** until it acquires
the lease.
2. Once leadership is acquired, event processing begins normally.
3. If leadership is lost (e.g. the leader pod becomes unresponsive), another instance acquires the lease
and takes over reconciliation. The instance that lost the lead is terminated (`System.exit()`)

### Identity and Namespace Inference

If not explicitly set:
- **Identity** is resolved from the `HOSTNAME` environment variable, then the pod name, falling back to a
random UUID.
- **Lease namespace** defaults to the namespace the operator pod is running in.

## RBAC Requirements

The operator's service account needs permissions to manage Lease objects:

```yaml
- apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
verbs: ["create", "update", "get"]
```

JOSDK checks for these permissions at startup and throws a clear error if they are missing.

## Sample E2E Test

A complete working example is available in the
[`leader-election` sample operator](https://github.com/java-operator-sdk/java-operator-sdk/tree/main/sample-operators/leader-election),
including multi-replica deployment manifests and an E2E test that verifies failover behavior.
51 changes: 51 additions & 0 deletions docs/content/en/docs/documentation/operations/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: Logging
weight: 72
---

## Contextual Info for Logging with MDC

Logging is enhanced with additional contextual information using
[MDC](http://www.slf4j.org/manual.html#mdc). The following attributes are available in most
parts of reconciliation logic and during the execution of the controller:

| MDC Key | Value added from primary resource |
|:---------------------------|:----------------------------------|
| `resource.apiVersion` | `.apiVersion` |
| `resource.kind` | `.kind` |
| `resource.name` | `.metadata.name` |
| `resource.namespace` | `.metadata.namespace` |
| `resource.resourceVersion` | `.metadata.resourceVersion` |
| `resource.generation` | `.metadata.generation` |
| `resource.uid` | `.metadata.uid` |
Comment on lines +12 to +20
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table rows start with || instead of a single |. Many Markdown renderers (and Hugo Markdown configurations) expect | ... | ... | and may not render this as a table. Use a single leading pipe per row (e.g., | MDC Key | ... |) for consistent rendering.

Copilot uses AI. Check for mistakes.

For more information about MDC see this [link](https://www.baeldung.com/mdc-in-log4j-2-logback).

### MDC entries during event handling

Although, usually users might not require it in their day-to-day workflow, it is worth mentioning that
there are additional MDC entries managed for event handling. Typically, you might be interested in it
in your `SecondaryToPrimaryMapper` related logs.
For `InformerEventSource` and `ControllerEventSource` the following information is present:

| MDC Key | Value from Resource from the Event |
|:-----------------------------------------------|:-------------------------------------------------|
| `eventsource.event.resource.name` | `.metadata.name` |
| `eventsource.event.resource.uid` | `.metadata.uid` |
| `eventsource.event.resource.namespace` | `.metadata.namespace` |
| `eventsource.event.resource.kind` | resource kind |
| `eventsource.event.resource.resourceVersion` | `.metadata.resourceVersion` |
| `eventsource.event.action` | action name (e.g. `ADDED`, `UPDATED`, `DELETED`) |
| `eventsource.name` | name of the event source |
Comment on lines +31 to +39
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: || is non-standard for Markdown tables and may break rendering. Switch these rows to start with a single |.

Copilot uses AI. Check for mistakes.

### Note on null values

If a resource doesn't provide values for one of the specified keys, the key will be omitted and not added to the MDC
context. There is, however, one notable exception: the resource's namespace, where, instead of omitting the key, we emit
the `MDCUtils.NO_NAMESPACE` value instead. This allows searching for resources without namespace (notably, clustered
resources) in the logs more easily.

### Disabling MDC support

MDC support is enabled by default. If you want to disable it, you can set the `JAVA_OPERATOR_SDK_USE_MDC` environment
variable to `false` when you start your operator.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Observability
weight: 55
title: Metrics
weight: 73
---

## Runtime Info
Expand All @@ -15,53 +15,6 @@ setting, where this flag usually needs to be set to false, in order to control t
See also an example implementation in the
[WebPage sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/3e2e7c4c834ef1c409d636156b988125744ca911/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java#L38-L43)

## Contextual Info for Logging with MDC

Logging is enhanced with additional contextual information using
[MDC](http://www.slf4j.org/manual.html#mdc). The following attributes are available in most
parts of reconciliation logic and during the execution of the controller:

| MDC Key | Value added from primary resource |
|:---------------------------|:----------------------------------|
| `resource.apiVersion` | `.apiVersion` |
| `resource.kind` | `.kind` |
| `resource.name` | `.metadata.name` |
| `resource.namespace` | `.metadata.namespace` |
| `resource.resourceVersion` | `.metadata.resourceVersion` |
| `resource.generation` | `.metadata.generation` |
| `resource.uid` | `.metadata.uid` |

For more information about MDC see this [link](https://www.baeldung.com/mdc-in-log4j-2-logback).

### MDC entries during event handling

Although, usually users might not require it in their day-to-day workflow, it is worth mentioning that
there are additional MDC entries managed for event handling. Typically, you might be interested in it
in your `SecondaryToPrimaryMapper` related logs.
For `InformerEventSource` and `ControllerEventSource` the following information is present:

| MDC Key | Value from Resource from the Event |
|:-----------------------------------------------|:-------------------------------------------------|
| `eventsource.event.resource.name` | `.metadata.name` |
| `eventsource.event.resource.uid` | `.metadata.uid` |
| `eventsource.event.resource.namespace` | `.metadata.namespace` |
| `eventsource.event.resource.kind` | resource kind |
| `eventsource.event.resource.resourceVersion` | `.metadata.resourceVersion` |
| `eventsource.event.action` | action name (e.g. `ADDED`, `UPDATED`, `DELETED`) |
| `eventsource.name` | name of the event source |

### Note on null values

If a resource doesn't provide values for one of the specified keys, the key will be omitted and not added to the MDC
context. There is, however, one notable exception: the resource's namespace, where, instead of omitting the key, we emit
the `MDCUtils.NO_NAMESPACE` value instead. This allows searching for resources without namespace (notably, clustered
resources) in the logs more easily.

### Disabling MDC support

MDC support is enabled by default. If you want to disable it, you can set the `JAVA_OPERATOR_SDK_USE_MDC` environment
variable to `false` when you start your operator.

## Metrics

JOSDK provides built-in support for metrics reporting on what is happening with your reconcilers in the form of
Expand All @@ -77,9 +30,9 @@ Metrics metrics; // initialize your metrics implementation
Operator operator = new Operator(client, o -> o.withMetrics(metrics));
```

### MicrometerMetricsV2
### MicrometerMetricsV2

[`MicrometerMetricsV2`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetricsV2.java)
[`MicrometerMetricsV2`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetricsV2.java)
is the recommended micrometer-based implementation. It is designed with low cardinality in mind:
all meters are scoped to the controller, not to individual resources. This avoids unbounded cardinality growth as
resources come and go.
Expand Down Expand Up @@ -230,8 +183,8 @@ Metrics loggingMetrics = new LoggingMetrics();

// combine them into a single aggregated instance
Metrics aggregatedMetrics = new AggregatedMetrics(List.of(
micrometerMetrics,
customMetrics,
micrometerMetrics,
customMetrics,
loggingMetrics
));

Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/docs/faq/_index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: FAQ
weight: 90
weight: 100
---

## Events and Reconciliation
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/docs/glossary/_index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Glossary
weight: 100
weight: 150
---

- **Primary Resource** - The resource representing the desired state that the controller works to achieve. While often a Custom Resource, it can also be a native Kubernetes resource (Deployment, ConfigMap, etc.).
Expand Down
Loading