feat(status): add component provenance tracking to environment status#635
Conversation
Add ComponentsStatus to EnvironmentStatus that tracks how each component was provisioned (source type, version, branch, git repo/ref/commit). Populated after successful provisioning and displayed in `holodeck describe`. - New types: ComponentProvenance, ComponentsStatus - Provisioner.Run() now returns (*ComponentsStatus, error) - CLI describe shows source details (git commit, branch tracking) - Supports CTK and K8s multi-source; driver/runtime ready for Phase 1/2 Closes: NVIDIA#567 (Phase 3 — provenance tracking) Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Pull Request Test Coverage Report for Build 21917678567Details
💛 - Coveralls |
There was a problem hiding this comment.
Pull request overview
Adds provenance tracking for provisioned components by extending the Environment status API, returning component provenance from the provisioner, and surfacing those details in holodeck describe.
Changes:
- Add
ComponentProvenance,ComponentsStatus, andEnvironmentStatus.Componentsto capture per-component source/version/ref/commit metadata. - Change
Provisioner.Run()to return(*ComponentsStatus, error)and persist this status from CLI flows. - Update
holodeck describetable output to include provenance details (git/latest hints, commit/ref/branch, etc.).
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
api/holodeck/v1alpha1/types.go |
Adds new status types/fields for component provenance tracking. |
pkg/provisioner/provisioner.go |
Updates Run() signature and returns component provenance status. |
pkg/provisioner/provenance.go |
Introduces BuildComponentsStatus() helper to build provenance from spec. |
pkg/provisioner/provenance_test.go |
Adds unit tests for BuildComponentsStatus(). |
cmd/cli/create/create.go |
Captures ComponentsStatus after provisioning and stores it in env status. |
cmd/cli/update/update.go |
Captures ComponentsStatus after provisioning and stores it in env status. |
cmd/cli/describe/describe.go |
Merges status provenance into describe output and enhances table formatting. |
pkg/provisioner/cluster.go |
Updates cluster provisioning to accommodate new Run() return signature. |
cmd/action/ci/entrypoint.go |
Updates CI action provisioning call for new Run() signature. |
tests/aws_test.go |
Updates E2E test to handle new Run() return value. |
| if env.Spec.Kubernetes.Install { | ||
| k8sVersion := env.Spec.Kubernetes.KubernetesVersion | ||
| if env.Spec.Kubernetes.Release != nil { | ||
| k8sVersion = env.Spec.Kubernetes.Release.Version | ||
| } | ||
| output.Components.Kubernetes = &KubernetesInfo{ | ||
| info := &KubernetesInfo{ | ||
| Install: true, | ||
| Installer: env.Spec.Kubernetes.KubernetesInstaller, | ||
| Version: k8sVersion, | ||
| Source: string(env.Spec.Kubernetes.Source), | ||
| } | ||
| if info.Source == "" { | ||
| info.Source = "release" | ||
| } | ||
| if env.Status.Components != nil && env.Status.Components.Kubernetes != nil { | ||
| p := env.Status.Components.Kubernetes | ||
| if p.Repo != "" { | ||
| info.Repo = p.Repo | ||
| } | ||
| if p.Ref != "" { | ||
| info.Ref = p.Ref | ||
| } | ||
| if p.Commit != "" { | ||
| info.Commit = p.Commit | ||
| } | ||
| if p.Branch != "" { | ||
| info.Branch = p.Branch | ||
| } | ||
| } |
There was a problem hiding this comment.
Similarly, the Kubernetes describe output only pulls repo/ref/commit/branch from env.Status.Components.Kubernetes and doesn’t override Source/Version when status is present. If provisioning resolves a specific commit/version (or applies defaults), describe may not reflect what was actually installed. Consider using status to override Source/Version (and potentially Installer if it can differ) when those fields are populated.
| // formatSourceDetail builds a parenthetical detail string showing source provenance. | ||
| // Examples: " (package)", " (git, abc12345)", " (latest, main)" | ||
| func formatSourceDetail(source, ref, commit, branch string) string { | ||
| if source == "" || source == "package" || source == "release" { | ||
| return "" | ||
| } | ||
| parts := source | ||
| if commit != "" { | ||
| parts += ", " + commit | ||
| } else if ref != "" { | ||
| parts += ", " + ref | ||
| } else if branch != "" { | ||
| parts += ", " + branch | ||
| } | ||
| return " (" + parts + ")" | ||
| } |
There was a problem hiding this comment.
The new provenance formatting logic (formatSourceDetail and its usage in printTableFormat) isn’t covered by unit tests in this package. Adding focused tests for key combinations (package/release vs git/latest, commit vs ref vs branch, and CDI-enabled toolkit) would help prevent regressions in CLI output.
| switch prov.Source { | ||
| case "package": | ||
| if nct.Package != nil { | ||
| prov.Version = nct.Package.Version | ||
| } else if nct.Version != "" { | ||
| prov.Version = nct.Version | ||
| } | ||
| case "git": | ||
| if nct.Git != nil { | ||
| prov.Repo = nct.Git.Repo | ||
| prov.Ref = nct.Git.Ref | ||
| } | ||
| case "latest": | ||
| if nct.Latest != nil { | ||
| prov.Branch = nct.Latest.Track | ||
| prov.Repo = nct.Latest.Repo | ||
| } |
There was a problem hiding this comment.
BuildComponentsStatus currently leaves default provenance details empty for CTK git/latest when the spec omits optional fields. For example, if CTK git is used with an empty repo, or CTK latest is used with Latest=nil, the templates default repo/track branch (e.g., main + the default NVIDIA repo), but this status won’t reflect that. Consider applying the same defaults as templates.NewContainerToolkit so describe/status provenance matches what was actually provisioned.
| switch prov.Source { | ||
| case "release": | ||
| if k.Release != nil { | ||
| prov.Version = k.Release.Version | ||
| } else if k.KubernetesVersion != "" { | ||
| prov.Version = k.KubernetesVersion | ||
| } | ||
| case "git": | ||
| if k.Git != nil { | ||
| prov.Repo = k.Git.Repo | ||
| prov.Ref = k.Git.Ref | ||
| } | ||
| case "latest": | ||
| if k.Latest != nil { | ||
| prov.Branch = k.Latest.Track | ||
| prov.Repo = k.Latest.Repo | ||
| } |
There was a problem hiding this comment.
BuildComponentsStatus doesn’t apply the same default repo/track branch behavior as templates.NewKubernetes for git/latest sources. If Kubernetes git is used with an empty repo, or latest is used with Latest=nil, the provisioner defaults (kubernetes.git repo, master) will be used but won’t be captured in status provenance. Consider filling prov.Repo/prov.Branch with the same defaults as templates.NewKubernetes so provenance is accurate.
| if env.Status.Components != nil && env.Status.Components.Runtime != nil { | ||
| p := env.Status.Components.Runtime | ||
| info.Source = p.Source | ||
| if p.Repo != "" { | ||
| info.Repo = p.Repo | ||
| } | ||
| if p.Ref != "" { | ||
| info.Ref = p.Ref | ||
| } | ||
| if p.Commit != "" { | ||
| info.Commit = p.Commit | ||
| } | ||
| if p.Branch != "" { | ||
| info.Branch = p.Branch | ||
| } |
There was a problem hiding this comment.
buildDescribeOutput merges status provenance for the container runtime, but it never overrides Version from env.Status.Components.Runtime when it is present. That means describe output can show a stale/incorrect runtime version if provisioning resolved/installed a different version. Consider taking Version (and any other populated provenance fields) from status with precedence over spec, similar to the driver block above.
| if env.Spec.NVIDIAContainerToolkit.Install { | ||
| output.Components.ContainerToolkit = &ContainerToolkitInfo{ | ||
| info := &ContainerToolkitInfo{ | ||
| Install: true, | ||
| Source: string(env.Spec.NVIDIAContainerToolkit.Source), | ||
| Version: env.Spec.NVIDIAContainerToolkit.Version, | ||
| EnableCDI: env.Spec.NVIDIAContainerToolkit.EnableCDI, | ||
| } | ||
| if info.Source == "" { | ||
| info.Source = "package" | ||
| } | ||
| if env.Status.Components != nil && env.Status.Components.Toolkit != nil { | ||
| p := env.Status.Components.Toolkit | ||
| if p.Repo != "" { | ||
| info.Repo = p.Repo | ||
| } | ||
| if p.Ref != "" { | ||
| info.Ref = p.Ref | ||
| } | ||
| if p.Commit != "" { | ||
| info.Commit = p.Commit | ||
| } | ||
| if p.Branch != "" { | ||
| info.Branch = p.Branch | ||
| } | ||
| } |
There was a problem hiding this comment.
buildDescribeOutput merges provenance from env.Status.Components.Toolkit, but it does not override Source/Version from status (only repo/ref/commit/branch). If provisioning sets a resolved Source/Version (or defaults), describe output will still reflect spec values instead of the post-provisioning status. Consider applying the same precedence rules as the driver block (use non-empty status fields to overwrite spec fields).
Resolves gocritic ifElseChain lint warning in formatSourceDetail. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Summary
ComponentsStatustoEnvironmentStatusthat tracks how each component was provisioned (source type, version, branch, git repo/ref/commit)Provisioner.Run()now returns(*ComponentsStatus, error)with provenance after provisioningholodeck describeshows source details for all components (e.g.,Container Toolkit: v1.17.3 (git, abc12345))Context
Part of #567 (Phase 3 — Provenance Tracking). This PR is independent of the driver/runtime multi-source phases and can be merged on its own.
New Types
ComponentProvenance— tracks source, version, branch, repo, ref, commitComponentsStatus— per-component provenance (driver, runtime, toolkit, kubernetes)EnvironmentStatus.Components— new optional fieldChanges
provisioner.Run()signature:(*ComponentsStatus, error)— all callers updatedBuildComponentsStatus()— builds provenance from environment specformatSourceDetail()helper for clean output formattingTest plan
BuildComponentsStatuscovering all component/source combinationsgo build ./...passesgo vet ./...passesholodeck describeshows provenance after provisioning