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
70 changes: 70 additions & 0 deletions api/holodeck/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,69 @@ type ClusterStatus struct {
Phase string `json:"phase,omitempty"`
}

// ComponentProvenance tracks how a component was installed.
type ComponentProvenance struct {
// Source is the installation method used (e.g., "package", "runfile", "git", "latest", "release").
Source string `json:"source"`

// Version is the installed version (if known).
// +optional
// +optional

Version string `json:"version,omitempty"`

// Branch is the package branch or tracked branch.
// +optional
// +optional

Branch string `json:"branch,omitempty"`

// Repo is the git repository URL (for git/latest sources).
// +optional
// +optional

Repo string `json:"repo,omitempty"`

// Ref is the git reference requested (for git sources).
// +optional
// +optional

Ref string `json:"ref,omitempty"`

// Commit is the resolved git commit SHA (for git/latest sources).
// +optional
// +optional

Commit string `json:"commit,omitempty"`
}

// ComponentsStatus tracks provisioned component information.
type ComponentsStatus struct {
// Driver tracks the NVIDIA driver installation provenance.
// +optional
// +optional

Driver *ComponentProvenance `json:"driver,omitempty"`

// Runtime tracks the container runtime installation provenance.
// +optional
// +optional

Runtime *ComponentProvenance `json:"runtime,omitempty"`

// Toolkit tracks the NVIDIA Container Toolkit installation provenance.
// +optional
// +optional

Toolkit *ComponentProvenance `json:"toolkit,omitempty"`

// Kubernetes tracks the Kubernetes installation provenance.
// +optional
// +optional

Kubernetes *ComponentProvenance `json:"kubernetes,omitempty"`
}

// EnvironmentStatus defines the observed state of the infra provider
type EnvironmentStatus struct {
// +listType=map
Expand All @@ -383,6 +446,13 @@ type EnvironmentStatus struct {
// +optional

Cluster *ClusterStatus `json:"cluster,omitempty"`

// Components tracks provenance information for installed components.
// Populated after provisioning with source, version, and commit details.
// +optional
// +optional

Components *ComponentsStatus `json:"components,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
2 changes: 1 addition & 1 deletion cmd/action/ci/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func entrypoint(log *logger.FunLogger) error {
defer p.Client.Close() // nolint: errcheck

log.Info("Provisioning \u2699")
if err = p.Run(cfg); err != nil {
if _, err = p.Run(cfg); err != nil {
return fmt.Errorf("failed to run provisioner: %w", err)
}

Expand Down
10 changes: 6 additions & 4 deletions cmd/cli/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,15 +430,16 @@ func runSingleNodeProvision(log *logger.FunLogger, opts *options) error {
}
defer p.Client.Close() // nolint: errcheck

if err = p.Run(opts.cfg); err != nil {
componentsStatus, runErr := p.Run(opts.cfg)
if runErr != nil {
// Set degraded condition when provisioning fails
opts.cfg.Status.Conditions = []metav1.Condition{
{
Type: v1alpha1.ConditionDegraded,
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: "ProvisioningFailed",
Message: fmt.Sprintf("Failed to provision environment: %v", err),
Message: fmt.Sprintf("Failed to provision environment: %v", runErr),
},
}
data, err := jyaml.MarshalYAML(opts.cfg)
Expand All @@ -448,11 +449,12 @@ func runSingleNodeProvision(log *logger.FunLogger, opts *options) error {
if err := os.WriteFile(opts.cacheFile, data, 0600); err != nil {
return fmt.Errorf("failed to update cache file with provisioning status: %w", err)
}
return fmt.Errorf("failed to run provisioner: %w", err)
return fmt.Errorf("failed to run provisioner: %w", runErr)
}

// Set provisioning status to true after successful provisioning
// Set provisioning status and component provenance after successful provisioning
opts.cfg.Labels[instances.InstanceProvisionedLabelKey] = "true"
opts.cfg.Status.Components = componentsStatus
data, err := jyaml.MarshalYAML(opts.cfg)
if err != nil {
return fmt.Errorf("failed to marshal environment: %w", err)
Expand Down
167 changes: 150 additions & 17 deletions cmd/cli/describe/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,24 @@ type KernelInfo struct {
// NVIDIADriverInfo contains NVIDIA driver configuration
type NVIDIADriverInfo struct {
Install bool `json:"install" yaml:"install"`
Source string `json:"source,omitempty" yaml:"source,omitempty"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
Commit string `json:"commit,omitempty" yaml:"commit,omitempty"`
}

// ContainerRuntimeInfo contains container runtime configuration
type ContainerRuntimeInfo struct {
Install bool `json:"install" yaml:"install"`
Name string `json:"name" yaml:"name"`
Source string `json:"source,omitempty" yaml:"source,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
Commit string `json:"commit,omitempty" yaml:"commit,omitempty"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"`
}

// ContainerToolkitInfo contains NVIDIA Container Toolkit configuration
Expand All @@ -142,6 +151,10 @@ type ContainerToolkitInfo struct {
Source string `json:"source,omitempty" yaml:"source,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
EnableCDI bool `json:"enableCDI" yaml:"enableCDI"`
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
Commit string `json:"commit,omitempty" yaml:"commit,omitempty"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"`
}

// KubernetesInfo contains Kubernetes configuration
Expand All @@ -150,6 +163,10 @@ type KubernetesInfo struct {
Installer string `json:"installer,omitempty" yaml:"installer,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
Source string `json:"source,omitempty" yaml:"source,omitempty"`
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
Ref string `json:"ref,omitempty" yaml:"ref,omitempty"`
Commit string `json:"commit,omitempty" yaml:"commit,omitempty"`
Branch string `json:"branch,omitempty" yaml:"branch,omitempty"`
}

// StatusInfo contains status and conditions
Expand Down Expand Up @@ -345,41 +362,119 @@ func (m command) buildDescribeOutput(instance *instances.Instance, env *v1alpha1
}

if env.Spec.NVIDIADriver.Install {
output.Components.NVIDIADriver = &NVIDIADriverInfo{
info := &NVIDIADriverInfo{
Install: true,
Source: "package",
Branch: env.Spec.NVIDIADriver.Branch,
Version: env.Spec.NVIDIADriver.Version,
}
// Merge provenance from status if available
if env.Status.Components != nil && env.Status.Components.Driver != nil {
p := env.Status.Components.Driver
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.Version != "" {
info.Version = p.Version
}
if p.Branch != "" {
info.Branch = p.Branch
}
}
output.Components.NVIDIADriver = info
}

if env.Spec.ContainerRuntime.Install {
output.Components.ContainerRuntime = &ContainerRuntimeInfo{
info := &ContainerRuntimeInfo{
Install: true,
Name: string(env.Spec.ContainerRuntime.Name),
Source: "package",
Version: env.Spec.ContainerRuntime.Version,
}
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
}
Comment on lines +401 to +415
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
}
output.Components.ContainerRuntime = info
}

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
}
}
Comment on lines 420 to +444
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
output.Components.ContainerToolkit = info
}

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
}
}
Comment on lines 448 to +476
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
output.Components.Kubernetes = info
}

// Status
Expand Down Expand Up @@ -425,6 +520,24 @@ func (m command) buildDescribeOutput(instance *instances.Instance, env *v1alpha1
return output
}

// 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
switch {
case commit != "":
parts += ", " + commit
case ref != "":
parts += ", " + ref
case branch != "":
parts += ", " + branch
}
return " (" + parts + ")"
}
Comment on lines +523 to +539
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

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.

Copilot generated this review using guidance from repository custom instructions.

//nolint:errcheck // stdout writes
func (m command) printTableFormat(d *DescribeOutput) error {
// Instance Information
Expand Down Expand Up @@ -497,42 +610,62 @@ func (m command) printTableFormat(d *DescribeOutput) error {
fmt.Printf("Kernel: %s\n", d.Components.Kernel.Version)
}
if d.Components.NVIDIADriver != nil {
version := d.Components.NVIDIADriver.Version
di := d.Components.NVIDIADriver
version := di.Version
if version == "" {
version = d.Components.NVIDIADriver.Branch
version = di.Branch
}
if version == "" {
version = "latest"
}
fmt.Printf("NVIDIA Driver: %s\n", version)
detail := formatSourceDetail(di.Source, di.Ref, di.Commit, di.Branch)
fmt.Printf("NVIDIA Driver: %s%s\n", version, detail)
}
if d.Components.ContainerRuntime != nil {
version := d.Components.ContainerRuntime.Version
ri := d.Components.ContainerRuntime
version := ri.Version
if version == "" {
version = "latest"
}
fmt.Printf("Container Runtime: %s (%s)\n", d.Components.ContainerRuntime.Name, version)
detail := formatSourceDetail(ri.Source, ri.Ref, ri.Commit, ri.Branch)
fmt.Printf("Container Runtime: %s %s%s\n", ri.Name, version, detail)
}
if d.Components.ContainerToolkit != nil {
version := d.Components.ContainerToolkit.Version
if version == "" {
version = d.Components.ContainerToolkit.Source
ti := d.Components.ContainerToolkit
version := ti.Version
if version == "" && ti.Ref != "" {
version = ti.Ref
}
if version == "" {
version = "latest"
}
cdi := ""
if d.Components.ContainerToolkit.EnableCDI {
cdi = " (CDI enabled)"
if ti.EnableCDI {
cdi = ", CDI"
}
detail := formatSourceDetail(ti.Source, ti.Ref, ti.Commit, ti.Branch)
if cdi != "" && detail != "" {
// Append CDI inside the parentheses
detail = detail[:len(detail)-1] + cdi + ")"
} else if cdi != "" {
detail = " (" + ti.Source + cdi + ")"
}
fmt.Printf("Container Toolkit: %s%s\n", version, cdi)
fmt.Printf("Container Toolkit: %s%s\n", version, detail)
}
if d.Components.Kubernetes != nil {
version := d.Components.Kubernetes.Version
ki := d.Components.Kubernetes
version := ki.Version
if version == "" {
version = "latest"
}
fmt.Printf("Kubernetes: %s (%s)\n", version, d.Components.Kubernetes.Installer)
detail := formatSourceDetail(ki.Source, ki.Ref, ki.Commit, ki.Branch)
if detail == "" {
detail = fmt.Sprintf(" (%s)", ki.Installer)
} else {
// Insert installer into detail
detail = fmt.Sprintf(" (%s, %s", ki.Installer, detail[2:])
}
fmt.Printf("Kubernetes: %s%s\n", version, detail)
}

// AWS Resources
Expand Down
Loading
Loading