Skip to content

test(deployments): add kind/kubeconfig integration tests for k8s backend (AIRCORE-757 phase 7) - #581

Merged
tylersbray merged 3 commits into
mainfrom
757-k8s-phase7-kind-it/tbray
Jul 7, 2026
Merged

tylersbray merged 3 commits into
mainfrom
757-k8s-phase7-kind-it/tbray

Conversation

@tylersbray

@tylersbray tylersbray commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds kind/kubeconfig-backed integration tests for the k8s deployments backend (AIRCORE-757 Phase 7), mirroring the existing Docker integration-test layout: PVC lifecycle, Job success, ConfigMap mount round-trip, Deployment+Service readiness, and label-gated delete rejection.
  • Adds a reconciler-level integration test (test_reconcile_k8s.py) exercising a puller→server prerequisite chain against a real cluster. PVC-mount gating is intentionally not covered here — kind's default local-path StorageClass uses WaitForFirstConsumer binding, which deadlocks against DeploymentReconciler's requirement that mounted volumes already be BOUND before pod creation (documented in the test's docstring and in the README).
  • Adds a kubeconfig_availability.py skip gate (mirrors docker_availability.py) so these tests skip cleanly in CI when no cluster is reachable — no new CI job required for this phase's closure.
  • Extends conftest.py's xdist grouping so k8s and docker integration tests run in separate serialized groups (they share cluster/daemon state within their own group but don't collide with each other).
  • Documents local validation (against kind or dev-blue) in the plugin README's new "Cluster validation" section.

Branched ahead of Phase 6

This branches from origin/main rather than waiting for Phase 6 (#575, RBAC + Helm chart integration) to merge. Phase 6's RBAC change only matters when the real nmp-core controller pod acts through its restricted ServiceAccount; these integration tests instantiate K8sDeploymentBackend directly in pytest, authenticated with the developer's own kubeconfig — not that constrained ServiceAccount — so there's no functional dependency. #575 was open/mergeable with green CI, blocked only on reviewer availability. Will rebase onto main once #575 lands (both touch the plugin README, but only via non-conflicting appends).

Test plan

  • uv run --frozen pytest plugins/nemo-deployments/tests/unit -q — 251 passed
  • uv run --frozen pytest plugins/nemo-deployments/tests/integration -v against a local kind cluster — 10 passed (5 docker + 5 k8s, including the new tests)
  • uv run pre-commit run -a on changed files — ruff, ty, copyright headers, merge-conflict checks all pass
  • Bugbot review pass — found and fixed a prerequisite-chain race (puller loop broke on status == SUCCEEDED without also waiting on exit_code, which the succeeded prerequisite condition requires)
  • CI: confirm these tests skip cleanly (no kubeconfig in the runner) rather than failing

Summary by CodeRabbit

  • New Features
    • Added “Cluster validation” documentation for running Kubernetes-backed integration checks (local kind and remote clusters), including kubeconfig/reachability behavior and namespace configuration.
    • Expanded Kubernetes integration coverage for PVC lifecycle, deployment job outcomes, ConfigMap mount verification, readiness/endpoint checks, and protection against deleting foreign resources.
    • Added an end-to-end reconciliation test validating prerequisite gating between two Kubernetes deployments.
  • Bug Fixes
    • Improved integration-test skipping and parallel grouping so Kubernetes tests run only when a real cluster is reachable.
  • Documentation
    • Documented expected coverage gaps and assumptions for PVC/gating edge cases.

@tylersbray
tylersbray requested review from a team as code owners July 6, 2026 21:29
@github-actions github-actions Bot added the test conventional-commit type label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f322b83e-ee53-49b6-b702-c62068730bd4

📥 Commits

Reviewing files that changed from the base of the PR and between 91bd3ab and cd2a341.

📒 Files selected for processing (1)
  • plugins/nemo-deployments/README.md
✅ Files skipped from review due to trivial changes (1)
  • plugins/nemo-deployments/README.md

📝 Walkthrough

Walkthrough

Adds Kubernetes cluster-gated integration tests for K8sDeploymentBackend and DeploymentReconciler, plus shared kubeconfig checks, xdist grouping, and README instructions for running them against a real cluster.

Changes

Kubernetes integration test suite

Layer / File(s) Summary
Kubeconfig availability and xdist grouping
plugins/nemo-deployments/tests/integration/kubeconfig_availability.py, plugins/nemo-deployments/tests/integration/conftest.py
Adds cluster reachability checks, a skip marker, and separate xdist grouping for Kubernetes integration tests.
Backend test setup and helpers
plugins/nemo-deployments/tests/integration/backends/k8s/test_k8s_backend.py
Adds module gating, shared namespace/label constants, backend fixtures, mocked entity resolution, helper constructors, and polling helpers.
Backend lifecycle and behavior tests
plugins/nemo-deployments/tests/integration/backends/k8s/test_k8s_backend.py
Covers PVC lifecycle, Never job completion, ConfigMap mount reads, readiness plus Service lookup, and foreign-resource deletion rejection.
Reconciler prerequisite chain
plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py
Adds a K8s-backed registry fixture and an end-to-end prerequisite-gating test for puller/server reconciliation.
Cluster validation documentation
plugins/nemo-deployments/README.md
Documents prerequisites and commands for running the Kubernetes integration tests against kind or dev-blue, plus coverage caveats.

Possibly related PRs

Suggested labels: feat

Suggested reviewers: mckornfield, benmccown

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding kind/kubeconfig-backed integration tests for the k8s deployments backend.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 757-k8s-phase7-kind-it/tbray

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (4)
plugins/nemo-deployments/README.md (1)

60-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a prerequisites block before the commands.

This section jumps straight into execution steps; list kind, kubectl, uv, and cluster access/kubeconfig requirements first, then the kind/dev-blue variants. As per coding guidelines, prerequisites should appear at the top of markdown docs.

🤖 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 `@plugins/nemo-deployments/README.md` around lines 60 - 96, The Cluster
validation section jumps straight into run commands, but it needs a
prerequisites block first. Update the README content around the integration test
instructions to add the required tools and setup at the top: kind, kubectl, uv,
and valid cluster access/kubeconfig. Then keep the existing Local run against
kind and Run against dev-blue subsections beneath that prerequisites section.

Source: Coding guidelines

plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py (2)

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

from __future__ import annotations defers annotations to strings.

As per coding guidelines, **/*.py should prefer concrete type hints over string-based ones. This future import makes all annotations in this module deferred/stringified at runtime, which cuts against that guideline. If this matches an existing repo-wide convention elsewhere in the plugin, feel free to disregard.

🤖 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 `@plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py` at line 16,
The module-level future import in test_reconcile_k8s.py is causing annotations
to be deferred/stringified, which conflicts with the preference for concrete
type hints. Remove the from __future__ import annotations line and, if needed,
update any affected annotations in the test module to use regular
runtime-resolved types so the test file follows the repo’s typing convention.

Source: Coding guidelines


92-102: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Silent fallback masks name typos.

return puller_dep if name == "puller" else server_dep — any unexpected name (e.g. a typo) silently resolves to server_dep instead of failing loudly.

🔧 Proposed tightening
         if entity_type is Deployment:
-            return puller_dep if name == "puller" else server_dep
+            if name == "puller":
+                return puller_dep
+            if name == "server":
+                return server_dep
+            raise KeyError(name)
🤖 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 `@plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py` around
lines 92 - 102, The get_side_effect helper in the test currently falls back to
server_dep for any Deployment name other than "puller", which can hide typos and
make the test pass incorrectly. Update the Deployment branch in get_side_effect
to explicitly handle only the expected names ("puller" and "server") and raise
an error for any unexpected name so failures are loud and obvious; keep the
DeploymentConfig lookup unchanged.
plugins/nemo-deployments/tests/integration/kubeconfig_availability.py (1)

10-20: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Import-time cluster probe runs for every integration collection, not just k8s tests.

This module does a real network call (get_code(_request_timeout=5)) as a side effect of import. Since conftest.py imports this unconditionally to populate __all__, every integration test session — including docker-only runs — pays this latency/timeout cost even when no k8s test is selected.

Consider deferring the check (e.g., compute lazily behind a cached function/fixture, or only import this module from the k8s-specific test files rather than the shared conftest.py).

🤖 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 `@plugins/nemo-deployments/tests/integration/kubeconfig_availability.py` around
lines 10 - 20, The kubeconfig availability probe in KUBECONFIG_AVAILABLE is
running a real cluster call at import time, which impacts every integration
collection. Move the VersionApi().get_code(_request_timeout=5) check behind a
lazy, cached helper or fixture instead of executing it on module import, and
update the shared conftest.py/import path so only k8s-specific tests trigger the
probe.
🤖 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 `@plugins/nemo-deployments/tests/integration/backends/k8s/test_k8s_backend.py`:
- Around line 123-134: The _wait_for_status helper in test_k8s_backend should
fail explicitly instead of silently exhausting retries; if
k8s_backend.read_status never returns a status in terminal_statuses after the
loop in _wait_for_status, raise an assertion or timeout error that includes the
name and number of attempts. Keep the existing polling logic, but add a clear
timeout path so the calling tests get an immediate, descriptive failure rather
than a later ambiguous assertion.
- Around line 138-155: The cleanup in test_pvc_lifecycle is masking failures
because the assert in the finally block can replace the original assertion error
from created.status or read.status. Update the test to avoid asserting inside
the cleanup path: keep the delete_volume call in finally for teardown, but do
any status verification outside cleanup or guard it so cleanup errors do not
override the primary failure. Use test_pvc_lifecycle, create_volume,
read_volume_status, and delete_volume to locate the fix.

In `@plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py`:
- Around line 135-137: The cleanup in the test's finally block only deletes
deployments sequentially, so a failure in backend.delete_deployment("itest",
"puller") prevents backend.delete_deployment("itest", "server") from running.
Update the cleanup around the backend.delete_deployment calls to isolate
failures per resource (for example by wrapping each deletion in its own
exception handling or using a safer teardown pattern) so both the puller and
server cleanup always get a chance to run.

---

Nitpick comments:
In `@plugins/nemo-deployments/README.md`:
- Around line 60-96: The Cluster validation section jumps straight into run
commands, but it needs a prerequisites block first. Update the README content
around the integration test instructions to add the required tools and setup at
the top: kind, kubectl, uv, and valid cluster access/kubeconfig. Then keep the
existing Local run against kind and Run against dev-blue subsections beneath
that prerequisites section.

In `@plugins/nemo-deployments/tests/integration/kubeconfig_availability.py`:
- Around line 10-20: The kubeconfig availability probe in KUBECONFIG_AVAILABLE
is running a real cluster call at import time, which impacts every integration
collection. Move the VersionApi().get_code(_request_timeout=5) check behind a
lazy, cached helper or fixture instead of executing it on module import, and
update the shared conftest.py/import path so only k8s-specific tests trigger the
probe.

In `@plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py`:
- Line 16: The module-level future import in test_reconcile_k8s.py is causing
annotations to be deferred/stringified, which conflicts with the preference for
concrete type hints. Remove the from __future__ import annotations line and, if
needed, update any affected annotations in the test module to use regular
runtime-resolved types so the test file follows the repo’s typing convention.
- Around line 92-102: The get_side_effect helper in the test currently falls
back to server_dep for any Deployment name other than "puller", which can hide
typos and make the test pass incorrectly. Update the Deployment branch in
get_side_effect to explicitly handle only the expected names ("puller" and
"server") and raise an error for any unexpected name so failures are loud and
obvious; keep the DeploymentConfig lookup unchanged.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2c3c1ab1-ebdd-4fc3-8b7b-dc3f1686383d

📥 Commits

Reviewing files that changed from the base of the PR and between 954c403 and 8cc29bb.

📒 Files selected for processing (5)
  • plugins/nemo-deployments/README.md
  • plugins/nemo-deployments/tests/integration/backends/k8s/test_k8s_backend.py
  • plugins/nemo-deployments/tests/integration/conftest.py
  • plugins/nemo-deployments/tests/integration/kubeconfig_availability.py
  • plugins/nemo-deployments/tests/integration/test_reconcile_k8s.py

@github-actions

github-actions Bot commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 23273/30443 76.4% 61.2%
Integration Tests 13617/29123 46.8% 20.0%

tylersbray added a commit that referenced this pull request Jul 6, 2026
Fixes 3 actionable review comments on #581:
- _wait_for_status now fails explicitly with an attempt count instead of
  silently returning on timeout, so callers get a clear message instead of
  a later ambiguous assertion failure.
- test_pvc_lifecycle no longer asserts inside its finally block, so a
  cleanup-path failure can't mask the real assertion failure from the
  try block.
- test_reconcile_k8s's teardown deletes puller and server concurrently via
  asyncio.gather(return_exceptions=True) so a failure deleting one doesn't
  leak the other.

Also addresses 2 nitpicks:
- README: added a prerequisites block ahead of the kind/dev-blue run
  commands in the new Cluster validation section.
- test_reconcile_k8s's get_side_effect now raises KeyError for any
  Deployment name other than "puller"/"server" instead of silently
  falling back to server_dep, so a typo fails loudly.

Left 2 nitpicks as-is (noted in the PR): the `from __future__ import
annotations` import and the import-time cluster/daemon availability probe
both match the pre-existing docker_availability.py /
test_reconcile_docker.py convention already used throughout this test
suite; fixing only the new k8s files would create inconsistency, and a
broader refactor of the shared pattern is out of scope here.

Signed-off-by: Tyler Bray <tbray@nvidia.com>
@tylersbray

Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit's feedback in 72a53c5:

Actionable comments (all fixed):

  • _wait_for_status now calls pytest.fail(...) on timeout instead of silently returning, so a stuck poll fails with a clear "did not reach {statuses} within {attempts} attempts" message.
  • test_pvc_lifecycle's terminal assertion moved outside the finally block so a cleanup failure can't mask the real assertion error.
  • test_reconcile_k8s.py's teardown now deletes puller and server concurrently via asyncio.gather(..., return_exceptions=True) so a failure deleting one doesn't leak the other.

Nitpicks (fixed):

  • README: added a prerequisites block (kind/kubectl/uv/kubeconfig) ahead of the run commands in the new "Cluster validation" section.
  • test_reconcile_k8s.py's get_side_effect now raises KeyError for any Deployment name other than "puller"/"server" instead of silently falling back to server_dep.

Nitpicks (left as-is, noted for reviewers):

  • from __future__ import annotations — this matches the established convention already used in every file in this test suite, including the pre-existing docker_availability.py and test_reconcile_docker.py. Removing it only from the new k8s files would create inconsistency within the same directory.
  • Import-time cluster probe in kubeconfig_availability.py — this mirrors the identical, already-accepted pattern in docker_availability.py (which does the same real network call via docker.from_env().ping() at import time). A lazy-check refactor would need to touch both modules together and is out of scope for this test-parity PR.

Verified after the fix: 251 unit tests + all 10 integration tests (5 docker + 5 k8s) pass locally against a kind cluster, and pre-commit run is clean.

…end (AIRCORE-757 phase 7)

Mirror the existing Docker integration-test layout for the k8s
deployments backend: PVC lifecycle, Job success, ConfigMap mount
round-trip, Deployment+Service readiness, and label-gated delete
rejection, plus a reconciler-level prerequisite-chain test. All tests
skip cleanly when no kubeconfig/cluster is reachable, matching the
Docker suite's CI behavior.

Branched from origin/main ahead of Phase 6 (#575) landing, since these
tests instantiate K8sDeploymentBackend directly with the developer's
own kubeconfig rather than the restricted nmp-core ServiceAccount that
Phase 6's RBAC changes target -- no functional dependency between the
two. Will rebase onto main once #575 merges.

Signed-off-by: Tyler Bray <tbray@nvidia.com>
Fixes 3 actionable review comments on #581:
- _wait_for_status now fails explicitly with an attempt count instead of
  silently returning on timeout, so callers get a clear message instead of
  a later ambiguous assertion failure.
- test_pvc_lifecycle no longer asserts inside its finally block, so a
  cleanup-path failure can't mask the real assertion failure from the
  try block.
- test_reconcile_k8s's teardown deletes puller and server concurrently via
  asyncio.gather(return_exceptions=True) so a failure deleting one doesn't
  leak the other.

Also addresses 2 nitpicks:
- README: added a prerequisites block ahead of the kind/dev-blue run
  commands in the new Cluster validation section.
- test_reconcile_k8s's get_side_effect now raises KeyError for any
  Deployment name other than "puller"/"server" instead of silently
  falling back to server_dep, so a typo fails loudly.

Left 2 nitpicks as-is (noted in the PR): the `from __future__ import
annotations` import and the import-time cluster/daemon availability probe
both match the pre-existing docker_availability.py /
test_reconcile_docker.py convention already used throughout this test
suite; fixing only the new k8s files would create inconsistency, and a
broader refactor of the shared pattern is out of scope here.

Signed-off-by: Tyler Bray <tbray@nvidia.com>
@tylersbray
tylersbray force-pushed the 757-k8s-phase7-kind-it/tbray branch from 72a53c5 to 91bd3ab Compare July 7, 2026 18:51

@mckornfield mckornfield 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.

some funny wording things, yay integration and not e2e tests.

Comment thread plugins/nemo-deployments/README.md Resolved
Addresses mckornfield's PR #581 nitpick that "ambient" reads oddly out
of context.

Signed-off-by: tbray <tbray@nvidia.com>
@tylersbray
tylersbray added this pull request to the merge queue Jul 7, 2026
Merged via the queue into main with commit 1942d40 Jul 7, 2026
55 checks passed
@tylersbray
tylersbray deleted the 757-k8s-phase7-kind-it/tbray branch July 7, 2026 23:40
arpitsardhana pushed a commit that referenced this pull request Jul 9, 2026
…end (AIRCORE-757 phase 7) (#581)

* test(deployments): add kind/kubeconfig integration tests for k8s backend (AIRCORE-757 phase 7)

Mirror the existing Docker integration-test layout for the k8s
deployments backend: PVC lifecycle, Job success, ConfigMap mount
round-trip, Deployment+Service readiness, and label-gated delete
rejection, plus a reconciler-level prerequisite-chain test. All tests
skip cleanly when no kubeconfig/cluster is reachable, matching the
Docker suite's CI behavior.

Branched from origin/main ahead of Phase 6 (#575) landing, since these
tests instantiate K8sDeploymentBackend directly with the developer's
own kubeconfig rather than the restricted nmp-core ServiceAccount that
Phase 6's RBAC changes target -- no functional dependency between the
two. Will rebase onto main once #575 merges.

Signed-off-by: Tyler Bray <tbray@nvidia.com>

* test(deployments): address CodeRabbit feedback on k8s integration tests

Fixes 3 actionable review comments on #581:
- _wait_for_status now fails explicitly with an attempt count instead of
  silently returning on timeout, so callers get a clear message instead of
  a later ambiguous assertion failure.
- test_pvc_lifecycle no longer asserts inside its finally block, so a
  cleanup-path failure can't mask the real assertion failure from the
  try block.
- test_reconcile_k8s's teardown deletes puller and server concurrently via
  asyncio.gather(return_exceptions=True) so a failure deleting one doesn't
  leak the other.

Also addresses 2 nitpicks:
- README: added a prerequisites block ahead of the kind/dev-blue run
  commands in the new Cluster validation section.
- test_reconcile_k8s's get_side_effect now raises KeyError for any
  Deployment name other than "puller"/"server" instead of silently
  falling back to server_dep, so a typo fails loudly.

Left 2 nitpicks as-is (noted in the PR): the `from __future__ import
annotations` import and the import-time cluster/daemon availability probe
both match the pre-existing docker_availability.py /
test_reconcile_docker.py convention already used throughout this test
suite; fixing only the new k8s files would create inconsistency, and a
broader refactor of the shared pattern is out of scope here.

Signed-off-by: Tyler Bray <tbray@nvidia.com>

* docs(deployments): clarify ambient kubeconfig wording in README

Addresses mckornfield's PR #581 nitpick that "ambient" reads oddly out
of context.

Signed-off-by: tbray <tbray@nvidia.com>

---------

Signed-off-by: Tyler Bray <tbray@nvidia.com>
Signed-off-by: tbray <tbray@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test conventional-commit type

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants