Skip to content

fix(nvca): keep the model-cache init namespace out of namespace GC and the metadata webhook - #1992

Open
balajinvda wants to merge 2 commits into
mainfrom
fix/nvca-modelcache-init-namespace-gc-webhook
Open

balajinvda wants to merge 2 commits into
mainfrom
fix/nvca-modelcache-init-namespace-gc-webhook

Conversation

@balajinvda

@balajinvda balajinvda commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Why

On a cluster running NVCA 3.12.4 with HelmModelCaching on, Helm functions deployed but every worker downloaded its own model, and the agent logged every 30 minutes:

Create instance ServiceAccount helm-instance-permissions in namespace nvca-modelcache-init: namespaces "nvca-modelcache-init" not found

The agent creates nvca-modelcache-init at startup and labels it nvca.nvcf.nvidia.io/workload-instance-type: miniservice so the unbound-DNS Kyverno policy matches cache writer jobs (#1116, #1303). Two other components select on that label and neither expects a namespace with no function instance behind it:

  1. internal/gc/namespace deletes every labelled namespace without an ICMSRequest of the same name. It runs at startup and hourly, so the init namespace was removed right after creation. The first "not found" error appeared exactly one permissions cycle after agent start.
  2. The miniservice mutating webhook's namespaceSelector matches the same label, and its Fail policy denies any pod whose namespace lacks nvcf-miniservice-metadata. Recreating the namespace by hand moved the failure here: admission webhook "miniservice-mutate-create.nvca.nvcf.nvidia.io" denied the request: namespace "nvca-modelcache-init" missing required ConfigMap "nvcf-miniservice-metadata".

Neither was visible in the GC's own logs: controller-runtime starts runnables with its own context, on which core.GetLogger returns an io.Discard logger, so the 20k-line agent log had zero GC lines.

What changed

  • internal/gc/namespace/cleaner.go: skip storage.ModelCacheInitNamespace when collecting orphaned namespaces.
  • pkg/webhook/miniservice_mutating_webhook.go: admit pods in the init namespace unchanged; there is no per-instance metadata to inject.
  • internal/gc/gc.go + pkg/nvca/agent_manager.go: Runnable.SetLogContext lets the agent pass a logging context, so GC runs, orphan detection and deletions appear in the agent log.
  • Bazel deps for the new pkg/storage import in the GC package.

Customer Release Notes

Helm functions with models now use the shared model cache on clusters with HelmModelCaching enabled; previously the cache init namespace was garbage-collected and each worker downloaded its own copy.

Plan Summary

Not applicable. No chart or resource changes; the init namespace is created by the agent as before and now survives.

Usage

Not applicable.

Testing

go test ./internal/gc/... ./pkg/webhook/ ./pkg/nvca/ in src/compute-plane-services/nvca (webhook and nvca packages need -ldflags '-X github.com/NVIDIA/k8s-dra-driver-gpu/internal/info.version=v25.8.0', as the Makefile does; without it the package panics at init on main too). New tests: TestCleaner_collectOrphanedNamespaces now includes a labelled init namespace with no ICMSRequest and asserts it is not collected; TestMiniserviceOperatorWebhook_ModelCacheInitNamespace_Allowed admits a writer pod without a metadata ConfigMap. Live validation on a cluster to follow via the dev image.

Notes

The GC deletion happened silently because of the discard logger; the third change is what would have made this a one-line log read instead of a source trace.

Issues

Closes #1991

References

None

Related Pull Requests

#1116 and #1303 added the label. #1969 fixed the neighbouring storage catalog ConfigMap gap.

Dependencies

None

Summary by CodeRabbit

  • Bug Fixes

    • Model-cache writer jobs can now run in the dedicated initialization namespace without requiring instance metadata or additional pod mutations.
    • Garbage collection now preserves the model-cache initialization namespace instead of treating it as orphaned or eligible for cleanup.
    • Garbage-collection operations now use the appropriate startup logging context for improved diagnostics.
  • Improvements

    • Manual image publishing handles multiple image targets with clearer repository naming while preserving existing behavior for single targets.

…d the metadata webhook

The agent creates nvca-modelcache-init at startup and labels it
workload-instance-type=miniservice so the unbound-DNS policy matches its
cache writer jobs (#1116, #1303). Two other components select on that
label and neither expects a namespace without a function instance:

- internal/gc/namespace treats every labelled namespace without an
  ICMSRequest of the same name as orphaned and deletes it. The GC runs at
  startup and hourly, so the init namespace disappeared right after
  creation; the agent then logged "namespaces nvca-modelcache-init not
  found" on every permissions pass and Helm functions fell back to
  per-worker model downloads.
- The miniservice mutating webhook's namespaceSelector matches the same
  label, and its Fail policy denies any pod whose namespace lacks the
  nvcf-miniservice-metadata ConfigMap. Writer pods in the init namespace
  have no instance metadata, so cache initialization could not start
  even when the namespace existed.

The GC now skips storage.ModelCacheInitNamespace, and the webhook admits
pods in it unchanged. The GC also logged nothing about any of this:
controller-runtime starts runnables with its own context, on which
core.GetLogger returns a discard logger. The agent now hands the GC a
logging context so orphan detection and deletions are visible.

Tests: the namespace cleaner test includes a labelled init namespace with
no ICMSRequest and asserts it is not collected; the webhook test admits a
writer pod in the init namespace without a metadata ConfigMap.

Closes #1991

Co-Authored-By: Balaji Ganesan <bganesan@nvidia.com>
@balajinvda
balajinvda requested a review from a team as a code owner September 19, 2026 19:16
@balajinvda
balajinvda requested a review from estroz September 19, 2026 19:16
@balajinvda balajinvda added the deploy-to-stg Build and push a dev image to ncp-dev on every push to this PR label Sep 19, 2026
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/nvcf/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6bedf519-78fc-4a2a-a5d5-c0cc63b78dfc

📥 Commits

Reviewing files that changed from the base of the PR and between 63b3cec and b74d8d1.

📒 Files selected for processing (8)
  • .github/workflows/image-push-manual.yml
  • src/compute-plane-services/nvca/internal/gc/gc.go
  • src/compute-plane-services/nvca/internal/gc/namespace/BUILD.bazel
  • src/compute-plane-services/nvca/internal/gc/namespace/cleaner.go
  • src/compute-plane-services/nvca/internal/gc/namespace/cleaner_test.go
  • src/compute-plane-services/nvca/pkg/nvca/agent_manager.go
  • src/compute-plane-services/nvca/pkg/webhook/miniservice_mutating_webhook.go
  • src/compute-plane-services/nvca/pkg/webhook/miniservice_mutating_webhook_test.go

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


📝 Walkthrough

Walkthrough

Changes

Model-cache namespace handling

Layer / File(s) Summary
GC logging context
src/compute-plane-services/nvca/internal/gc/gc.go, src/compute-plane-services/nvca/pkg/nvca/agent_manager.go
Runnable accepts a logging context and applies its logger during startup. The agent passes its startup context before registration.
GC namespace exclusion
src/compute-plane-services/nvca/internal/gc/namespace/*
Namespace collection skips storage.ModelCacheInitNamespace. Build dependencies and collection tests cover the exclusion.
Webhook namespace bypass
src/compute-plane-services/nvca/pkg/webhook/*
The webhook allows Pods in storage.ModelCacheInitNamespace without metadata lookup or mutation patches. A test covers this case.

Image target repository naming

Layer / File(s) Summary
Image repository resolution
.github/workflows/image-push-manual.yml
The workflow counts plain image targets. Multiple targets use package leaf names unless the leaf matches the service name; one target keeps the service repository name.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: kristinapathak

Merge Risk: ⚪ Minimal · up to b74d8

The model-cache namespace bypass retains existing request logging and metrics coverage. No actionable regression remains from the reviewed changes.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The workflow change in .github/workflows/image-push-manual.yml changes image repository naming and assigns separate development repositories to multiple NVCA image targets. This change is not connec… Remove the unrelated workflow change from this pull request, or link it to a directly relevant issue and submit it in a separate pull request.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 7 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits format and accurately describes the primary bug fix: preserving the model-cache initialization namespace and bypassing metadata webhook handling for it. The nvca…
Linked Issues check ✅ Passed The changes satisfy the coding objectives in issue #1991. collectOrphanedNamespaces excludes storage.ModelCacheInitNamespace, and the namespace test verifies that the init namespace is retained wh…
Full details: Out of Scope Changes check

Explanation

The workflow change in .github/workflows/image-push-manual.yml changes image repository naming and assigns separate development repositories to multiple NVCA image targets. This change is not connected to issue #1991's namespace garbage collection, GC logging, or model-cache writer admission objectives. The repository summary identifies it as an additional CI fix, but no linked issue or coding objective includes it.

Full details: Docstring Coverage

Explanation

Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 7 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 golangci-lint (2.13.2)

level=error msg="Running error: context loading failed: failed to load packages: failed to load packages: failed to load with go/packages: err: exit status 1: stderr: go: inconsistent vendoring in /src/compute-plane-services/nvca:\n\tgithub.com/NVIDIA/KAI-scheduler@v0.12.6: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt\n\tgithub.com/NVIDIA/k8s-dra-driver-gpu@v0.0.0-20251017125642-cfe35ffd3d2c: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt\n\tgithub.com/NVIDIA/nvcf/src/libraries/go/lib@v0.0.0-20260722095202-f5e2792f5630: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt\n\tgithub.com/aws/aws-sdk-go@v1.55.5: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt\n\tgithub.com/bombsimon/logrusr/v4@v4.1.0: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt\n\tgithub.com/evanphx/json-patch/v5@v5.9.11: is explicitly required in

... [truncated 21721 characters] ...

i: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\tk8s.io/apiextensions-apiserver: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\tk8s.io/apimachinery: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\tk8s.io/client-go: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\tk8s.io/component-base: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\tsigs.k8s.io/controller-runtime: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\tgolang.org/x/crypto: is replaced in go.mod, but not marked as replaced in vendor/modules.txt\n\n\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor\n"


Comment @coderabbitai help to get the list of available commands.

…sitory

image-push mapped every target named `image` to the service repository.
NVCA has four such targets, cmd/nvca, cmd/nvca-operator,
cmd/cluster-validator and cmd/tools, so a deploy-to-stg build pushed all
four to <registry>/nvca:<tag> and the last one won. When a subtree has
more than one plain `image` target, the package leaf now names the
repository, with the leaf equal to the service keeping the service name.
Subtrees with a single `image` target keep mapping to the service
wherever that target lives.

Co-Authored-By: Balaji Ganesan <bganesan@nvidia.com>
(cherry picked from commit 94b4dd0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deploy-to-stg Build and push a dev image to ncp-dev on every push to this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nvca: namespace GC deletes the model-cache init namespace because it carries the miniservice instance-type label

1 participant