Skip to content
Merged
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
8 changes: 4 additions & 4 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ These structures propagate across every provider and engine. Changing them in a
| Surface | Why it's load-bearing |
|---|---|
| `pkg/topology/` — `Graph`, the `Vertex` tree, and topology constants | Every provider returns it; every engine consumes it. A shape change ripples to all of them. |
| Helm `provider.name` / `engine.name` / `topologyNodeLabels` | External contract for operators deploying Topograph. |
| The four default label keys `network.topology.nvidia.com/{accelerator,leaf,spine,core}` | Consumed by downstream projects (KAI Scheduler, NVSentinel, Kueue). |
| Helm `provider.name` / `engine.name` | External contract for operators deploying Topograph. |
| The variable fabric labels `network.topology.nvidia.com/tier-N` and single accelerator label `network.topology.nvidia.com/accelerator` | Consumed by downstream projects (KAI Scheduler, NVSentinel, Kueue); fabric tier 0 is closest to the node. |

## 2. Setup and Installation

Expand Down Expand Up @@ -138,7 +138,7 @@ type Provider interface {
}
```

A provider returns a `*topology.Graph` of the discovered topology. `Tiers` is the root of the switch hierarchy; `Domains` is a `topology.DomainMap` mapping accelerator/block domains to hosts, with each finalized domain carrying the enumerated ID used by block-topology output. Leaf vertices are compute nodes; interior tier vertices are switches. Return `*httperr.Error` so the API server can propagate the correct HTTP status code — plain `error` is not acceptable at this boundary.
A provider returns a `*topology.Graph` of the discovered topology. Providers using `ClusterTopology` populate `InstanceTopology.FabricTiers` closest-first and the optional single `InstanceTopology.AcceleratorID`, then call `ToGraph`; the fabric path has no fixed depth. `Graph.Tiers` is the fabric hierarchy, and `Graph.Domains` is the `topology/block` source. Leaf vertices are compute nodes; interior tier vertices are switches. Return `*httperr.Error` so the API server can propagate the correct HTTP status code — plain `error` is not acceptable at this boundary.

### Adding a new provider

Expand Down Expand Up @@ -169,7 +169,7 @@ Engines are much rarer (four exist: slurm, k8s, nfd, slinky). Follow the same re

### Label and annotation reference

Label keys written by the Kubernetes and Slinky engines are documented in `docs/reference/node-labels.md`. Do not invent new keys in provider or engine code — values flow through the canonical graph; keys are configured via Helm `topologyNodeLabels`.
Label keys written by the Kubernetes engine are documented in `docs/reference/node-labels.md`. Do not invent new keys in provider code — values flow through the canonical graph. Optional custom keys are configured through the k8s engine's closest-first `fabricLabels` array and singular `acceleratorLabel`; when `fabricLabels` is provided, only explicitly listed fabric tiers are labeled.

## 5. Pull Request Guidelines

Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ These structures propagate across every provider and engine. Changing them in a
| Surface | Why it's load-bearing |
|---|---|
| `pkg/topology/` — `Graph`, the `Vertex` tree, and topology constants | Every provider returns it; every engine consumes it. A shape change ripples to all of them. |
| Helm `provider.name` / `engine.name` / `topologyNodeLabels` | External contract for operators deploying Topograph. |
| The four default label keys `network.topology.nvidia.com/{accelerator,leaf,spine,core}` | Consumed by downstream projects (KAI Scheduler, NVSentinel, Kueue). |
| Helm `provider.name` / `engine.name` | External contract for operators deploying Topograph. |
| The variable fabric labels `network.topology.nvidia.com/tier-N` and single accelerator label `network.topology.nvidia.com/accelerator` | Consumed by downstream projects (KAI Scheduler, NVSentinel, Kueue); fabric tier 0 is closest to the node. |

## 2. Setup and Installation

Expand Down Expand Up @@ -138,7 +138,7 @@ type Provider interface {
}
```

A provider returns a `*topology.Graph` of the discovered topology. `Tiers` is the root of the switch hierarchy; `Domains` is a `topology.DomainMap` mapping accelerator/block domains to hosts, with each finalized domain carrying the enumerated ID used by block-topology output. Leaf vertices are compute nodes; interior tier vertices are switches. Return `*httperr.Error` so the API server can propagate the correct HTTP status code — plain `error` is not acceptable at this boundary.
A provider returns a `*topology.Graph` of the discovered topology. Providers using `ClusterTopology` populate `InstanceTopology.FabricTiers` closest-first and the optional single `InstanceTopology.AcceleratorID`, then call `ToGraph`; the fabric path has no fixed depth. `Graph.Tiers` is the fabric hierarchy, and `Graph.Domains` is the `topology/block` source. Leaf vertices are compute nodes; interior tier vertices are switches. Return `*httperr.Error` so the API server can propagate the correct HTTP status code — plain `error` is not acceptable at this boundary.

### Adding a new provider

Expand Down Expand Up @@ -169,7 +169,7 @@ Engines are much rarer (four exist: slurm, k8s, nfd, slinky). Follow the same re

### Label and annotation reference

Label keys written by the Kubernetes and Slinky engines are documented in `docs/reference/node-labels.md`. Do not invent new keys in provider or engine code — values flow through the canonical graph; keys are configured via Helm `topologyNodeLabels`.
Label keys written by the Kubernetes engine are documented in `docs/reference/node-labels.md`. Do not invent new keys in provider code — values flow through the canonical graph. Optional custom keys are configured through the k8s engine's closest-first `fabricLabels` array and singular `acceleratorLabel`; when `fabricLabels` is provided, only explicitly listed fabric tiers are labeled.

## 5. Pull Request Guidelines

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Changed

- **BREAKING:** Fabric topology now uses variable, closest-first tiers labeled `network.topology.nvidia.com/tier-N`; `InstanceTopology.FabricTiers` and graph conversion support arbitrary fabric depth. Accelerator topology remains a single `AcceleratorID`/`Graph.Domains` dimension labeled `network.topology.nvidia.com/accelerator`. The fixed `leaf`, `spine`, and `core` keys and process-wide Helm/CLI label overrides are replaced by optional `fabricLabels` and `acceleratorLabel` parameters on the `k8s` engine.
- Simulation model node names are now treated as hostnames; the model-backed test provider generates their instance IDs with an `i-` prefix.
- The node-observer and node-data-broker are now rendered directly by the main Topograph Helm chart instead of local subcharts. Their existing `node-observer.*` and `node-data-broker.*` values paths are unchanged.
- **BREAKING (Helm chart `0.5.0` → `0.6.0`):** the chart now ships a hardened default security context across the API server, node-observer, and node-data-broker: non-root (`runAsNonRoot`, UID/GID `65532`), `seccompProfile: RuntimeDefault`, `allowPrivilegeEscalation: false`, `readOnlyRootFilesystem: true`, and all capabilities dropped — satisfying the Kubernetes `restricted` Pod Security Standard out of the box. This changes the default runtime posture of every workload; operators who relied on root, a writable rootfs, or added capabilities must override the relevant keys (see the migration note below). `appVersion` is unchanged (`v0.5.0`; no binary change).
Expand Down
1 change: 1 addition & 0 deletions charts/topograph/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
NOTE: The NFD engine writes NodeFeature and NodeFeatureGroup resources in
{{ .Values.nfdNamespace }}. This must be the namespace where NFD master runs.
{{- end }}

{{- if not .Values.serviceAccount.create }}

2. Topograph is configured to use the existing ServiceAccount "{{ include "topograph.serviceAccountName" . }}".
Expand Down
3 changes: 0 additions & 3 deletions charts/topograph/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ spec:
- /usr/local/bin/topograph
args:
- -v={{ .Values.verbosity }}
{{- range $key, $value := .Values.topologyNodeLabels }}
- -k8s-topology-key-{{ $key }}={{ $value }}
{{- end }}
{{- if or .Values.nodeDataBroker.enabled $hasGcpCredentials $isNFDEngine .Values.env }}
env:
{{- if .Values.nodeDataBroker.enabled }}
Expand Down
8 changes: 1 addition & 7 deletions charts/topograph/tests/deployment_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,14 @@ tests:
name: NODE_DATA_BROKER_NAMESPACE
template: templates/deployment.yaml

- it: passes the verbosity flag and topology key args
- it: passes the verbosity flag
set:
verbosity: 5
topologyNodeLabels:
accelerator: network.topology.nvidia.com/accelerator
asserts:
- contains:
path: spec.template.spec.containers[0].args
content: -v=5
template: templates/deployment.yaml
- contains:
path: spec.template.spec.containers[0].args
content: -k8s-topology-key-accelerator=network.topology.nvidia.com/accelerator
template: templates/deployment.yaml

- it: renders user-supplied environment variables, initContainers, and lifecycle
set:
Expand Down
3 changes: 1 addition & 2 deletions charts/topograph/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
},
"params": {
"type": "object",
"description": "Engine-specific parameters. For slinky, useGpuCliqueLabel=true reads nvidia.com/gpu.clique as the topology/block domain source."
"description": "Engine-specific parameters. The k8s engine accepts a closest-first fabricLabels array and a singular acceleratorLabel. Omitted values use the default label keys; when fabricLabels is provided, additional fabric tiers are omitted. For slinky, useGpuCliqueLabel=true reads nvidia.com/gpu.clique as the topology/block domain source."
}
},
"required": ["name"]
Expand Down Expand Up @@ -119,7 +119,6 @@
"initContainers": { "type": "array" },
"lifecycle": { "type": "object" },
"config": { "type": "object" },
"topologyNodeLabels": { "type": "object" },
"podAnnotations": { "type": "object" },
"podLabels": { "type": "object" },
"podSecurityContext": { "type": "object" },
Expand Down
6 changes: 0 additions & 6 deletions charts/topograph/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,6 @@ config:
# Optional secret with API credentials
# credentialsSecret:

#topologyNodeLabels:
# accelerator: network.topology.nvidia.com/accelerator
# leaf: network.topology.nvidia.com/leaf
# spine: network.topology.nvidia.com/spine
# core: network.topology.nvidia.com/core

podAnnotations: {}
podLabels: {}

Expand Down
8 changes: 0 additions & 8 deletions cmd/topograph/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,13 @@ import (

"github.com/NVIDIA/topograph/internal/version"
"github.com/NVIDIA/topograph/pkg/config"
"github.com/NVIDIA/topograph/pkg/engines/k8s"
"github.com/NVIDIA/topograph/pkg/server"
)

func main() {
var cfg string
var labelAccelerator, labelLeaf, labelSpine, labelCore string
var ver bool
flag.StringVar(&cfg, "c", "/etc/topograph/topograph-config.yaml", "config file")
flag.StringVar(&labelAccelerator, "k8s-topology-key-accelerator", k8s.DefaultLabelAccelerator, "K8s node label for accelerated network type")
flag.StringVar(&labelLeaf, "k8s-topology-key-leaf", k8s.DefaultLabelLeaf, "K8s node label for the cluster's lower network tier")
flag.StringVar(&labelSpine, "k8s-topology-key-spine", k8s.DefaultLabelSpine, "K8s node label for the cluster's middle network tier")
flag.StringVar(&labelCore, "k8s-topology-key-core", k8s.DefaultLabelCore, "K8s node label for the cluster's top network tier")
flag.BoolVar(&ver, "version", false, "show the version")

klog.InitFlags(nil)
Expand All @@ -52,8 +46,6 @@ func main() {
os.Exit(0)
}

k8s.InitLabels(labelAccelerator, labelLeaf, labelSpine, labelCore)

if err := mainInternal(cfg); err != nil {
klog.Error(err.Error())
os.Exit(1)
Expand Down
2 changes: 2 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ Topograph exposes three endpoints for interacting with the service. Below are th
- **namespace**: Used in: [`slinky`]. The required namespace where the SLURM cluster is running. The NFD namespace is deployment-scoped and cannot be supplied in a topology request; Helm deployments configure it with the top-level `nfdNamespace` value.
- **podSelector**: Used in: [`slinky`]. A required Kubernetes label selector for pods running SLURM nodes.
- **nodeSelector**: (optional) Used in: [`k8s`, `nfd`, `slinky`]. A Kubernetes node label map that filters which nodes participate in topology generation.
- **fabricLabels**: (optional) Used in: [`k8s`]. Closest-first array of Kubernetes label keys for fabric tiers. If omitted, every discovered fabric tier uses its default numbered key; if provided, tiers beyond the array are omitted.
- **acceleratorLabel**: (optional) Used in: [`k8s`]. Kubernetes label key for the single accelerator domain. Defaults to `network.topology.nvidia.com/accelerator`.
- **topologyConfigmapName**: Used in: [`slinky`]. The required name of the ConfigMap containing the topology config.
- **useDynamicNodes**: (optional) Used in: [`slinky`]. If `true`, Kubernetes nodes matched by the Node Selector will be annotated with the topology spec.
- **useGpuCliqueLabel**: (optional) Used in: [`slinky`]. If `true`, `topology/block` domains are built from the GPU Operator's `nvidia.com/gpu.clique` node label instead of provider accelerator-domain data.
Expand Down
32 changes: 16 additions & 16 deletions docs/design/nfd-engine-sdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Implemented.
Add an experimental `nfd` engine that converts Topograph's canonical
`topology.Graph` into Node Feature Discovery (NFD) `NodeFeatureGroup` objects.
The engine creates one group for each distinct topology label value, such as one
group for each accelerator domain, leaf switch, spine switch, and core switch.
group for each distinct fabric-tier or accelerator value.

This should not replace the current `k8s` engine. The `k8s` engine writes node
labels that can be consumed by native Kubernetes affinity and topology-aware
Expand All @@ -21,9 +21,9 @@ NFD CRs for consumers that already watch NFD.
Topograph already maps topology into four Kubernetes label dimensions:

- `network.topology.nvidia.com/accelerator`
- `network.topology.nvidia.com/leaf`
- `network.topology.nvidia.com/spine`
- `network.topology.nvidia.com/core`
- `network.topology.nvidia.com/tier-0`
- `network.topology.nvidia.com/tier-1`
- `network.topology.nvidia.com/tier-2`

NFD `NodeFeatureGroup` is an alpha NFD API. NFD master watches
`NodeFeatureGroup` objects, evaluates their feature-group rules, and writes the
Expand Down Expand Up @@ -72,9 +72,9 @@ spec:
topograph.network:
elements:
accelerator: nvl3
leaf: leaf-12
spine: spine-2
core: core-1
fabric-tier-0: leaf-12
fabric-tier-1: spine-2
fabric-tier-2: core-1
```

Example generated group for one leaf switch:
Expand All @@ -83,20 +83,20 @@ Example generated group for one leaf switch:
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeatureGroup
metadata:
name: topograph-leaf-x3f91c4a0
name: topograph-fabric-tier-0-leaf-12-...
labels:
app.kubernetes.io/managed-by: topograph
topograph.nvidia.com/group-type: leaf
topograph.nvidia.com/group-type: fabric-tier-0
annotations:
topograph.nvidia.com/label-key: network.topology.nvidia.com/leaf
topograph.nvidia.com/label-key: network.topology.nvidia.com/tier-0
topograph.nvidia.com/label-value: leaf-12
spec:
featureGroupRules:
- name: leaf equals leaf-12
- name: fabric-tier-0 equals leaf-12
matchFeatures:
- feature: topograph.network
matchExpressions:
leaf:
fabric-tier-0:
op: In
value: ["leaf-12"]
```
Expand Down Expand Up @@ -127,7 +127,7 @@ returns an error if `NFD_NAMESPACE` is unset or blank.
- Add `pkg/engines/nfd` with the standard `NamedLoader`.
- Register it in `pkg/registry/registry.go`.
- Factor the current `k8s` label projection into a shared helper so both engines
produce identical accelerator, leaf, spine, and core values.
produce identical values at every discovered fabric tier and accelerator domain.
- Use the dynamic Kubernetes client or generated NFD client types, depending on
whether the project wants to pin an NFD API dependency.
- Update Helm RBAC to allow create, update, patch, list, watch, and delete for
Expand All @@ -151,7 +151,7 @@ label name is the same on every node, but the label value differs from one switc
to another.

The current Kubernetes label model handles this naturally: pod affinity can use
`topologyKey: network.topology.nvidia.com/leaf`, and Kubernetes compares values
`topologyKey: network.topology.nvidia.com/tier-0`, and Kubernetes compares values
on candidate nodes. `NodeFeatureGroup` exposes precomputed groups instead, so
the consumer needs extra logic to choose among them.

Expand Down Expand Up @@ -190,7 +190,7 @@ overhead.
Assumptions:

- 10,000 `NodeFeature` objects, one per node.
- Four topology attributes per node: accelerator, leaf, spine, and core.
- One topology attribute for every discovered fabric tier and accelerator domain.
- Each node appears in one `NodeFeatureGroup.status.nodes` list per topology
dimension, so status contains about 40,000 node references total.
- Average node names and topology values are short, roughly 10-30 characters.
Expand Down Expand Up @@ -220,7 +220,7 @@ small patches to reduce write amplification.

## Test Plan

- Unit-test graph-to-group generation for accelerator, leaf, spine, and core.
- Unit-test graph-to-group generation across variable fabric tiers and accelerator domains.
- Verify long and invalid topology values produce stable CR names.
- Verify stale Topograph-managed groups are removed when `cleanup` is enabled.
- Verify an empty generated object set returns an error and preserves existing
Expand Down
7 changes: 6 additions & 1 deletion docs/engines/graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ The engine preserves the provider/engine boundary: providers still discover topo

## Output

By default, the generated JSON is returned in the `/v1/topology` response:
By default, the generated JSON is returned in the `/v1/topology` response.

Each instance entry contains:
- `id` — the provider instance ID
- `network_layers` — switch names or IDs in closest-first order (tier 0 is the switch directly connected to the node, increasing outward); the depth is variable and provider-dependent
- `labels` — per-instance topology labels inherited from the model or provider (e.g., accelerator domain)

```json
{
Expand Down
Loading
Loading