From 0f92ef33797af768d42243ab1c20b8b11a5273b2 Mon Sep 17 00:00:00 2001 From: Benoit Tigeot Date: Thu, 13 Nov 2025 14:05:42 +0100 Subject: [PATCH] fix(template): add helpful hint to kubeVersion error when using defaults On Slack a user reported that he didn't understand why `helm template` was reporting v1.20.0. He had no Kubernetes cluster connection. This is because we fallback on default Kube version in capabilities.go. This change tracks whether the KubeVersion is a default fallback and adds a helpful hint to the error message only when the default is used (not when users explicitly set --kube-version). Before: ``` Error: chart requires kubeVersion: >= 1.28.0-0 which is incompatible with Kubernetes v1.20.0 ``` After (with default): ``` Error: chart requires kubeVersion: >= 1.28.0-0 which is incompatible with Kubernetes v1.20.0 (no cluster connected, using default version; specify target version with --kube-version) ``` After (with explicit --kube-version 1.27.0): ``` Error: chart requires kubeVersion: >= 1.28.0-0 which is incompatible with Kubernetes v1.27.0 ``` Testing: ```sh $ bin/helm template test-release /tmp/test-chart Error: chart requires kubeVersion: >= 1.28.0-0 which is incompatible with Kubernetes v1.20.0 (no cluster connected, using default version; specify target version with --kube-version) $ bin/helm template test-release /tmp/test-chart --kube-version 1.27.0 Error: chart requires kubeVersion: >= 1.28.0-0 which is incompatible with Kubernetes v1.27.0 $ bin/helm template test-release /tmp/test-chart --kube-version 1.20.0 Error: chart requires kubeVersion: >= 1.28.0-0 which is incompatible with Kubernetes v1.20.0 $ bin/helm template test-release /tmp/test-chart --kube-version 1.28.0 --- apiVersion: v1 kind: ConfigMap ... ``` Signed-off-by: Benoit Tigeot --- pkg/action/action.go | 15 ++++++++------ pkg/action/install.go | 2 +- pkg/chart/common/capabilities.go | 35 +++++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/pkg/action/action.go b/pkg/action/action.go index fd75b85d3..ac0b4fc0b 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -204,7 +204,11 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values common.Values, if ch.Metadata.KubeVersion != "" { if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) { - return hs, b, "", fmt.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) + errMsg := fmt.Sprintf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) + if caps.IsDefaultKubeVersion { + errMsg += " (no cluster connected, using default version; specify target version with --kube-version)" + } + return hs, b, "", fmt.Errorf("%s", errMsg) } } @@ -383,15 +387,14 @@ func (cfg *Configuration) getCapabilities() (*common.Capabilities, error) { } } - cfg.Capabilities = &common.Capabilities{ - APIVersions: apiVersions, - KubeVersion: common.KubeVersion{ + cfg.Capabilities = common.NewCapabilitiesFromCluster( + common.KubeVersion{ Version: kubeVersion.GitVersion, Major: kubeVersion.Major, Minor: kubeVersion.Minor, }, - HelmVersion: common.DefaultCapabilities.HelmVersion, - } + apiVersions, + ) return cfg.Capabilities, nil } diff --git a/pkg/action/install.go b/pkg/action/install.go index 87752684c..dd21f4083 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -304,7 +304,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st // NOTE(bacongobbler): used for `helm template` i.cfg.Capabilities = common.DefaultCapabilities.Copy() if i.KubeVersion != nil { - i.cfg.Capabilities.KubeVersion = *i.KubeVersion + i.cfg.Capabilities = i.cfg.Capabilities.WithKubeVersion(*i.KubeVersion) } i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...) i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: io.Discard} diff --git a/pkg/chart/common/capabilities.go b/pkg/chart/common/capabilities.go index 355c3978a..edffb3b7e 100644 --- a/pkg/chart/common/capabilities.go +++ b/pkg/chart/common/capabilities.go @@ -45,8 +45,9 @@ var ( Major: k8sVersionMajor, Minor: k8sVersionMinor, }, - APIVersions: DefaultVersionSet, - HelmVersion: helmversion.Get(), + APIVersions: DefaultVersionSet, + HelmVersion: helmversion.Get(), + IsDefaultKubeVersion: true, } ) @@ -58,13 +59,37 @@ type Capabilities struct { APIVersions VersionSet // HelmVersion is the build information for this helm version HelmVersion helmversion.BuildInfo + // IsDefaultKubeVersion indicates if KubeVersion is a default fallback value + // (not from a real cluster or user-provided --kube-version flag) + IsDefaultKubeVersion bool } func (capabilities *Capabilities) Copy() *Capabilities { return &Capabilities{ - KubeVersion: capabilities.KubeVersion, - APIVersions: capabilities.APIVersions, - HelmVersion: capabilities.HelmVersion, + KubeVersion: capabilities.KubeVersion, + APIVersions: capabilities.APIVersions, + HelmVersion: capabilities.HelmVersion, + IsDefaultKubeVersion: capabilities.IsDefaultKubeVersion, + } +} + +// WithKubeVersion returns a copy of Capabilities with a custom KubeVersion. +// Sets IsDefaultKubeVersion to false since this is an explicit override. +func (capabilities *Capabilities) WithKubeVersion(kv KubeVersion) *Capabilities { + c := capabilities.Copy() + c.KubeVersion = kv + c.IsDefaultKubeVersion = false + return c +} + +// NewCapabilitiesFromCluster creates a Capabilities from cluster discovery information. +// IsDefaultKubeVersion is set to false since this comes from a real cluster. +func NewCapabilitiesFromCluster(kubeVersion KubeVersion, apiVersions VersionSet) *Capabilities { + return &Capabilities{ + KubeVersion: kubeVersion, + APIVersions: apiVersions, + HelmVersion: DefaultCapabilities.HelmVersion, + IsDefaultKubeVersion: false, } }