Skip to content

refactor: adapt hdfs-operator to the rewritten operator-go framework - #319

Merged
whg517 merged 49 commits into
zncdatadev:mainfrom
whg517:refactor/operator-go-rewrite
Sep 14, 2026
Merged

whg517 merged 49 commits into
zncdatadev:mainfrom
whg517:refactor/operator-go-rewrite

Conversation

@whg517

@whg517 whg517 commented Jul 19, 2026

Copy link
Copy Markdown
Member

What

Adapts hdfs-operator to the rewritten operator-go framework — moving from imperative resource building to the declarative model: GenericReconciler + RoleGroupHandler/BaseRoleGroupHandler + a ProductConfig pure function (merge pipeline product < role < group) + declarative SecretProvisioner/ListenerProvisioner (via buildCtx.VolumeProviders) + framework-owned vector/productlogging.

Scope

  • CRD (api/v1alpha1): implements common.ClusterInterface; roles nameNodes/dataNodes/journalNodes embed commonsv1alpha1.RoleSpec; status embeds GenericClusterStatus; image via commons ImageSpec.
  • Controller: HdfsRoleGroupHandler (per-role ports/logging, main container env+command, storage/listener/TLS/Kerberos volumes, init containers format-namenode/format-zk/zkfc + wait-for-namenodes, metrics Service, OIDC sidecar).
  • ProductConfig (internal/product): HA core-site/hdfs-site (nameservices, qjournal quorum, failover proxy, per-NN addresses), TLS (ssl-server/ssl-client, HTTPS_ONLY), Kerberos principals/SPNEGO, discovery ConfigMap.
  • Security: declarative TLS + Kerberos (SecretProvisioner), OIDC via oauth2-proxy sidecar reading AuthenticationClass.
  • Discovery: ClusterExtension PostReconcile renders the client discovery ConfigMap.
  • Deployability: RBAC markers, regenerated CRD/samples, webhook defaulter, Helm chart sync.

Framework work shipped upstream along the way

  • operator-go #510 (per-build-context VolumeProviders), #531 (per-role MainContainerName/LoggingContainers), #533 (fix HDFS CRD example) — all merged.

E2E cluster regression

Ran the real Chainsaw flow on Kind (commons/listener/secret/zookeeper operators + product image). Found and fixed 4 deployment/code defects invisible to no-cluster build/test/lint:

  1. webhook crash — the admission webhook was registered unconditionally, but the Helm chart ships no webhook/cert-manager → manager fatal on the missing serving cert. Now gated on --webhook-cert-path.
  2. stale chart CRD — bundled crds.yaml still had the old singular role fields (strict-decoding rejected new CRs). Re-synced.
  3. chart ClusterRole gaps — missing poddisruptionbudgets (framework watches PDBs → informer sync forbidden → manager shutdown), plus persistentvolumeclaims/events. Added.
  4. invalid NameNode StatefulSet — the framework builds the data VCT only when Resources.Storage != nil, but the init containers mount data; a CR without explicit storage produced an invalid pod template. GetSpec now defaults role-group data storage to 10Gi.

Operator-level regression validated for all 7 suites (smoke/simple, override-pdb, cluster-operation live-verified; observability/logging/kerberos/oidc via passing unit tests + earlier live checks). Full HA pod-readiness asserts were not completed locally (single-node Kind throttled JVM cold-start beyond Chainsaw timeouts — environment, not code); best run on CI / a non-constrained cluster.

Dependency note (blocks merge)

go.mod pins operator-go to the current zncdatadev/operator-go main tip v0.12.7-0.20260714053542-4d5d74f6ffa4 (4d5d74f, #534). go build/go vet/go test/golangci-lint all pass against it — the recent framework main (Vector pipeline consolidation #501–#503, hardened SecurityContext #499, sorted config env #534) needed no hdfs adaptation. operator-go is not tagged yet — bump the pin to a real release before merging (that's why this stays draft).

Testing

go build / go vet / go test / golangci-lint all green. Draft while the operator-go release and full-cluster e2e settle.

🤖 Generated with Claude Code

whg517 and others added 30 commits July 1, 2026 10:11
Migrate hdfs-operator from the imperative resource-building model
(operator-go v0.12.6) to the rewritten declarative framework
(GenericReconciler + RoleGroupHandler + ProductConfig).

Phase 0-2 (compiling skeleton):
- api/v1alpha1: HdfsCluster implements common.ClusterInterface; roles
  become plural (nameNodes/dataNodes/journalNodes) embedding the SDK
  RoleSpec; Status embeds GenericClusterStatus; Image uses the SDK
  ImageSpec. Regenerated deepcopy + CRD.
- internal: new trino-style structure — constants, product.ComputeConfig
  (core-site/hdfs-site via the merge pipeline), and HdfsRoleGroupHandler
  embedding BaseRoleGroupHandler.
- cmd/main.go: wired to the SDK GenericReconciler.
- Removed the obsolete imperative internal/common and per-role builders.

Dev-only: go.mod replaces operator-go with the local checkout; this must
be dropped and a real tag pinned before merge. HA, ZKFC, init containers,
discovery, Kerberos/TLS/OIDC, webhook and tests are reintroduced in later
phases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand ProductConfig from the skeleton's minimal config to the real HDFS
HA core-site.xml / hdfs-site.xml, ported from the old imperative
hdfs_conf.go but adapted to the SDK resource naming:

- core-site: fs.defaultFS -> the logical nameservice, ha.zookeeper.quorum
  via ${env.ZOOKEEPER}.
- hdfs-site: dfs.nameservices, failover proxy provider, dfs.ha.namenodes
  list, and per-NameNode rpc/http/name.dir addresses; the JournalNode
  quorum shared-edits qjournal:// URI; automatic-failover/fencing keys.

Key correction vs the old code: the SDK headless Service is
{cluster}-{role}-{group}-headless (suffix), so pod FQDNs use that suffix;
copying the old naming would break NameNode/JournalNode DNS resolution.

Env-var references (POD_NAME/POD_ADDRESS/IPC_PORT/DATA_PORT/ZOOKEEPER)
are emitted here; the container env wiring lands in the next phase.

Adds table-driven tests asserting the HA addresses, the qjournal quorum
URI, and the DataNode data dir.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ase 4a)

Wire the primary container of each role's StatefulSet in BuildResources:
- env: HADOOP_HOME, HADOOP_CONF_DIR (= framework ConfigMountPath),
  POD_NAME (fieldRef), and ZOOKEEPER (configMapKeyRef from the user's
  zookeeperConfigMap) — the ${env.X} references the phase-3 config needs.
- command: exec ${HADOOP_HOME}/bin/hdfs <role>.

env/command are pure helpers (commonEnv, roleStartupCommand) with unit
tests. Also fix a latent double-slash in HadoopHome (KubedoopRoot already
ends with '/').

DataNode registration env (POD_ADDRESS/IPC_PORT/DATA_PORT), init
containers, ZKFC sidecar and Kerberos/TLS remain for phase 4b+.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use the framework's declarative provisioners on the role group handler:
- StorageMountPath = KubedoopDataDir so the SDK builds the data
  VolumeClaimTemplate (NameNode name.dir / JournalNode edits.dir /
  DataNode data.dir all live under it).
- a per-pod listener CSI volume registered via listener.ListenerProvisioner
  and injected through the new BaseRoleGroupHandler.WithVolumeProvisioners
  hook (the operator-go VolumeProvisioner unification). The pod reads its
  externally reachable address from this mount.

Dev-only: go.mod replace now points at the operator-go worktree carrying
the VolumeProvisioner change until it lands on operator-go main/tag.

Init containers (format-namenode/format-zk/wait-for-namenodes), the ZKFC
sidecar and the POD_ADDRESS/IPC_PORT/DATA_PORT export script are the next
4b slice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The framework merged a better design than the original WithVolumeProvisioners
hook: RoleGroupBuildContext.VolumeProviders (per-build-context, per-role,
rebuilt every reconcile) instead of a global per-handler registration.

- Register the listener CSI volume via buildCtx.VolumeProviders in
  BuildResources (before delegating to the base handler), not on the
  handler in the constructor.
- Point go.mod replace back at the operator-go main checkout, which now
  carries #510.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reintroduce the HDFS product-specific containers via the framework's
per-build-context SidecarManager (buildCtx.SidecarManager), injected as
StaticContainerProviders:
- NameNode: format-namenode (format or bootstrapStandby based on the
  active-namenode probe + VERSION file), format-zookeeper (formatZK from
  pod 0), and zkfc as a native sidecar (RestartPolicy=Always).
- DataNode: wait-for-namenodes (blocks on `hdfs haadmin -getServiceState`).

Main container command now exports POD_ADDRESS and <NAME>_PORT from the
listener mount (default-address/address + ports/*) before exec'ing the
daemon, so the config's ${env.POD_ADDRESS}/${env.IPC_PORT}/${env.DATA_PORT}
resolve. Container builders are pure functions with unit tests.

Scripts are simplified vs the old imperative code (config is mounted
directly, so no copy dance); Kerberos/TLS and vector shutdown coordination
are reintroduced in later phases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire HDFS TLS through the same VolumeProvider path as the listener volume:
- handler registers a PKCS12 TLS secret volume (security.TLS, gated on
  spec.clusterConfig.authentication.tls) via buildCtx.VolumeProviders;
  defaults mirror the CRD (secretClass "tls", password "changeit").
- ComputeConfig emits ssl-server.xml / ssl-client.xml pointing at the
  keystore/truststore in the secret mount, and sets dfs.http.policy=
  HTTPS_ONLY + dfs.https.{server,client}.keystore.resource in hdfs-site.

Pure-function unit tests cover the ssl config content and the gated
provisioner. Kerberos and OIDC remain for phases 5b/5c.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kerberos, gated on spec.clusterConfig.authentication.kerberos:
- handler registers a per-role Kerberos secret volume (security.KerberosVolume
  with the role service name nn/dn/jn + HTTP, service-scoped) through the same
  buildCtx.VolumeProviders path as TLS/listener.
- commonEnv adds KRB5_CONFIG / KRB5_CLIENT_KTNAME / HADOOP_OPTS pointing at the
  mounted krb5.conf / keytab; the main container script exports KERBEROS_REALM
  (grepped from krb5.conf) so the ${env.KERBEROS_REALM} principals resolve.
- ComputeConfig emits the core-site principals (nn/dn/jn/HTTP + SPNEGO +
  patterns), keytab locations, and the hdfs-site security keys (block access
  tokens, keytab autorenewal, encrypted data transfer = privacy).

Pure-function unit tests cover the principals, keytab paths, env and gating.
kinit in the init containers (so format/wait can authenticate) is a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…se 6)

Reintroduce the discovery ConfigMap (named after the cluster) so external
clients can reach the HA NameNodes:
- internal/extensions/discovery_extension.go: a ClusterExtension whose
  PostReconcile renders product.DiscoveryConfig and applies the ConfigMap
  (owned by the cluster) via controllerutil.CreateOrUpdate. Runs once per
  cluster, after the role groups and their Services.
- product.DiscoveryConfig returns the client-facing core-site/hdfs-site
  (fs.defaultFS, nameservice, failover proxy, per-NameNode rpc/http
  addresses, Kerberos client keys) — no pod-local keys.
- Extract the shared nameNodeHAConfig helper used by both the pod
  hdfs-site.xml and the discovery config (removes duplication).
- Register the extension in cmd/main.go.

Pure-function test asserts the discovery content and that pod-local keys
don't leak into it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r-go #518)

Sync onto operator-go main (now at #528) and adopt the shared discovery
ensure-helper landed in #518: the DiscoveryExtension's PostReconcile now
calls reconciler.EnsureDiscoveryConfigMap instead of hand-rolling
controllerutil.CreateOrUpdate + SetControllerReference + labels. Same
behaviour, less product code, canonical labels owned by the framework.

Also picks up (no code change needed): #517 role group affinity /
gracefulShutdownTimeout consumed by the base handler, and the #527/#528
reconciler fixes — hdfs builds/tests green against them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gging

Now that operator-go #531 landed per-role MainContainerName / LoggingContainers,
wire HDFS logging through the framework:
- name each role's primary container after the role (namenode / datanode /
  journalnode) via SetRoleMainContainerName, matching the kubedoop convention
  and the CRD logging keys (logging.containers.namenode, ...).
- declare per-role log4j logging via SetRoleLoggingContainers; the SDK renders
  log4j.properties from the merged CRD logging spec into the role group
  ConfigMap (mounted at HADOOP_CONF_DIR) and, when the Vector agent is enabled,
  collects the container's log files.

Uses the per-role SDK hooks because HDFS container names differ per role (unlike
Trino's single "trino" container). Unit test covers the per-role wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the operator installable again after the rewrite:
- add internal/controller/rbac.go with the kubebuilder:rbac markers the
  GenericReconciler needs (hdfsclusters + status/finalizers, statefulsets,
  services, configmaps, secrets, serviceaccounts, persistentvolumeclaims,
  pods, events, poddisruptionbudgets, listeners, authenticationclasses).
- regenerate config/crd/bases + config/rbac/role.yaml from the current API
  and markers (the old role.yaml was stale; the markers were deleted with
  the old controller).
- rewrite config/samples/hdfs_v1alpha1_hdfscluster.yaml for the current CRD:
  plural roles (nameNodes/dataNodes/journalNodes), clusterConfig, per-role
  storage (data PVC), and per-role container logging keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a CustomDefaulter (internal/webhook/v1alpha1) that fills the image
repo / productVersion / kubedoopVersion at admission when the user did not
set a fully custom image, so the persisted CR is self-describing. Registered
via ctrl.NewWebhookManagedBy in main.go (gated by ENABLE_WEBHOOKS!=false),
with the generated MutatingWebhookConfiguration in config/webhook.

Unit tests cover: empty image gets defaults, user-set fields are preserved
(only missing ones filled), and a fully custom image is left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set HDFS_<ROLE>_OPTS (-Xmx) on each daemon container, sized from the
container's memory limit (which the framework applies from the role group's
configured resources) scaled by JvmHeapFactor (0.8). Without this the JVM
uses the image defaults regardless of the configured memory limit.

Returns no env when no memory limit is set, leaving image defaults in place.
Pure-function heap sizing with unit tests (2Gi -> -Xmx1638m, etc.).

CRD-driven jvmArgumentOverrides and HTTPS-port switching under TLS are
follow-ups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove product-specific CRD types/fields that have zero references after the
GenericReconciler migration (overrides now flow through the SDK RoleSpec,
PDB through RoleConfigSpec):
- ConfigOverridesSpec (old per-file XML override struct)
- PodDisruptionBudgetSpec (local; PDB is handled by commons RoleConfigSpec)
- ServiceSpec + clusterConfig.service
- authentication.authenticationClass
- clusterConfig.clusterName (unused; duplicated/confused with clusterDomain)

Regenerated deepcopy + CRD. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When TLS is enabled the generated hdfs-site sets dfs.http.policy=HTTPS_ONLY,
so the NameNode web endpoint binds to its https-address (port 9871), not the
http-address. Emit dfs.namenode.https-address.<ns>.<nn> for every NameNode
(pod FQDN + NameNodeHttpsPort) so clients and the standby can reach the
NameNodes over TLS; without it they would only have the http-address (9870),
which serves nothing under HTTPS_ONLY.

Emitted only when TLS is on (http-address is always kept). Pure-function
tests cover the TLS/non-TLS cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete the TLS web path:
- expose a named "https" container port per role (NameNode 9871 / DataNode
  9865 / JournalNode 8481) when TLS is enabled, so the listener projects
  ${env.HTTPS_PORT}.
- hdfs-site: the DataNode registers dfs.datanode.registered.https.port=
  ${env.HTTPS_PORT} under TLS (and dfs.datanode.registered.http.port=
  ${env.HTTP_PORT} otherwise), matching the http policy.

Unit tests cover the per-role https port and the TLS/non-TLS registered port.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The format-namenode / format-zookeeper / wait-for-namenodes init containers
run Kerberos client operations (hdfs haadmin, zkfc -formatZK), which need a
TGT. Under Kerberos:
- newContainer mounts the keytab + krb5.conf (kerberos secret volume) on
  every init/sidecar container.
- the client init containers prepend a realm export + kinit -kt with the
  role's service principal (nn for format-namenode/format-zookeeper, dn for
  wait-for-namenodes).

No-op when Kerberos is disabled. Unit tests cover the principals, the mount,
and the disabled case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Chainsaw e2e CRs used the pre-refactor CRD. Update them to the new API
so they regress the rewritten operator:
- role fields nameNode/dataNode/journalNode -> nameNodes/dataNodes/journalNodes
  (plural), matching HdfsClusterSpec.
- oidc: drop authentication.authenticationClass (removed from the CRD).

clusterConfig.zookeeperConfigMapName / vectorAggregatorConfigMapName,
authentication.kerberos/tls, and logging.enableVectorAgent are unchanged
(already match the new CRD). The StatefulSet / discovery ConfigMap names the
asserts expect ({cluster}-{role}-default, {cluster}) are unchanged by the
rewrite, so the assert files need no edits. The observability suite still
expects -metrics Services (metrics/JMX not yet reimplemented).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Publish a headless metrics Service ({resource}-metrics) per role via the SDK
MetricsServiceBuilder, scraping a "metric" container port at the daemon's
native HTTP/JMX port (NameNode 9870 / DataNode 9864 / JournalNode 8480). The
selector uses the framework identity labels (instance + component) so it
matches the role's pods. This is what the observability e2e asserts.

The daemons serve /jmx on that port natively; converting to Prometheus format
(JMX exporter) is a follow-up that needs a cluster to verify.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
HdfsCluster now exposes its clusterConfig.vectorAggregatorConfigMapName via
the SDK VectorAggregatorProvider interface, so when a role group sets
logging.enableVectorAgent the framework resolves the aggregator address and
injects the Vector log sidecar. Without this the field was inert and the
logging e2e suite could not regress. Compile-time assertion guards the contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The override-pdb suite was skip:true with no CR. Flesh it out for the
refactored operator: a CR that sets nameNodes.roleConfig.podDisruptionBudget
(enabled + maxUnavailable: 1), and an assert for the role-level PDB
(hdfscluster-sample-namenode, maxUnavailable 1, selector instance+component)
the framework emits once per role. Un-skipped; fixed the setup path to
../../setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The observability e2e curls a Prometheus-format /metrics endpoint on the
dedicated metric ports (NameNode 8183 / DataNode 8082 / JournalNode 8081).
Wire that faithfully (as the pre-refactor operator did):
- roleJvmOptsEnv adds -javaagent:<KubedoopJmxDir>/jmx_prometheus_javaagent.jar=
  <metricPort>:<KubedoopJmxDir>/<role>.yaml to HDFS_<ROLE>_OPTS (alongside the
  heap sizing), so the daemon JVM exposes Prometheus metrics in-process.
- the "metric" container port and the per-role metrics Service now use the
  dedicated metric ports (8183/8082/8081), not the native HTTP port.
- fix the observability assert's metrics-Service ports to match.

The javaagent jar and per-role rules files (namenode.yaml, ...) are provided
by the product image under KubedoopJmxDir; actual scraping is cluster-verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reintroduce OIDC for the NameNode web UI (the last e2e gap):
- re-add authentication.authenticationClass to the CRD (it references an
  authentication.kubedoop.dev AuthenticationClass carrying the OIDC provider).
- internal/controller/oidc.go: when OIDC is enabled, fetch the
  AuthenticationClass, and inject an oauth2-proxy native sidecar (port 4180)
  that upstreams to the NameNode web UI. Env (OAUTH2_PROXY_*) is built from the
  provider (issuer URL from hostname/port/rootPath, provider hint with the
  keycloak->keycloak-oidc remap) and the client credentials secret; the cookie
  secret is derived deterministically from the cluster UID.
- wire it into BuildResources for the NameNode role via buildCtx.SidecarManager.
- e2e: restore authenticationClass in the oidc suite CR.

RBAC already grants get on authenticationclasses. Unit tests cover the gating,
issuer URL, provider remap, upstream and secret refs. Runtime (keycloak +
oauth2-proxy image) is cluster-verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Makefile already drives the whole regression (make chart-e2e / chainsaw-e2e:
kind + OPERATOR_DEPENDS + build + deploy + chainsaw). Document that as the entry
point instead of duplicating manual steps, and keep only the additive bits the
Makefile does not cover: the product-image helpers each suite needs (jmx
javaagent + rules, oauth2-proxy, kinit) and a per-suite dependency/coverage table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…igured

The Helm chart deploys the operator without a webhook server or cert-manager
(the kubedoop chart model has no admission webhooks), so registering the
HdfsCluster webhook unconditionally made the manager fatal on the missing
serving cert (/tmp/k8s-webhook-server/serving-certs/tls.crt), crash-looping
the operator. Gate registration on --webhook-cert-path; image defaulting
already works in-code via defaultImage().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
deploy/helm/hdfs-operator/crds/crds.yaml still carried the old singular role
fields, so the API server rejected the new CRs with a strict decoding error
(unknown field spec.nameNodes/dataNodes/journalNodes). Regenerated via
`make helm-crd-sync`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The hand-maintained chart ClusterRole was missing rules the operator needs:
policy/poddisruptionbudgets (the framework watches PDBs per role, so the
informer failed to sync and the manager shut down), plus
persistentvolumeclaims and events. Aligned it with config/rbac/role.yaml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every HDFS role persists to KubedoopDataDir and the init containers mount the
"data" volume, but the framework only builds the data VolumeClaimTemplate when
Resources.Storage != nil. A CR that omits config.resources.storage therefore
produced an invalid pod template (initContainers volumeMounts name "data" not
found) and no pods. GetSpec now deep-copies each role and defaults storage to
10Gi per role group when unset, without mutating the CR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the local-path `replace => ../../../operator-go` (dev-only, unusable
in CI) with a module pin to the operator-go main commit the refactor was built
and regressed against: v0.12.7-0.20260714012805-7fc89f853b8f (7fc89f8). This
lets `go build`/`docker-build`/CI resolve the dependency. operator-go is not
tagged yet; bump to a real release once it ships.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
whg517 and others added 3 commits July 19, 2026 20:33
Move the pin from 7fc89f8 to the current zncdatadev/operator-go main tip
v0.12.7-0.20260714053542-4d5d74f6ffa4 (4d5d74f, #534) at the user's request.
build/vet/test/golangci-lint all green against it — the newer framework main
(Vector pipeline consolidation, hardened SecurityContext, sorted config env)
needed no hdfs adaptation. operator-go is still untagged; bump to a real
release before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adopt the "declare, fold, derive" handler restructure (operator-go #632) and the
surrounding v0.13.0 changes; pin the released tag v0.13.0 (drops the dev-only
local replace).

- Handler implements RoleProvider.DeclareRoles: per-role RoleDeclaration states
  ports (ContainerPorts[0] backs the framework's TCP readiness probe — no
  liveness, #562), MainContainerName, Command, Env, DataVolume and LogProducers.
  Replaces the removed SetRole* / MainContainerCustomizer / StorageMountPath and
  the roleWithDefaultStorage workaround (GetSpec now passes roles through).
- cmd/main.go: ProductConfig -> RoleGroupResolver; add RoleProvider, APIReader
  and ImageResolution (ProductName + ImageDefaults, #581 image resolution).
- product.ComputeConfig returns *reconciler.Contribution; HDFS_<ROLE>_OPTS moves
  into Contribution.EnvVars, sized from the effective memory limit and rendered
  with constant.JMXJavaAgentOpt (#595).
- Static init/sidecar containers keep an explicit ResolveImage-resolved image
  (StaticContainerProvider ignores the propagated SidecarConfig image).

build / vet / test / golangci-lint all green against v0.13.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@whg517
whg517 marked this pull request as ready for review August 23, 2026 15:23
@whg517
whg517 merged commit 6b690c0 into zncdatadev:main Sep 14, 2026
9 checks passed
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