镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

feat(webhook): reject CacheRuntime updates to fields that cannot be applied in place - #6243

Open
btxu-db wants to merge 1 commit into
fluid-cloudnative:masterfrom
btxu-db:feat/cacheruntime-update-validating-webhook
Open

btxu-db wants to merge 1 commit into
fluid-cloudnative:masterfrom
btxu-db:feat/cacheruntime-update-validating-webhook

Conversation

@btxu-db

@btxu-db btxu-db commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

Ⅰ. Describe what this PR does

Adds a validating admission webhook for CacheRuntime (update.cacheruntime.fluid.io). An UPDATE of a CacheRuntime that changes a spec field which cannot take effect in place is now rejected at admission time.

Background

The CacheRuntime controller syncs only the replicas, runtimeVersion and resources of the master and worker to their AdvancedStatefulSets (see AdvancedStatefulSetManager.SyncComponentSpec). The client is a DaemonSet and is never synced. Every other field is read once, when the component is created, so after a change:

  • most fields: the API server accepts the update and it is silently ignored, with no error shown to the user;
  • tieredStore: the update is applied halfway. The quota in the runtime ConfigMap follows the spec, while the tmpfs sizeLimit of the Worker Pod keeps its creation-time value, and the mismatch only surfaces when the Worker restarts.

Changes

  1. Validation (pkg/webhook/handler/validating/cacheruntime)

    • ValidateSpecUpdate compares the old and new spec field by field. Any changed field other than master/worker replicas, runtimeVersion and resources yields a field.Forbidden; the client's runtimeVersion and resources are immutable too.
    • The comparison is semantic (equality.Semantic.DeepEqual): nil equals an empty list, and 2Gi equals 2048Mi.
    • The denial names every offending field at once, prefixed with the rule, e.g.:
      admission webhook "update.cacheruntime.fluid.io" denied the request: only master/worker replicas, runtimeVersion and resources can be updated in place: spec.worker.tieredStore: Forbidden: field is immutable
      
    • Only spec is compared, so writes to metadata and status (such as the controller's own finalizer and condition updates) are never affected. The webhook rule matches only the main resource, so writes through the status and scale subresources (e.g. kubectl scale) are not checked.
    • validating.HandlerMap is enabled in pkg/webhook/handler/register.go, serving /validate-data-fluid-io-v1alpha1-cacheruntime.
  2. caBundle injection

    The webhook serves a self-signed certificate generated at startup, so the chart installs the configuration with a placeholder caBundle and the webhook process patches in the real CA. The existing mechanism only handled the MutatingWebhookConfiguration; this PR extends it to the new ValidatingWebhookConfiguration.

    • CertificateBuilder gains PatchValidatingCABundle, which injects the caBundle into a ValidatingWebhookConfiguration and skips the patch when nothing changed.
    • WebhookReconciler gains a ValidatingWebhookName field and picks the configuration to patch by the request name.
    • pkg/ctrl/watch watches fluid-cacheruntime-validating-webhook (by that name only) and re-injects the caBundle whenever it is created or updated, e.g. when helm upgrade resets the caBundle to the placeholder. The webhook's cache lists/watches this object by name only.
  3. Helm chart

    • Adds the ValidatingWebhookConfiguration fluid-cacheruntime-validating-webhook, reusing the existing fluid-pod-admission-webhook Service.
    • Adds webhook.cacheRuntimeValidation.failurePolicy (default Fail), which decides whether a CacheRuntime update is rejected or let through unchecked while the webhook is unreachable.
    • Grants the webhook ClusterRole get/patch/list/watch on that configuration, restricted by resourceNames.

    The chart and the code must agree on a few names: the configuration name (common.ValidatingWebhookName), the path (common.WebhookValidateCacheRuntimePath), and the RBAC resourceNames. Because RBAC with resourceNames only allows list/watch with a metadata.name field selector, the webhook's cache in cmd/webhook/app/webhook.go filters this object by name.

  4. Docs: updates the English and Chinese cacheruntime_spec_update.md to explain that an update to an unsupported field is rejected, that the CacheRuntime must be deleted and recreated to change such a field, and what failurePolicy means.

Ⅱ. Does this pull request fix one issue?

NONE

Ⅲ. List the added test cases (unit test/integration test) if any, please explain if no tests are needed.

  • pkg/webhook/handler/validating/cacheruntime/spec_update_test.go: the updatable fields (master/worker replicas/runtimeVersion/resources), each immutable field, several violations at once, and semantic equality (nil vs. empty, quantities in different units).
  • pkg/webhook/handler/validating/cacheruntime/handler_test.go: CREATE/DELETE/CONNECT are allowed, subresource writes are allowed, a missing or malformed old/new object returns 400, metadata/status-only writes are allowed, and an update changing immutable fields is denied with every offending field listed.
  • pkg/webhook/certificate_builder_test.go: PatchValidatingCABundle injects the caBundle into every webhook, leaves an already patched configuration alone, fails when the configuration does not exist, and does not touch a MutatingWebhookConfiguration of the same name.
  • pkg/controllers/v1alpha1/webhook/webhook_controller_test.go: patches only the Mutating or Validating configuration the request names, and requeues after 10 seconds when the patch fails.
  • pkg/ctrl/watch/validatingwebhookconfiguration_test.go: the event filter passes create/update events for the target name only; delete events do not trigger a reconcile.
  • e2e (test/gha-e2e/curvine/test.sh): new verify_immutable_spec_update_rejected. Patching spec.worker.tieredStore must be rejected by the webhook and leave the spec unchanged; annotating the CacheRuntime must succeed.

Ⅳ. Describe how to verify it

  1. Install the Fluid chart built from this PR (webhook.enabled=true, the default) and create a CacheRuntime and a Dataset.
  2. Changing a field that cannot be updated in place is rejected:
    kubectl patch cacheruntime demo --type merge \
      -p '{"spec":{"worker":{"tieredStore":{"levels":[{"processMemory":{"quota":"4Gi"}}]}}}}'
    # Error from server (Forbidden): admission webhook "update.cacheruntime.fluid.io" denied the request: ...spec.worker.tieredStore: Forbidden: field is immutable
  3. Changing a supported field succeeds and is synced to the AdvancedStatefulSet:
    kubectl patch cacheruntime demo --type merge -p '{"spec":{"worker":{"replicas":2}}}'
    kubectl scale cacheruntime demo --replicas=1
  4. A metadata-only change succeeds:
    kubectl annotate cacheruntime demo foo=bar --overwrite
  5. The caBundle has been injected:
    kubectl get validatingwebhookconfiguration fluid-cacheruntime-validating-webhook \
      -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | head -1

Ⅴ. Special notes for reviews

  • Immutable fields are listed explicitly: ValidateSpecUpdate enumerates the fields that cannot change. A field added to CacheRuntimeSpec later is updatable unless it is added here as well, so this list needs to be kept in sync with the spec.
  • failurePolicy defaults to Fail: while the webhook is unreachable, or before its caBundle is injected, CacheRuntime updates are rejected. The controller's status writes go through the subresource and are unaffected, but its writes to the main resource (such as finalizers) fail too. Set it to Ignore to favor availability.
  • Right after install, until the caBundle is injected, calls to the webhook fail certificate verification (and updates are rejected under Fail); the e2e test retries for this window.
  • With the webhook disabled (webhook.enabled=false) nothing is checked, and the behavior is as before: the update is accepted but does not take effect.

🤖 Generated with Claude Code

…pplied in place

Add a validating admission webhook (update.cacheruntime.fluid.io) that
rejects a CacheRuntime UPDATE changing any spec field other than
master/worker replicas, runtimeVersion and resources. Such fields are only
read when the component is created, so a change was silently ignored or,
for tieredStore, half-applied (ConfigMap quota updated while the worker
tmpfs sizeLimit kept its creation-time value).

- Only spec is compared; metadata/status writes and the scale subresource
  are never checked.
- Add the ValidatingWebhookConfiguration to the chart with a configurable
  failurePolicy (webhook.cacheRuntimeValidation.failurePolicy, default Fail)
  and RBAC for the webhook to patch it.
- Extend the webhook controller and certificate builder to keep the
  caBundle of the validating webhook configuration in sync.
- Add unit tests, a curvine e2e check, and update the en/zh docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: btxu-db <btxu-db@outlook.com>
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Nov 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.41558% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.90%. Comparing base (ff99142) to head (98d5eac).

Files with missing lines Patch % Lines
pkg/ctrl/watch/manager.go 0.00% 17 Missing ⚠️
pkg/webhook/certificate_builder.go 85.18% 3 Missing and 1 partial ⚠️
cmd/webhook/app/webhook.go 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6243      +/-   ##
==========================================
+ Coverage   65.81%   65.90%   +0.08%     
==========================================
  Files         486      489       +3     
  Lines       34454    34604     +150     
==========================================
+ Hits        22677    22806     +129     
- Misses      10024    10044      +20     
- Partials     1753     1754       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Validation permits master/worker imagePullPolicy updates that are silently ignored while blocking client.cleanPolicy changes that already take effect dynamically.

2 open issues
What changed in this PR

Adds CacheRuntime update validation to Fluid’s admission webhook, rejecting changes to fields the controller cannot apply in place rather than silently accepting them.

Changes:

  • Adds semantic spec comparison, admission handling, and validation tests.
  • Extends certificate injection, configuration watches, and Helm permissions to support the validating webhook.
  • Documents update restrictions and failure-policy behavior, with end-to-end coverage.
File Description
test/​gha-e2e/​curvine/​test.sh Checks immutable-field rejection and metadata updates.
pkg/​webhook/​handler/​validating/​webhook.go Maps the validation endpoint to its handler.
pkg/​webhook/​handler/​validating/​cacheruntime/​spec_update.go Defines immutable-field validation.
pkg/​webhook/​handler/​validating/​cacheruntime/​spec_update_test.go Tests field restrictions and semantic equality.
pkg/​webhook/​handler/​validating/​cacheruntime/​handler.go Handles CacheRuntime update admission requests.
pkg/​webhook/​handler/​validating/​cacheruntime/​handler_test.go Tests admission responses and request exemptions.
pkg/​webhook/​handler/​validating/​cacheruntime/​cacheruntime_suite_test.go Registers the validation test suite.
pkg/​webhook/​handler/​register.go Enables validating handler registration.
pkg/​webhook/​certificate_builder.go Adds validating webhook CA injection.
pkg/​webhook/​certificate_builder_test.go Tests CA injection and configuration isolation.
pkg/​ctrl/​watch/​validatingwebhookconfiguration.go Filters validating configuration events by name.
pkg/​ctrl/​watch/​validatingwebhookconfiguration_test.go Tests configuration event filtering.
pkg/​ctrl/​watch/​manager.go Registers the validating configuration watch.
pkg/​controllers/​v1alpha1/​webhook/​webhook_controller.go Routes CA reconciliation by configuration name.
pkg/​controllers/​v1alpha1/​webhook/​webhook_controller_test.go Tests reconciliation routing and retries.
pkg/​common/​webhook.go Defines configuration and endpoint names.
docs/​zh/​samples/​cacheruntime/​cacheruntime_spec_update.md Documents restrictions and availability behavior in Chinese.
docs/​en/​samples/​cacheruntime/​cacheruntime_spec_update.md Documents restrictions and availability behavior in English.
cmd/​webhook/​app/​webhook.go Restricts the validating configuration cache by name.
charts/​fluid/​fluid/​values.yaml Adds the validation failure-policy setting.
charts/​fluid/​fluid/​templates/​webhook/​webhookconfiguration.yaml Installs the validating webhook configuration.
charts/​fluid/​fluid/​templates/​role/​webhook/​rabc.yaml Grants name-scoped configuration access.

Review effort: Balanced

Comment on lines +60 to +61
errs = append(errs, requireUnchanged(client.Child("tieredStore"), oldSpec.Client.TieredStore, newSpec.Client.TieredStore)...)
errs = append(errs, requireUnchanged(client.Child("cleanPolicy"), oldSpec.Client.CleanPolicy, newSpec.Client.CleanPolicy)...)
Comment on lines +79 to +82
if !syncedInPlace {
errs = append(errs, requireUnchanged(path.Child("runtimeVersion"), oldSpec.RuntimeVersion, newSpec.RuntimeVersion)...)
errs = append(errs, requireUnchanged(path.Child("resources"), oldSpec.Resources, newSpec.Resources)...)
}
cheyang pushed a commit to cheyang/fluid that referenced this pull request Nov 14, 2026
The *.out artifacts are gitignored; force-added as review evidence.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@cheyang cheyang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The premise is real — on base, non-synced CacheRuntime spec changes are silently ignored and tieredStore updates half-apply — and the guard is carefully built (semantic equality, subresource exclusion, name-filtered watch). But two PR-introduced issues block merge.

1. The webhook denies the controller's own metadata writes for client-less CacheRuntimes (stored form without spec.client, e.g. the mooncake e2e fixture). Structural defaulting doesn't materialize absent parent objects, but the typed client always serializes client:{}, so the API server defaults cleanPolicy: OnRuntimeDeleted onto the update body only — a phantom immutable-field diff. The controller's finalizer/owner-reference writes are then rejected forever: datasets stay NotBound and deletion hangs. This is exactly what fails in this PR's kind-e2e on all five K8s versions, and both of us reproduced it under envtest with a real API server (control and guard subtests green, a demo fix flips it green). Please fix it generally — normalize CRD defaults before comparing (or exempt cleanPolicy, which CSI reads dynamically via NodeUnstageVolume) — not as a phantom-diff special case, and land the stored-form/defaulting regression test with it; the harnesses have runnable examples.

2. config/webhook/manifests.yaml was not regenerated after adding the +kubebuilder:webhook marker, so the required lint gate fails. make update-crd and commit.

Meaningful disagreement in the debate was minor and resolved in cross-critique (one severity label; one e2e-coverage claim narrowed rather than upheld). Reviewer C (Copilot) flagged two items, both independently verified by us: cleanPolicy is a live CSI knob (widens the F1 fix space) and imagePullPolicy is admitted-but-never-synced.

Next round

[MUST] F1: make the webhook admit metadata-only updates of client-less CacheRuntimes via a general CRD-default normalization (or cleanPolicy exemption); add the stored-form/defaulting regression test; mooncake e2e green on all five K8s versions.
[MUST] F2: run make update-crd and commit config/webhook/manifests.yaml; lint gate green.
[SHOULD] F3: imagePullPolicy — make it immutable (keep image/tag updatable) or actually propagate it in updateImage; invert the canaries.
[SHOULD] F4: state the deletion-hang consequence of failurePolicy: Fail (incl. the helm-upgrade 404 window) in cacheruntime_spec_update.md; consider Ignore or a controller-SA exemption.
[SHOULD] F5: document the supported upgrade ordering for the new VWC RBAC watch, or tolerate a missing grant.
[SHOULD] F7: add a reflection-based completeness tripwire (or a leaf-granular fail-closed comparison) so future spec fields can't silently reopen the ignored-update class.
[SHOULD] M2: add workload-sync e2e assertions that run on multi-node clusters (currently skipped in single-node kind CI).
[Filed] none.

Non-blocking observations (6) — not part of this round's ask; fix now, follow up, or file an issue as you see fit
  • pkg/webhook/handler/validating/cacheruntime/spec_update.go:75 [minor] — runtimeVersion.imagePullPolicy is admitted by the webhook but never synced — the exact no-op-update class this PR exists to reject (one subfield)
    F3 (Claude's phase-1 catch, missed by Codex in phase 1, independently verified by Codex in cross-critique, and independently flagged by Reviewer C as C4575767913): validateComponentCommonSpecUpdate treats the whole master/worker runtimeVersion as updatable for synced components, but AdvancedStatefulSetManager.updateImage (pkg/ddc/cache/component/advanced_statefulset_manager.go:279) patches only container.Image = image:tag — the ImagePullPolicy in the ASTS pod template keeps its creation-time value, and the code comment at :197 claiming ImagePullPolicy is in-place supported is wrong (pre-existing on base). So a spec.master.runtimeVersion.imagePullPolicy: Never update is admitted and silently ignored: the PR's own contract ('only fields that take effect in place may change') is inaccurate for this subfield. Attribution note (Codex): the underlying non-sync is pre-existing (identical updateImage at base); the PR-introduced part is the new admission decision that over-claims the contract. Fix: either make imagePullPolicy immutable (Reviewer C's suggestion, keeping image/tag updatable) or actually propagate it in updateImage; then invert the canaries.
  • charts/fluid/fluid/values.yaml:372 [minor] — failurePolicy=Fail fail-closes controller finalizer writes during webhook outages and the helm-upgrade window — CacheRuntime deletion hangs
    F4 (consensus, both reviewers, minor): with failurePolicy Fail (chart default, values.yaml:372; rendered at webhookconfiguration.yaml:72, no namespace/objectSelector so cluster-wide), every CacheRuntime UPDATE is rejected while the webhook is unreachable — including the controller's finalizer add/remove, which are main-resource Updates (pkg/controllers/runtime_controller.go:243/330/354) — so CacheRuntime creation AND deletion stall until the webhook recovers; DELETE itself is not matched but finalizer removal is an UPDATE. Beyond the documented install window, two windows matter (Claude's added helm-upgrade window verified sound by Codex): webhook pod crash/rollout, and helm upgrade where the new VWC becomes active while old webhook pods 404 the new path (a 404 counts as fail-closed under Fail). Folded in (Q1): whether Fail is the right default for a UX-validation webhook — the handler only rejects silent no-ops, not an integrity requirement; Ignore is one values.yaml switch away, and exempting the controller SA would moot the controller-written case. Ask: state the deletion-hang consequence explicitly in cacheruntime_spec_update.md so operators choose knowingly.
  • cmd/webhook/app/webhook.go:136 [minor] — Upgrade skew: new webhook binary with old chart RBAC cannot list/watch ValidatingWebhookConfiguration — whole serving path (incl. the Fail mutating webhook) degrades
    F5 (raised as a question by Codex; every code link upgraded from hypothesis to code-path verified by Claude in cross-critique): the new source.Kind watch and the cache ByObject entry make the webhook process list/watch ValidatingWebhookConfiguration at manager start (cmd/webhook/app/webhook.go:136-138, pkg/ctrl/watch/manager.go:260-271); the RBAC grant is new in this PR (charts/fluid/fluid/templates/role/webhook/rabc.yaml, commit 98d5eac), so an old chart has no such grant — the informer's list/watch returns 403 and can never sync. Under controller-runtime v0.17.5, CacheSyncTimeout (2 min) fails Controller.Start; mgr.Start propagates; webhook.go:182-185 logs 'start webhook handler failed' and os.Exit(1) — the whole process, including the mutating webhook (itself failurePolicy=Fail for pod injection), crash-loops for the duration of the skew. Helm ships RBAC in the same release so only an image-only upgrade/rollback is exposed. Policy question for maintainers: is image-only skew a supported upgrade ordering? Cheap mitigations: a chart/upgrade-docs note, or tolerating a missing RBAC grant instead of hard-failing cache sync.
  • pkg/webhook/handler/validating/cacheruntime/spec_update.go:45 [minor] — Immutable-field list is fail-open: future CacheRuntimeSpec fields silently become updatable again
    F7 (Codex's F3 merged with Claude's O1 — same finding, same fix, trivial severity delta between nit and minor): ValidateSpecUpdate enumerates immutable fields one by one; any field later added to CacheRuntimeSpec is updatable by default unless its author also edits this function — silently re-opening the exact bug class this webhook closes (the author discloses this in 'Special notes'). Preferred shape (Claude's critique of Codex's sketch): the low-risk variant is a reflection-based tripwire test that walks CacheRuntimeSpec and the component specs and fails on any leaf not classified as updatable-or-immutable. If the fail-closed rewrite is taken instead (copy new spec, overwrite updatable fields from old, semantic-DeepEqual the whole spec, report differing paths), it must classify at leaf granularity — overwriting whole runtimeVersion inherits the F3 imagePullPolicy hole, overwriting nothing breaks image updates — and it centralizes the F1 class of phantom diffs, so the defaulting normalization is still required.
  • pkg/webhook/handler/validating/cacheruntime/handler_test.go:58 [minor] — Handler tests never model the stored-form/CRD-defaulting asymmetry that produced F1
    F6 (consensus, both reviewers): all handler/spec tests build old and new from the same fully-populated baseSpec() (e.g. spec_update_test.go:66 bakes CleanPolicy: OnRuntimeDeleted into both), so no test constructs the old object from a stored form omitting spec.client while the new one carries the API-server-defaulted cleanPolicy — precisely the asymmetry that let the PR's own suite stay green while CI went red on five versions. Must land WITH the F1 fix, not after: a regression case where the old object is decoded from a stored form without spec.client and the new one carries the defaulted cleanPolicy, plus the anti-overfix guard (a real change to OnDemand stays denied). Runnable examples exist on both verify branches (verify_pr6243_defaulting_test.go, TestVerifyPR6243ClientlessMetadataUpdateAllowed).
  • test/gha-e2e/curvine/test.sh:550 [minor] — Workload-sync assertions are skipped in single-node CI — no e2e verifies a supported update reaches the AdvancedStatefulSet
    M2 (narrow residual after Codex's rebuttal of Claude's broader M1; the residual is agreed by both): the curvine e2e already covers the admission half end-to-end — scale_worker_and_verify 1 patch (test/gha-e2e/curvine/test.sh:550/554, pre-existing on base) issues kubectl patch of spec.worker.replicas on the main resource and asserts the value lands, and it passed in this PR's CI. But the kind clusters are single-node (default helm/kind-action, no extra nodes in .github/workflows/kind-e2e.yml), so verify_pods=false and the sync-to-AdvancedStatefulSet assertions are skipped — equally for the main-resource and the scale-subresource paths. Unit tests cover the sync logic; the real-cluster propagation is unverified in CI (A10).
Verified review delta
  • F1: open (initial-pr)
  • F2: open (initial-pr)
  • F3: open (initial-pr)
  • F4: open (initial-pr)
  • F5: open (initial-pr)
  • F7: open (initial-pr)
  • F6: open (initial-pr)
  • M2: open (initial-pr)

Code: needs-work. CI: failure; attribution: pr-regression. Required CI checks still apply.

client := root.Child("client")
errs = append(errs, validateComponentCommonSpecUpdate(client, &oldSpec.Client.RuntimeComponentCommonSpec, &newSpec.Client.RuntimeComponentCommonSpec, false)...)
errs = append(errs, requireUnchanged(client.Child("tieredStore"), oldSpec.Client.TieredStore, newSpec.Client.TieredStore)...)
errs = append(errs, requireUnchanged(client.Child("cleanPolicy"), oldSpec.Client.CleanPolicy, newSpec.Client.CleanPolicy)...)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

F1 (consensus, both reviewers independently, confirmed in both cross-critiques): for a CacheRuntime whose stored form has no spec.client section (any client-less topology applied from YAML, e.g. test/gha-e2e/mooncake/cacheruntime.yaml), EVERY full-object Update is denied — including the controller's own finalizer/owner-reference writes — because: (1) CRD structural defaulting does not materialize absent parent objects in the stored/old object; (2) a typed Go client marshals the runtime with a materialized client object (encoding/json never omits zero-value structs, api/v1alpha1/cacheruntime_types.go:153); (3) the API server then applies the CRD default spec.client.cleanPolicy=OnRuntimeDeleted to the new object only, producing a phantom immutable-field diff that ValidateSpecUpdate forbids. Consequences: the CacheRuntime never reconciles (dataset stuck NotBound — mooncake e2e fails on all five K8s versions in this PR's CI) and deletion hangs because finalizer removal is also a full-object Update. The PR's claim that metadata writes are never affected is false for exactly these CRs. Fix direction (harness-bites verified, then reverted): make it GENERAL — normalize CRD defaults before comparing / treat old-zero-new-CRD-default as unchanged / re-default the old object, so any future defaulted immutable field (Q2) is covered; an identity exemption for the controller SA alone is not general (a user doing kubectl replace or a typed write of a pre-existing client-less CacheRuntime would still be denied). Reviewer C (C4575767901), verified by both debaters, widens the fix space: cleanPolicy is dynamically consumed by CSI NodeUnstageVolume (pkg/ddc/base/runtime.go -> SetupFuseCleanPolicy, pkg/csi/plugins/nodeserver.go:719-730), so exempting cleanPolicy from the immutable list is a legitimate fix that also preserves a genuinely dynamic knob. A real change (e.g. -> OnDemand) must stay denied; the harnesses guard that.

"github.com/fluid-cloudnative/fluid/pkg/webhook/handler/validating/cacheruntime"
)

// +kubebuilder:webhook:path=/validate-data-fluid-io-v1alpha1-cacheruntime,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=data.fluid.io,resources=cacheruntimes,verbs=update,versions=v1alpha1,name=update.cacheruntime.fluid.io

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

F2 (consensus; severity label adjudicated from Claude's 'major' vs Codex's 'blocker' — a required CI gate failing on the PR head is merge-blocking by definition, and both reviewers list it as must-fix): the PR adds the +kubebuilder:webhook marker but does not commit the regenerated config/webhook/manifests.yaml. The lint gate runs make update-crd (manifests via the repo-pinned controller-gen v0.19.0) and fails on the dirty tree: 'CRD validation failed. Please use update-crd to keep CRDs latest'. The generated config no longer reflects the shipped webhook. Fix: run make update-crd / make manifests and commit the output.

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.

3 participants