Skip to content

refactor: Extract build and apply steps in the zk- and znode-controller - #1069

Open
maltesander wants to merge 5 commits into
mainfrom
refactor/extract-apply-and-status-steps
Open

refactor: Extract build and apply steps in the zk- and znode-controller#1069
maltesander wants to merge 5 commits into
mainfrom
refactor/extract-apply-and-status-steps

Conversation

@maltesander

@maltesander maltesander commented Aug 5, 2026

Copy link
Copy Markdown
Member

Description

Brings the ZooKeeper operator in line with the apply and update status steps merged in airflow-operator#828 and hbase-operator#787, with the opensearch-operator as the target shape.

Both controllers now run an explicit pipeline, with each step in its own submodule:

  • ZookeeperCluster:apply, update_status
  • ZookeeperZnode: build, apply

Behavioural changes

  • The ZookeeperCluster controller watches Listeners, and the operator ClusterRole gains the watch verb on listeners.stackable.tech, which it did not have.
  • A Listener without ingress addresses is no longer an error for the cluster controller (the discovery ConfigMap is simply skipped until the next reconciliation). It remains an error for the znode controller.

Tests

--- PASS: kuttl (218.72s)
    --- PASS: kuttl/harness (0.00s)
        --- PASS: kuttl/harness/cluster-operation_zookeeper-latest-3.9.5_openshift-false (45.76s)
        --- PASS: kuttl/harness/smoke_zookeeper-3.9.5_use-server-tls-false_use-client-auth-tls-true_openshift-false (93.24s)
        --- PASS: kuttl/harness/logging_zookeeper-3.9.5_openshift-false (60.66s)
        --- PASS: kuttl/harness/smoke_zookeeper-3.9.5_use-server-tls-false_use-client-auth-tls-false_openshift-false (50.81s)
        --- PASS: kuttl/harness/smoke_zookeeper-3.9.5_use-server-tls-true_use-client-auth-tls-true_openshift-false (55.86s)
        --- PASS: kuttl/harness/znode_zookeeper-latest-3.9.5_openshift-false (22.54s)
        --- PASS: kuttl/harness/delete-rolegroup_zookeeper-3.9.5_openshift-false (31.13s)
        --- PASS: kuttl/harness/smoke_zookeeper-3.9.5_use-server-tls-true_use-client-auth-tls-false_openshift-false (56.42s)

Definition of Done Checklist

  • Not all of these items are applicable to all PRs, the author should update this template to only leave the boxes in that are relevant
  • Please make sure all these things are done and tick the boxes

Author

  • Changes are OpenShift compatible
  • CRD changes approved
  • CRD documentation for all fields, following the style guide.
  • Helm chart can be installed and deployed operator works
  • Integration tests passed (for non trivial changes)
  • Changes need to be "offline" compatible
  • Links to generated (nightly) docs added
  • Release note snippet added

Reviewer

  • Code contains useful comments
  • Code contains useful logging statements
  • (Integration-)Test cases added
  • Documentation added or updated. Follows the style guide.
  • Changelog updated
  • Cargo.toml only contains references to git tags (not specific commits or branches)

Acceptance

  • Feature Tracker has been updated
  • Proper release label has been added
  • Links to generated (nightly) docs added
  • Release note snippet added
  • Add type/deprecation label & add to the deprecation schedule
  • Add type/experimental label & add to the experimental features tracker

The discovery ConfigMap advertises the addresses that the listener
operator publishes on the ZooKeeper role Listener, so it used to be
built and applied inline in reconcile_zk, from the Listener that had
just been applied. That Listener carries no addresses yet on the first
reconciliation, so the controller relied on the reconciliation failing
and being requeued five seconds later.

Follow the pattern the OpenSearch operator already uses: dereference the
role Listener, validate its addresses into the ValidatedCluster, and let
build() emit an Option<ConfigMap>. The controller now watches Listeners,
so the reconciliation that writes the discovery ConfigMap is triggered as
soon as the addresses appear, and it also reruns when they change.

The listener address extraction moves into a shared listener_addresses
module, because the ZookeeperZnode controller needs it too.

The operator ClusterRole gains the watch verb on listeners, which is
required for the new watch.
reconcile_zk built the ClusterResources itself and applied every
collection with its own inline loop, so the driver carried the apply
order, the orphan deletion and the resource specific error variants.

Extract all of that into an Applier, following the airflow and hbase
operators. KubernetesResources is now marked as either Prepared or
Applied, which makes it impossible to derive the cluster status from
resources that were only built. apply() destructures the resource set
exhaustively, so a new field fails to compile here instead of silently
never being applied.

Unlike the sibling operators, the apply module is declared in
zk_controller.rs itself, so the Applier is imported without `self` to
avoid a name collision with the module declaration.
The cluster conditions and the discovery hash were computed inline at
the end of reconcile_zk, from resources that happened to be in scope.

Move both into an update_status step, following the airflow and hbase
operators. It takes KubernetesResources<Applied>, so the type system
proves the status is derived from resources that were actually applied
rather than merely built. The discovery hash, which the sibling
operators do not have, becomes a private helper next to it.

reconcile_zk is now the dereference, validate, build, apply and
update_status pipeline and nothing else.
reconcile_apply created the ClusterResources, talked to ZooKeeper,
fetched the role Listener, built the discovery ConfigMap and applied it,
all inline, so the ZookeeperZnode controller was the only one left
without a pipeline.

Give it the same dereference, validate, build and apply structure as the
ZookeeperCluster controller. The Listener fetch moves into the
dereference step, where a missing Listener stays a non error so it can
never block finalizer removal, and its addresses become a validated
field on ValidatedZnode. Creating the znode inside the ZooKeeper
ensemble lives in the apply step as a free function, next to the
Applier, because it is a client side effect that the client free build()
cannot perform.

The discovery ConfigMap builders move to a shared discovery module, so
the znode controller no longer reaches into the cluster controller's
build tree.

Unlike the cluster controller, the resources carry no Prepared or
Applied marker: the ZookeeperZnode has no cluster conditions, so there
is no status step the marker could protect.
@maltesander maltesander changed the title refactor: Extract build and apply steps in the znode controller refactor: Extract build and apply steps in the zk- and znode-controller Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant