fix-1828: started work - #2280
Conversation
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
|
@ryanjbaxter can you trigger copilot in here please? |
| } | ||
|
|
||
| private boolean isDeploymentReady(String deploymentName, String namespace) throws ApiException { | ||
| private boolean isDeploymentReady(String deploymentName, String namespace, int expectedReplicas) |
There was a problem hiding this comment.
in the new IT that I added, there is a need for two replicas, to really test the HA set-up
Signed-off-by: wind57 <eugen.rabii@gmail.com>
| envVars.add(new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_SECRETS_ENABLED").value("TRUE")); | ||
|
|
||
| if (enableHa) { | ||
| envVars.add(new V1EnvVar().name("SPRING_CLOUD_KUBERNETES_LEADER_ELECTION_ENABLED").value("true")); |
There was a problem hiding this comment.
two properties are needed to enable HA
| } | ||
|
|
||
| /** | ||
| * <pre> |
There was a problem hiding this comment.
I've added a single IT, that goes through a cycle of leader / no leader / leader
| * | ||
| * @author wind57 | ||
| */ | ||
| sealed interface ConfigurationWatcherStateStore permits LeaseConfigurationWatcherStateStore { |
There was a problem hiding this comment.
this is the definition of the resource version store. Methods in this one are executed only by the leader
Signed-off-by: wind57 <eugen.rabii@gmail.com>
Signed-off-by: wind57 <eugen.rabii@gmail.com>
| } | ||
|
|
||
| @Override | ||
| public ConfigurationWatcherState readOrCreate() { |
There was a problem hiding this comment.
if HA is enabled and this is the first call, this will create an empty lease. Otherwise, it will read whatever is stored there.
We store in the spring.cloud.kubernetes.configuration.watcher/configmap-resource-version annotation, something like : "default=123,prod=456", so each namespace tracks its own resource version checkpoint.
We need such a store because during a downtime when there is no leader at all, resource version can progress ( meaning configmap is updated ), but since there is no leader, events can get lost. As such, we always increment and "store" ( via this implementation ) the most recent resource version we have observed. This happens in the handlers onAdd / onDelete / onUpdate.
So for example:
- we are now the leader and the resourceVersion is at
1. - we lose leadership, so our store stays at
1. - configmap progresses to
resourceVersion=2 - another leader is elected, it reads the store, sees that it holds
1 - starts the informer at
resourceVersion=1, so it can replay events it has not seen
| } | ||
|
|
||
| @Override | ||
| public void onApplicationEvent(ApplicationEvent event) { |
There was a problem hiding this comment.
these events are triggered only when the instance became the leader
| LOG.debug(() -> "Secret " + secret.getMetadata().getName() + " was deleted in namespace " | ||
| + secret.getMetadata().getNamespace()); | ||
| onEvent.accept(secret); | ||
| writeResourceVersion(secret); |
There was a problem hiding this comment.
after every update that we receive in the handler, write the resource version to the store
Signed-off-by: wind57 <eugen.rabii@gmail.com>
| } | ||
| } | ||
|
|
||
| public final void start(Map<String, String> storedResourceVersions, |
There was a problem hiding this comment.
With HA enabled:
-
@PostConstruct does nothing, so the informers are not started.
-
The native leader-election callback emits
StartLeadingEvent. -
ConfigurationWatcherHACoordinatorreceives that event. -
It calls
stateStore.readOrCreate()- If the HA state Lease does not exist, it creates it and returns an empty state.
- Otherwise, it reads the stored
ConfigMapandSecretresource versions.
-
The coordinator calls
start(...)on the available detectors, passing:- the stored resource versions
- a writer that persists future resource versions
-
Each detector creates its
InformerResourceVersionResolver. -
For each watched namespace, the informer sends its initial
LISTrequest:- if a stored checkpoint exists, the request starts from that resource version
- otherwise, it starts normally
-
The informer then starts its
WATCH. -
Resource events are handled normally:
- ConfigMap or Secret change is processed
- refresh or bus notification is triggered
- the processed resource version is written to the HA state Lease
-
Later
LIST/WATCHrequests use the resource version supplied by the informer, not the stored checkpoint. -
If the leader loses leadership, StopLeadingEvent is emitted.
-
The coordinator stops both detectors.
-
The next leader reads the persisted state and repeats the process from the stored checkpoints.
TL;DRThis PR adds high-availability support to the Kubernetes client-based Configuration Watcher. The Configuration Watcher can now run with multiple replicas while ensuring that only one replica actively watches ConfigMaps and Secrets at a time. If the leader fails, another replica acquires leadership, restores the persisted informer checkpoints, and continues processing changes. How It WorksHA requires both properties to be enabled: The two properties have separate responsibilities:
When HA is disabled, the existing behavior is unchanged. ConfigMap and Secret informers start during normal bean initialization. When HA is enabled:
The HA coordinator is initialized before the leader-election callbacks so that it is ready to receive leadership events. Persistent State The Configuration Watcher stores its state in a Kubernetes Lease. The leader-election lock and the Configuration Watcher state lease are separate resources. The state lease stores the last processed resource version independently for:
The values are stored in annotations on the lease. For example: The default configuration is: The lease is created automatically when the first leader starts, if it does not already exist. Resource-Version Replay
Repeated refresh notifications must therefore be tolerated by refresh targets.
|
|
@ryanjbaxter in case you missed this... can you trigger copilot please? |
|
Have no missed it, just need to set aside some time to look at it. Probably next week |
|
ok, thank you! as usual, Im available on any ways to answer any questions. |
There was a problem hiding this comment.
Pull request overview
Adds High Availability (HA) support for the Kubernetes-client Configuration Watcher by deferring informer startup until leadership is acquired, persisting last-processed informer resourceVersions in a Kubernetes Lease, and replaying missed events after leader loss. This extends the configuration-watcher controller with HA auto-configuration and introduces new integration/unit tests to validate lease-backed checkpointing and leader failover behavior.
Changes:
- Introduce HA coordinator + lease-backed state store to persist and restore informer checkpoints (ConfigMap/Secret resourceVersions).
- Update ConfigMap/Secret event-based change detectors to support HA mode (deferred start, explicit start/stop, checkpoint replay).
- Add/adjust integration and unit tests plus documentation for HA enablement, RBAC, and deployment replicas.
Reviewed changes
Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/NativeClientKubernetesFixture.java | Add HA env vars + configurable replicas; wait for expected replica count. |
| spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/k3s/NativeClientIntegrationTestExtension.java | Wire scenario HA flags/replicas into fixture calls. |
| spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/k3s/NativeClientIntegrationTest.java | Extend test annotation with HA + replicas options. |
| spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientConfigMapLabelEventTriggeredIT.java | Update asserted log message for informer startup. |
| spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientConfigMapEventTriggeredIT.java | Update asserted log message for informer startup. |
| spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/KubernetesClientConfigurationWatcherHaIT.java | New k3s-based HA integration test (leader lease + replay). |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/LeaseConfigurationWatcherStateStoreTests.java | New unit tests for lease read/create/update failure modes. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherHACoordinatorTests.java | New unit tests for leader start/stop wiring and validation. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherHAAutoConfigurationTests.java | New auto-config tests for HA coordinator conditional creation. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/test/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigurationWatcherConfigurationPropertiesTests.java | Verify new HA properties defaults + binding. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | Register HA auto-configuration. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/SecretsWatcherChangeDetector.java | Pass HA enabled flag into base change detector. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/LeaseConfigurationWatcherStateStore.java | New lease-backed state store implementation. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherStateStore.java | New state store interface. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherState.java | New state record (per-namespace checkpoints). |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherHaProperties.java | New HA properties (enabled, lease name/namespace). |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherHACoordinator.java | New coordinator listening to leader election events. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConfigurationWatcherHAAutoConfiguration.java | New HA auto-config (store + coordinator beans). |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/ConditionalOnConfigurationWatcherHAEnabled.java | New conditional annotation for HA enablement. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigurationWatcherConfigurationProperties.java | Add HA properties to watcher configuration properties. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ConfigMapWatcherChangeDetector.java | Pass HA enabled flag into base change detector. |
| spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/pom.xml | Add leader-election dependency for HA support. |
| spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetectorTests.java | Expand tests for HA deferred start + resourceVersion behavior. |
| spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetectorTests.java | Expand tests for HA deferred start + resourceVersion behavior. |
| spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload/InformerResourceVersionResolverTests.java | New unit tests for resolver semantics in HA/non-HA. |
| spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/SecretResourceEventHandler.java | Add optional resourceVersion writer callback. |
| spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/NamespaceAndResourceVersion.java | New value record for persisted checkpoints. |
| spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java | Add HA mode: deferred informers + explicit start/stop + replay logic. |
| spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java | Add HA mode: deferred informers + explicit start/stop + replay logic. |
| spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/InformerResourceVersionResolver.java | New helper to consume checkpoints once per namespace. |
| spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/ConfigMapResourceEventHandler.java | Add optional resourceVersion writer callback. |
| docs/modules/ROOT/pages/spring-cloud-kubernetes-configuration-watcher.adoc | Document HA enablement, leases, RBAC, and replicas. |
Suppressed comments (2)
spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java:121
- Log message mentions "configmap informer" in the Secrets change detector; should refer to secret informers to avoid confusion in HA mode.
LOG.info(() -> "config watcher HA is enabled : deferring configmap informer startup "
+ "until leadership is acquired");
spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configuration-watcher/src/main/java/org/springframework/cloud/kubernetes/configuration/watcher/ha/LeaseConfigurationWatcherStateStore.java:166
- parseResourceVersions() throws "Invalid ConfigMap resource version entry" even when parsing the Secret annotation, which can mislead debugging. The message should be resource-type agnostic.
throw new IllegalStateException("Invalid ConfigMap resource version entry: " + entry);
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Signed-off-by: wind57 <eugen.rabii@gmail.com>
No description provided.