From f08c6e842adc9fdc2c6ba00c0957e872c7da09ea Mon Sep 17 00:00:00 2001 From: sowmya-sl Date: Tue, 12 May 2026 13:47:09 +0530 Subject: [PATCH 1/3] HELM-728: Persist auth secret across installs and upgrades Use a RegistryClientSetter interface so applyBasicAuthFromUserCredentials works with both action.Install and action.Upgrade via their shared ChartPathOptions. Persist the basic-auth secret name as a chart annotation (helm.openshift.io/auth-secret) during install and propagate it on upgrade so authenticated registries remain accessible across the release lifecycle. --- pkg/helm/actions/get_chart.go | 2 +- pkg/helm/actions/install_chart.go | 24 +++++++++++++++++++++--- pkg/helm/actions/upgrade_release.go | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/pkg/helm/actions/get_chart.go b/pkg/helm/actions/get_chart.go index bd76446902a..7ae03cd7422 100644 --- a/pkg/helm/actions/get_chart.go +++ b/pkg/helm/actions/get_chart.go @@ -77,7 +77,7 @@ func GetChartFromURL(url string, conf *action.Configuration, namespace string, c if err != nil { return nil, err } - if err := applyBasicAuthFromUserCredentials(cmd, userCredentials); err != nil { + if err := applyBasicAuthFromUserCredentials(&cmd.ChartPathOptions, cmd, userCredentials); err != nil { return nil, err } } diff --git a/pkg/helm/actions/install_chart.go b/pkg/helm/actions/install_chart.go index f206f440413..ed8472fa5f1 100644 --- a/pkg/helm/actions/install_chart.go +++ b/pkg/helm/actions/install_chart.go @@ -14,6 +14,7 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/release" kv1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,6 +49,14 @@ var ( httpURLRe = regexp.MustCompile(`(?i)^https?://` + hostPort + `/.+\.(?:tar\.gz|tgz)$`) ) +const ( + helmAuthSecretAnnotation = "helm.openshift.io/auth-secret" +) + +type RegistryClientSetter interface { + SetRegistryClient(rc *registry.Client) +} + // isValidChartURL validates chart URLs using RFC-compliant hostname labels. // Accepts oci:/// and http(s):///.tgz|tar.gz URLs. func isValidChartURL(raw string) bool { @@ -281,17 +290,25 @@ func GetUserCredentials(coreClient corev1client.CoreV1Interface, ns, secretName } // applyBasicAuthFromSecret sets cmd.Username and cmd.Password from a userCredentials and sets the registry client -func applyBasicAuthFromUserCredentials(cmd *action.Install, userCredentials *UserCredentials) error { +func applyBasicAuthFromUserCredentials(cmd *action.ChartPathOptions, setter RegistryClientSetter, userCredentials *UserCredentials) error { cmd.Username = userCredentials.Username cmd.Password = userCredentials.Password rc, err := GetOCIRegistry(false, false, userCredentials) if err != nil { return fmt.Errorf("failed to configure OCI registry client: %w", err) } - cmd.SetRegistryClient(rc) + setter.SetRegistryClient(rc) return nil } +// addAuthSecretAnnotation adds the auth secret reference to the release annotations via chart metadata. +func addAuthSecretAnnotation(ch *chart.Chart, secretName string) { + if secretName == "" { + return + } + ch.Metadata.Annotations[helmAuthSecretAnnotation] = secretName +} + // InstallChartFromURL installs a chart from an OCI or direct HTTP(S) chart URL. // If not provided, version is extracted from the OCI URL tag when applicable. // basicAuthSecretName names a Secret in ns containing username and password keys for registry auth. @@ -312,7 +329,7 @@ func InstallChartFromURL(ns, name, url string, vals map[string]interface{}, conf if err != nil { return nil, err } - if err := applyBasicAuthFromUserCredentials(cmd, userCredentials); err != nil { + if err := applyBasicAuthFromUserCredentials(&cmd.ChartPathOptions, cmd, userCredentials); err != nil { return nil, err } } @@ -345,6 +362,7 @@ func InstallChartFromURL(ns, name, url string, vals map[string]interface{}, conf } ch.Metadata.Annotations["chart_url"] = url ch.Metadata.Annotations["installation"] = "url_install" + addAuthSecretAnnotation(ch, basicAuthSecretName) go func() { _, err := cmd.Run(ch, vals) if err == nil { diff --git a/pkg/helm/actions/upgrade_release.go b/pkg/helm/actions/upgrade_release.go index 1b4cb9a99de..c12854434f0 100644 --- a/pkg/helm/actions/upgrade_release.go +++ b/pkg/helm/actions/upgrade_release.go @@ -17,6 +17,7 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/dynamic" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/klog/v2" ) func UpgradeRelease( @@ -158,11 +159,15 @@ func UpgradeReleaseAsync( return nil, err } + auth_secret := "" // Before proceeding, check if chart URL is present as an annotation if rel.Chart.Metadata.Annotations != nil { if chart_url, ok := rel.Chart.Metadata.Annotations["chart_url"]; chartUrl == "" && ok { chartUrl = chart_url } + if authSecret, ok := rel.Chart.Metadata.Annotations[helmAuthSecretAnnotation]; ok { + auth_secret = authSecret + } } var tlsFiles []*os.File @@ -224,6 +229,20 @@ func UpgradeReleaseAsync( } ch.Metadata.Annotations["chart_url"] = chartUrl } + if auth_secret != "" { + klog.Infof("Found persisted auth secret %s for release %s/%s, applying credentials for upgrade", auth_secret, releaseNamespace, releaseName) + userCredentials, err := GetUserCredentials(coreClient, releaseNamespace, auth_secret) + if err != nil { + klog.Infof("Failed to get user credentials for release upgrade %s/%s: %v", releaseNamespace, releaseName, err) + + } else { + if err := applyBasicAuthFromUserCredentials(&client.ChartPathOptions, client, userCredentials); err != nil { + klog.Errorf("Failed to apply auth from secret %s for release %s/%s: %v", auth_secret, releaseNamespace, releaseName, err) + // Continue with upgrade but log the error - the upgrade may still work if auth is not required + } + } + + } go func() { _, err := client.Run(releaseName, ch, vals) if err != nil { From e841cadca7a4482c79914b7a8cb34546796774a8 Mon Sep 17 00:00:00 2001 From: sowmya-sl Date: Tue, 26 May 2026 11:07:54 +0530 Subject: [PATCH 2/3] HELM-728: Persist auth secret annotation on chart during upgrade Ensure addAuthSecretAnnotation is called when rewriting chart metadata so subsequent upgrades continue to resolve registry credentials. --- pkg/helm/actions/upgrade_release.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/helm/actions/upgrade_release.go b/pkg/helm/actions/upgrade_release.go index c12854434f0..b4a61f40a55 100644 --- a/pkg/helm/actions/upgrade_release.go +++ b/pkg/helm/actions/upgrade_release.go @@ -228,6 +228,7 @@ func UpgradeReleaseAsync( ch.Metadata.Annotations = make(map[string]string) } ch.Metadata.Annotations["chart_url"] = chartUrl + addAuthSecretAnnotation(ch, auth_secret) } if auth_secret != "" { klog.Infof("Found persisted auth secret %s for release %s/%s, applying credentials for upgrade", auth_secret, releaseNamespace, releaseName) @@ -241,7 +242,6 @@ func UpgradeReleaseAsync( // Continue with upgrade but log the error - the upgrade may still work if auth is not required } } - } go func() { _, err := client.Run(releaseName, ch, vals) From dd8f981908026879e775609865e217ee3746a026 Mon Sep 17 00:00:00 2001 From: sowmya-sl Date: Tue, 26 May 2026 16:27:06 +0530 Subject: [PATCH 3/3] HELM-728: Apply auth credentials before LocateChart on upgrade Credentials were being set after LocateChart, which is the call that actually contacts the registry. Move applyBasicAuthFromUserCredentials before LocateChart so the registry client has auth headers when pulling. --- pkg/helm/actions/upgrade_release.go | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/pkg/helm/actions/upgrade_release.go b/pkg/helm/actions/upgrade_release.go index b4a61f40a55..98bcc17b7d6 100644 --- a/pkg/helm/actions/upgrade_release.go +++ b/pkg/helm/actions/upgrade_release.go @@ -204,6 +204,17 @@ func UpgradeReleaseAsync( } } } + if auth_secret != "" { + klog.Infof("Found persisted auth secret %s for release %s/%s, applying credentials for upgrade", auth_secret, releaseNamespace, releaseName) + userCredentials, err := GetUserCredentials(coreClient, releaseNamespace, auth_secret) + if err != nil { + klog.Errorf("Failed to get user credentials for release upgrade %s/%s: %v", releaseNamespace, releaseName, err) + } else { + if err := applyBasicAuthFromUserCredentials(&client.ChartPathOptions, client, userCredentials); err != nil { + klog.Errorf("Failed to apply auth from secret %s for release %s/%s: %v", auth_secret, releaseNamespace, releaseName, err) + } + } + } chartLocation = chartUrl client.ChartPathOptions.Version = chartInfo.Version cp, err = client.ChartPathOptions.LocateChart(chartLocation, settings) @@ -230,19 +241,6 @@ func UpgradeReleaseAsync( ch.Metadata.Annotations["chart_url"] = chartUrl addAuthSecretAnnotation(ch, auth_secret) } - if auth_secret != "" { - klog.Infof("Found persisted auth secret %s for release %s/%s, applying credentials for upgrade", auth_secret, releaseNamespace, releaseName) - userCredentials, err := GetUserCredentials(coreClient, releaseNamespace, auth_secret) - if err != nil { - klog.Infof("Failed to get user credentials for release upgrade %s/%s: %v", releaseNamespace, releaseName, err) - - } else { - if err := applyBasicAuthFromUserCredentials(&client.ChartPathOptions, client, userCredentials); err != nil { - klog.Errorf("Failed to apply auth from secret %s for release %s/%s: %v", auth_secret, releaseNamespace, releaseName, err) - // Continue with upgrade but log the error - the upgrade may still work if auth is not required - } - } - } go func() { _, err := client.Run(releaseName, ch, vals) if err != nil {