Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions isvctl/configs/providers/vcluster/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# vCluster Provider — ISV NCP Validation

This provider runs the full ISV NCP Kubernetes validation suite against a
[vCluster](https://www.vcluster.com) tenant cluster provisioned on top of an
existing Kubernetes Control Plane Cluster with NVIDIA GPU nodes.

## What is vCluster?

vCluster is a CNCF-certified open-source project from vCluster Labs that
provisions isolated tenant clusters on top of any Kubernetes cluster. Each
tenant cluster has its own virtual control plane (API server, scheduler,
controller manager) while sharing the host cluster's nodes and GPU hardware.
vCluster is CNCF-certified for Kubernetes 1.28-1.35 across three configurations:
[vcluster-standalone](https://github.com/cncf/k8s-conformance/tree/master/v1.35/vcluster-standalone),
[vcluster-with-private-nodes](https://github.com/cncf/k8s-conformance/tree/master/v1.35/vcluster-with-private-nodes),
and [vcluster-with-shared-nodes](https://github.com/cncf/k8s-conformance/tree/master/v1.35/vcluster-with-shared-nodes).
This provider validates the **shared-nodes** topology (`sync.fromHost.nodes`),
which is the configuration that enables tenant clusters to schedule GPU
workloads onto the Control Plane Cluster's physical GPU nodes.

## Prerequisites

| Tool | Version | Purpose |
|------|---------|---------|
| `vcluster` CLI | >= 0.34 | Create / connect to tenant cluster |
| `kubectl` | >= 1.28 | Kubernetes client |
| `helm` | >= 3.14 | GPU Operator (when required) + NIM charts |
| `jq` | any | JSON parsing in setup / teardown scripts |

Environment variables:

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `KUBECONFIG` | yes | — | Kubeconfig pointing at the **Control Plane Cluster** |
| `NGC_API_KEY` | yes | — | NVIDIA NGC API key (image pulls + NIM deployment) |
| `VCLUSTER_NAME` | no | `vcluster-isv-validation` | Tenant cluster name |
| `VCLUSTER_NAMESPACE` | no | `vcluster-isv-validation` | Host namespace for the tenant cluster |
| `VCLUSTER_KUBECONFIG_PATH` | no | `/tmp/vcluster-isv-validation.kubeconfig` | Where setup writes the tenant kubeconfig |
| `CLOUD_PROVIDER` | no | auto-detected | Set `gke` to enable GKE-specific GPU handling |
| `SKIP_PREFLIGHT` | no | `false` | Skip GPU readiness waits (for pre-warmed clusters) |

## GPU Architecture

vCluster syncs GPU resources from the Control Plane Cluster into each tenant
cluster via `sync.fromHost.nodes.enabled=true`. This makes GPU node capacity,
labels, and taints visible to workloads scheduled in the tenant cluster.

**GKE COS nodes**: containerd is configured with the NVIDIA device plugin model
(no `nvidia` runtime handler). GPU pods use only `nvidia.com/gpu` resource
limits — no `runtimeClassName: nvidia` in pod specs.

**GPU Operator**: When the Control Plane Cluster's GPU Operator is already
running (`nvidia.com/gpu.present=true` on host nodes), `setup.sh` creates only
the `gpu-operator` namespace and a pause-image Deployment in the tenant instead
of installing the full chart. This avoids duplicate DaemonSet pod registrations
on host nodes while satisfying `K8sGpuOperatorNamespaceCheck` and
`K8sGpuOperatorPodsCheck`.

## Running the Validation Suite

```bash
# Full run (setup → test → teardown) in one command:
NGC_API_KEY="$(cat /path/to/ngc-api-key)" \
KUBECTL="kubectl --kubeconfig=/tmp/vcluster-isv-validation.kubeconfig" \
isvctl test run -f isvctl/configs/providers/vcluster/config/k8s.yaml

# Setup only (creates the tenant cluster; does not run tests):
isvctl test run -f isvctl/configs/providers/vcluster/config/k8s.yaml --phase setup

# Test only (tenant cluster must already exist from a prior setup):
KUBECTL="kubectl --kubeconfig=/tmp/vcluster-isv-validation.kubeconfig" \
isvctl test run -f isvctl/configs/providers/vcluster/config/k8s.yaml --phase test

# Teardown only:
isvctl test run -f isvctl/configs/providers/vcluster/config/k8s.yaml --phase teardown
```

## CNCF Conformance Skips

The `K8sCncfConformanceCheck` runs in `certified-conformance` mode (the full
441-test `[Conformance]` suite). 28 test patterns are skipped across 15
architectural limitation groups. All skips are specific to the
`sync.fromHost.nodes` topology required for GPU node sharing; on dedicated
nodes with `virtualScheduler.enabled`, vCluster passes the full conformance
suite with zero skips.

The main limitation categories are:

- **HostPorts**: vCluster does not map virtual ports to physical host interfaces.
- **NodePort IP mismatch**: `sync.fromHost.nodes` reports pod-CIDR IPs as node
InternalIPs, which differ from the VPC IPs where kube-proxy binds.
- **Read-only synced nodes**: Label and extended-resource patches on synced nodes
are overwritten by the sync cycle; NodeSelector and preemption tests that
patch node objects fail.
- **PV sync race**: Eventual-consistency PV synchronization causes transient
"not found" errors in two PersistentVolume lifecycle tests.
- **CRD conversion webhook isolation**: The virtual API server cannot reach
webhook pods in the tenant namespace across the control-plane/workload
network boundary.
- **Runtime and DNS**: RuntimeClass objects are not synced to the host cluster;
CoreDNS ExternalName CNAME forwarding does not satisfy conformance expectations
in the GKE DNS topology.

See `config/k8s.yaml` for the full per-pattern justification.

## Scripts

| Script | Purpose |
|--------|---------|
| `scripts/k8s/setup.sh` | Create (or reuse) the tenant cluster; install GPU Operator if needed; emit cluster inventory JSON |
| `scripts/k8s/teardown.sh` | Delete the tenant cluster and restore GPU node taints |
148 changes: 148 additions & 0 deletions isvctl/configs/providers/vcluster/config/control-plane.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2026 vCluster Labs
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# vCluster Provider - Control Plane Validation Configuration
#
# Imports the provider-agnostic control-plane suite and wires it to vCluster
# scripts. The conceptual mapping is:
# "tenant" = a vCluster instance (namespace-scoped on the Control Plane Cluster)
# "access_key" = a Kubernetes ServiceAccount + bound token in the host namespace
#
# Environment variables:
# VCLUSTER_NAMESPACE - namespace where vCluster instances live
# (default: vcluster-isv-validation)
# VCLUSTER_HOST_KUBECONFIG - path to the Control Plane Cluster kubeconfig
# (falls back to KUBECONFIG)
#
# Usage:
# isvctl test run -f isvctl/configs/providers/vcluster/config/control-plane.yaml

import:
- ../../../suites/control-plane.yaml

version: "1.0"

commands:
control_plane:
phases: ["setup", "test", "teardown"]
steps:
- name: check_api
phase: setup
command: "python ../scripts/control-plane/check_api.py"
args: ["--region", "{{region}}", "--services", "{{services}}"]
timeout: 120

- name: create_access_key
phase: setup
command: "python ../scripts/control-plane/create_access_key.py"
args: ["--region", "{{region}}"]
timeout: 60

- name: create_tenant
phase: setup
command: "python ../scripts/control-plane/create_tenant.py"
args: ["--region", "{{region}}"]
timeout: 300

- name: test_access_key
phase: test
command: "python ../scripts/control-plane/test_access_key.py"
args:
- "--access-key-id"
- "{{steps.create_access_key.access_key_id}}"
- "--secret-access-key"
- "{{steps.create_access_key.secret_access_key}}"
- "--region"
- "{{region}}"
sensitive_args: ["--secret-access-key"]
timeout: 120

- name: disable_access_key
phase: test
command: "python ../scripts/control-plane/disable_access_key.py"
args:
- "--username"
- "{{steps.create_access_key.username}}"
- "--access-key-id"
- "{{steps.create_access_key.access_key_id}}"
- "--region"
- "{{region}}"
timeout: 60

- name: verify_key_rejected
phase: test
command: "python ../scripts/control-plane/verify_key_rejected.py"
args:
- "--access-key-id"
- "{{steps.create_access_key.access_key_id}}"
- "--secret-access-key"
- "{{steps.create_access_key.secret_access_key}}"
- "--region"
- "{{region}}"
sensitive_args: ["--secret-access-key"]
timeout: 120

- name: list_tenants
phase: test
command: "python ../scripts/control-plane/list_tenants.py"
args:
- "--region"
- "{{region}}"
- "--group-name"
- "{{steps.create_tenant.tenant_name}}"
timeout: 60

- name: get_tenant
phase: test
command: "python ../scripts/control-plane/get_tenant.py"
args:
- "--group-name"
- "{{steps.create_tenant.tenant_name}}"
- "--region"
- "{{region}}"
timeout: 60

- name: delete_access_key
phase: teardown
command: "python ../scripts/control-plane/delete_access_key.py"
args:
- "--username"
- "{{steps.create_access_key.username}}"
- "--access-key-id"
- "{{steps.create_access_key.access_key_id}}"
- "--region"
- "{{region}}"
timeout: 60
output_schema: teardown

- name: delete_tenant
phase: teardown
command: "python ../scripts/control-plane/delete_tenant.py"
args:
- "--group-name"
- "{{steps.create_tenant.tenant_name}}"
- "--region"
- "{{region}}"
timeout: 120
output_schema: teardown

tests:
cluster_name: "vcluster-control-plane-validation"
description: "vCluster tenant cluster lifecycle validation (ServiceAccount-based access keys)"

settings:
region: "vcluster"
services: "compute,identity,kubernetes"
Loading