Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion pkg/helm/actions/get_chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
24 changes: 21 additions & 3 deletions pkg/helm/actions/install_chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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://<registry>/<path> and http(s)://<host>/<path>.tgz|tar.gz URLs.
func isValidChartURL(raw string) bool {
Expand Down Expand Up @@ -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.
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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 {
Expand Down
19 changes: 19 additions & 0 deletions pkg/helm/actions/upgrade_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -224,6 +229,20 @@ func UpgradeReleaseAsync(
}
ch.Metadata.Annotations["chart_url"] = chartUrl
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should helmAuthSecretAnnotation also be written here alongside chart_url? My thinking is that on a second upgrade, the annotation would already be gone from the stored release, so the secret lookup would fail. Could be wrong though, curious what you think. I have not tested out the changes myself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch, I have added it now.

}
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
}
}

}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
go func() {
_, err := client.Run(releaseName, ch, vals)
if err != nil {
Expand Down