Skip to content

fix(device-plugin): resolve stale annotation in multi-container Allocate - #2380

Merged
hami-robot[bot] merged 3 commits into
Project-HAMi:masterfrom
peachest:fix/allocate-stale-annotation-upstream
Aug 7, 2026
Merged

fix(device-plugin): resolve stale annotation in multi-container Allocate#2380
hami-robot[bot] merged 3 commits into
Project-HAMi:masterfrom
peachest:fix/allocate-stale-annotation-upstream

Conversation

@peachest

@peachest peachest commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind bug

What this PR does / why we need it

A Pod with 2 containers both requesting split GPU. If the first container does NOT set CUDA_DISABLE_CONTROL and the second does, the expected behavior is: the first container's libvgpu.so interception takes effect, the second does NOT. But the actual behavior: the second container's CUDA_DISABLE_CONTROL=true is ignored — libvgpu.so stays active.

Root Cause

The Allocate loop decodes the pod annotation on every iteration via GetNextDeviceRequest, but eraseNextDeviceTypeFromAnnotation only patches the API server without updating the in-memory pod.Annotations map. When kubelet passes multiple ContainerRequests in a single Allocate call, every iteration re-reads the original annotation, always returning the first container's devices and env vars.

This is complementary to PR #1299 which fixed the scheduler side (correct annotation indexing for multi-container pods). The device plugin side (Allocate stale annotation) was not fixed.

Fix

Replace the per-iteration decode+erase pattern with a pop-based design:

  • decodePodSingleDevice(dtype, pod) — decode the annotation once before the loop
  • popNextContainerDevices(pod, podSingleDev) — mutate the in-memory slice in place and return the resolved container directly
  • patchErasedAnnotation(pod, dtype, podSingleDev) — patch the API server exactly once after the loop

This also reduces API server pressure: one patch per Allocate call instead of one per container.

Which issue(s) this PR fixes

Fixes #1050

Special notes for your reviewer

  • this fix align to my previous implementation for ascend-device-plugin Allocate method. Now both device-plugin use the same pop-erase in memory-patch mechanism
  • make verify passes (staticcheck warnings are pre-existing in cmd/nvlink-diag/main.go, not introduced by this PR)
  • 14 regression tests added in alloc_refactor_test.go
  • GetNextDeviceRequest is kept as exported (dead code now) to avoid breaking API consumers; can be cleaned up in a follow-up

Does this PR introduce a user-facing change?

Fix: multi-container Pods now correctly honor `CUDA_DISABLE_CONTROL=true` per-container. Previously, the second container's `CUDA_DISABLE_CONTROL` was silently ignored because the Allocate function re-read stale pod annotations on each iteration.

AI Assistance: The code and tests in this PR were primarily authored by pi agent+glm5.2. The solution design, code review, and test planning were directed by the contributor. All code has been reviewed and understood by the contributor.

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Improved NVIDIA GPU allocation for single- and multi-container workloads.
    • Improved handling of MIG, virtual, replica, and initialization-container assignments.
    • Added clearer errors for invalid requests, allocation mismatches, and annotation failures.
    • Preserved allocation state when processing fails, supporting safer retries.
    • Ensured device allocation annotations are updated consistently after successful processing.
  • Tests

    • Expanded coverage for allocation behavior, annotation updates, CUDA controls, error handling, and preferred MIG allocation.

@hami-robot
hami-robot Bot requested review from DSFans2014 and FouoF August 5, 2026 12:04
@github-actions github-actions Bot added the kind/bug Something isn't working label Aug 5, 2026
@hami-robot hami-robot Bot added the size/XXL label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a89a7625-a8ed-42c7-962a-5c98fdd04edb

📥 Commits

Reviewing files that changed from the base of the PR and between c5e99b6 and 63b95fc.

📒 Files selected for processing (4)
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/server.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/util.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/util.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/server.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go

📝 Walkthrough

Walkthrough

The NVIDIA device plugin now decodes GPU annotations once, consumes allocations in memory, and patches the remaining annotation once after processing. Tests cover multi-container allocation, MIG behavior, CUDA control, errors, mounts, and patch behavior.

Changes

NVIDIA allocation flow

Layer / File(s) Summary
Allocation state helpers
pkg/device-plugin/nvidiadevice/nvinternal/plugin/util.go, pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go
Helpers decode annotations, pop the next non-empty container allocation, patch remaining data, and update in-memory annotations. Tests cover selection, mutation, sequential consumption, decoding, and patching.
Allocate integration and persistence
pkg/device-plugin/nvidiadevice/nvinternal/plugin/server.go, pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go, pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go
Allocate decodes once, assigns devices to containers, and performs one final annotation patch. Tests cover responses, CUDA control, device mismatches, patch counts, decode failures, pending-pod failures, and patch failures.
Allocation compatibility and MIG coverage
pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go, pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go
Tests use realistic GPU and MIG identifiers, validate preferred allocation and container ordering, exercise identifier normalization and fallback setup, and cover MIG success and error paths.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Kubelet
  participant Allocate
  participant AllocationHelpers
  participant KubernetesAPI
  Kubelet->>Allocate: submit device requests
  Allocate->>AllocationHelpers: decode annotation once
  loop Each allocated container
    Allocate->>AllocationHelpers: pop next container devices
    AllocationHelpers-->>Allocate: return devices and container
  end
  Allocate->>AllocationHelpers: patch remaining allocation
  AllocationHelpers->>KubernetesAPI: update pod annotation
  Allocate-->>Kubelet: return allocation responses
Loading

Possibly related issues

  • Project-HAMi/HAMi issue 1667: Refactors and tests NVIDIA allocation across regular and init containers.

Possibly related PRs

Suggested reviewers: dsfans2014, fouof

Poem

A rabbit pops each GPU request,
Then stores the remainder with care.
MIG slices follow their paths,
While one patch updates the pod.
Tests check errors, mounts, and order—
Hop safely through allocation!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.24% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary fix for stale annotations during multi-container Allocate processing.
Linked Issues check ✅ Passed The changes address issue #1050 by resolving container-specific allocation state and testing CUDA_DISABLE_CONTROL behavior in multi-container Pods.
Out of Scope Changes check ✅ Passed The implementation and supporting tests remain within the scope of fixing stale annotation handling and CUDA control behavior.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go (1)

50-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Place Project-HAMi imports after external imports.

Move github.com/Project-HAMi/HAMi/pkg/util/client after the github.com/stretchr/testify and k8s.io/... imports. Run goimports with local prefix github.com/Project-HAMi/HAMi.

As per coding guidelines, group standard imports first, external imports second, and github.com/Project-HAMi/HAMi/... imports last; use goimports with local prefix github.com/Project-HAMi/HAMi.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go` around lines
50 - 55, Reorder the imports in the test file so standard-library imports come
first, external dependencies including testify and Kubernetes packages come
next, and the Project-HAMi client import is last; run goimports with
github.com/Project-HAMi/HAMi as the local prefix.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go`:
- Around line 80-84: Update newTestPlugin to capture the existing HOOK_PATH
environment value and hostHookPath before changing them, then register t.Cleanup
callbacks that restore both values after the test. Preserve correct handling of
whether HOOK_PATH was originally set or unset.

---

Nitpick comments:
In `@pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go`:
- Around line 50-55: Reorder the imports in the test file so standard-library
imports come first, external dependencies including testify and Kubernetes
packages come next, and the Project-HAMi client import is last; run goimports
with github.com/Project-HAMi/HAMi as the local prefix.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f10aeec0-2578-441e-a4b9-cbbba06e8136

📥 Commits

Reviewing files that changed from the base of the PR and between e6d0902 and 5566484.

📒 Files selected for processing (4)
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/server.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/server_test.go
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/util.go

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.29412% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...vice-plugin/nvidiadevice/nvinternal/plugin/util.go 80.00% 3 Missing and 2 partials ⚠️
Flag Coverage Δ
unittests 64.26% <85.29%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ce-plugin/nvidiadevice/nvinternal/plugin/server.go 42.55% <100.00%> (+0.40%) ⬆️
...vice-plugin/nvidiadevice/nvinternal/plugin/util.go 37.05% <80.00%> (+5.27%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@peachest
peachest force-pushed the fix/allocate-stale-annotation-upstream branch from 5566484 to 3b8bc61 Compare August 5, 2026 12:13
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go`:
- Around line 614-617: Update the MIG allocation path in
NvidiaDevicePlugin.Allocate to consume the allocation via
popNextContainerDevices before the final patchErasedAnnotation call, so only
remaining annotation state is persisted. Extend the test around Allocate to
re-decode the fake API-server Pod after success and assert that no device
allocation remains, while preserving the existing response-count assertion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 365fd43e-ee93-49b7-8965-45f0aebffc54

📥 Commits

Reviewing files that changed from the base of the PR and between 3b8bc61 and 989f381.

📒 Files selected for processing (1)
  • pkg/device-plugin/nvidiadevice/nvinternal/plugin/alloc_refactor_test.go

Comment thread pkg/device-plugin/nvidiadevice/nvinternal/plugin/server.go Outdated
@peachest

peachest commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

unit test fail due to master branch fail the unit test

@archlitchi

Copy link
Copy Markdown
Member

please resolve these conflicts and sync with master to pass the CI

houyuxi added 3 commits August 7, 2026 15:29
The Allocate loop decoded the pod annotation on every iteration via
GetNextDeviceRequest, but eraseNextDeviceTypeFromAnnotation only patched
the API server without updating the in-memory pod.Annotations map. This
worked under the assumption that kubelet calls Allocate once per
container, but kubelet may pass multiple ContainerRequests in a single
Allocate call. In that case every iteration re-read the original
annotation, always returning the first container's devices and env vars.

This caused CUDA_DISABLE_CONTROL to be ignored on the second (and
subsequent) containers — libvgpu.so interception stayed active even when
the env var was set to true.

Fix: replace the per-iteration decode+erase pattern with a pop-based
design (matching ascend-device-plugin's approach):
- decodePodSingleDevice decodes the annotation once before the loop
- popNextContainerDevices mutates the in-memory slice in place and
  returns the resolved container directly (no bare index leakage)
- patchErasedAnnotation patches the API server exactly once after the
  loop, also updating pod.Annotations in place for kubelet retries

Add 14 regression tests: pop/decode/patch unit tests, decode→pop
integration test, multi-container Allocate end-to-end tests covering
CUDA_DISABLE_CONTROL on first and second containers, device number
mismatch, API patch count verification, and single-container baseline.

Signed-off-by: houyuxi <yuxi.hou@transwarp.io>
Add 7 tests to improve codecov patch coverage above the 61.72% target:

MIG branch:
- MIG success path (device in rm, normal allocation)
- MIG FailRequestsGreaterThanOne (requesting >1 shared annotated device)
- MIG unknown device (device ID not in resource manager)

Error paths:
- getPendingPod returns error
- decodePodSingleDevice with empty annotation
- popNextContainerDevices error (more containers than annotation entries)
- patchErasedAnnotation API failure (fake client reactor)

Allocate coverage: 64.7% → 87.1%

Signed-off-by: houyuxi <yuxi.hou@transwarp.io>
…tate

- Remove explanatory comments above decodePodSingleDevice and
  patchErasedAnnotation in Allocate (requested by @archlitchi)
- Restore HOOK_PATH env and hostHookPath in newTestPlugin via
  t.Cleanup (CodeRabbit finding)

Signed-off-by: houyuxi <yuxi.hou@transwarp.io>
@peachest
peachest force-pushed the fix/allocate-stale-annotation-upstream branch from c1499ff to 63b95fc Compare August 7, 2026 07:30
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@archlitchi archlitchi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/lgtm

@hami-robot hami-robot Bot added the lgtm label Aug 7, 2026
@hami-robot

hami-robot Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: archlitchi, peachest

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CUDA_DISABLE_CONTROL=true has no effect — HAMI still hijacks GPU behavior

2 participants