diff --git a/src/helm/helm.go b/src/helm/helm.go index 8d7dc303..b7a0ff1d 100644 --- a/src/helm/helm.go +++ b/src/helm/helm.go @@ -248,12 +248,18 @@ type HelmChartSearchRequest struct { type HelmChartInstallUpgradeRequest struct { Namespace string `json:"namespace" validate:"required"` - Chart string `json:"chart" validate:"required"` - Release string `json:"release" validate:"required"` + // Chart is either a repo-relative reference ("repoName/chartName") or a full + // "oci://..." reference — HelmReleaseUpgrade dispatches on registry.IsOCI(Chart). + Chart string `json:"chart" validate:"required"` + Release string `json:"release" validate:"required"` // Optional fields Version string `json:"version,omitempty"` Values string `json:"values,omitempty"` DryRun bool `json:"dryRun,omitempty"` + // OCI specific fields — only used when Chart is an "oci://" reference. + AuthHost string `json:"authHost,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` } type HelmChartOciInstallUpgradeRequest struct { @@ -275,6 +281,10 @@ type HelmChartShowRequest struct { Chart string `json:"chart" validate:"required"` ShowFormat action.ShowOutputFormat `json:"format" validate:"required"` // "all" "chart" "values" "readme" "crds" Version string `json:"version,omitempty"` // optional, if not set, the latest version will be used + // OCI specific fields — only used when Chart is an "oci://" reference. + AuthHost string `json:"authHost,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` } type HelmChartVersionRequest struct { @@ -843,17 +853,34 @@ func HelmChartShow(data HelmChartShowRequest) (string, error) { return "", err } + // action.Show embeds ChartPathOptions, so LocateChart/SetRegistryClient are + // both called on the same show object — matching how HelmOciInstall and + // HelmReleaseUpgrade's OCI branch wire OCI support into action.Install and + // action.Upgrade, which embed it the same way. + show := action.NewShow(data.ShowFormat, actionConfig) + show.Version = data.Version + + if registry.IsOCI(data.Chart) { + registryClient, err := newRegistryClient(settings, false) + if err != nil { + return "", fmt.Errorf("failed to create OCI registry client: %w", err) + } + if (data.Username != "" || data.Password != "") && data.AuthHost != "" { + if err := registryClient.Login(data.AuthHost, registry.LoginOptBasicAuth(data.Username, data.Password)); err != nil { + return "", fmt.Errorf("failed to login to OCI registry: %w", err) + } + } + show.SetRegistryClient(registryClient) + } + // Fetch the chart - chartPathOptions := action.ChartPathOptions{} - chartPathOptions.Version = data.Version - chartPath, err := chartPathOptions.LocateChart(data.Chart, settings) + chartPath, err := show.LocateChart(data.Chart, settings) if err != nil { helmLogger.Error("HelmShow LocateChart", "error", err.Error()) return "", err } // Show the chart - show := action.NewShow(data.ShowFormat, actionConfig) result, err := show.Run(chartPath) if err != nil { helmLogger.Error("HelmShow Run", "error", err.Error()) @@ -1240,9 +1267,15 @@ func HelmReleaseUpgrade(data HelmChartInstallUpgradeRequest) (result string, err settings := NewCli() settings.SetNamespace(data.Namespace) - helmLogger.Info("Updating repo index ...", "releaseName", data.Release, "namespace", data.Namespace) - if err := helmRepoUpdateForChart(data.Chart); err != nil { - helmLogger.Error("failed to update helm repository", "chart", data.Chart, "error", err.Error()) + isOCI := registry.IsOCI(data.Chart) + + // helmRepoUpdateForChart refreshes a classic repo index — meaningless for an + // OCI reference, which resolves directly against the registry below instead. + if !isOCI { + helmLogger.Info("Updating repo index ...", "releaseName", data.Release, "namespace", data.Namespace) + if err := helmRepoUpdateForChart(data.Chart); err != nil { + helmLogger.Error("failed to update helm repository", "chart", data.Chart, "error", err.Error()) + } } actionConfig := new(action.Configuration) @@ -1272,6 +1305,23 @@ func HelmReleaseUpgrade(data HelmChartInstallUpgradeRequest) (result string, err // resolve to SSA=false, making ForceConflicts invalid. upgrade.ServerSideApply = "true" + // action.Upgrade embeds ChartPathOptions, so SetRegistryClient/LocateChart are + // both called on the same upgrade object — same OCI wiring as HelmOciInstall. + if isOCI { + registryClient, err := newRegistryClient(settings, false) + if err != nil { + return "", fmt.Errorf("failed to create OCI registry client: %w", err) + } + if (data.Username != "" || data.Password != "") && data.AuthHost != "" { + if err := registryClient.Login(data.AuthHost, registry.LoginOptBasicAuth(data.Username, data.Password)); err != nil { + return "", fmt.Errorf("failed to login to OCI registry: %w", err) + } + } else { + helmLogger.Info("No OCI registry credentials provided, attempting anonymous access", "releaseName", data.Release, "namespace", data.Namespace) + } + upgrade.SetRegistryClient(registryClient) + } + helmLogger.Info("Locating chart ...", "releaseName", data.Release, "namespace", data.Namespace) chartPath, err := upgrade.LocateChart(data.Chart, settings) if err != nil {