Files
portainer/pkg/libhelm/sdk/get.go
2026-03-13 11:34:28 +13:00

132 lines
4.0 KiB
Go

package sdk
import (
"strconv"
"github.com/portainer/portainer/pkg/libhelm/options"
"github.com/portainer/portainer/pkg/libhelm/release"
"github.com/rs/zerolog/log"
"helm.sh/helm/v4/pkg/action"
releasev1 "helm.sh/helm/v4/pkg/release/v1"
)
// Get implements the HelmPackageManager interface by using the Helm SDK to get a release.
// It returns a Release.
func (hspm *HelmSDKPackageManager) Get(getOptions options.GetOptions) (*release.Release, error) {
log.Debug().
Str("context", "HelmClient").
Str("namespace", getOptions.Namespace).
Str("name", getOptions.Name).
Msg("Get Helm release")
actionConfig := new(action.Configuration)
err := hspm.initActionConfig(actionConfig, getOptions.Namespace, getOptions.KubernetesClusterAccess)
if err != nil {
log.Error().
Str("context", "HelmClient").
Str("namespace", getOptions.Namespace).
Err(err).Msg("Failed to initialise helm configuration")
return nil, err
}
statusClient, err := hspm.initStatusClient(actionConfig, getOptions)
if err != nil {
log.Error().
Str("context", "HelmClient").
Str("namespace", getOptions.Namespace).
Err(err).Msg("Failed to initialise helm status client")
return nil, err
}
release, err := statusClient.Run(getOptions.Name)
if err != nil {
log.Error().
Str("context", "HelmClient").
Str("namespace", getOptions.Namespace).
Err(err).Msg("Failed to query helm chart")
return nil, err
}
values, err := hspm.getValues(getOptions)
if err != nil {
// error is already logged in getValuesFromStatus
return nil, err
}
v1Release, err := releaserToV1Release(release)
if err != nil {
log.Error().
Str("context", "HelmClient").
Str("namespace", getOptions.Namespace).
Str("name", getOptions.Name).
Err(err).Msg("Failed to convert release to v1")
return nil, err
}
return convert(v1Release, values), nil
}
// Helm status is just an extended helm get command with resources added on (when flagged), so use the status client with the optional show resources flag
// https://github.com/helm/helm/blob/0199b748aaea3091852d16687c9f9f809061777c/pkg/action/get.go#L40-L47
// https://github.com/helm/helm/blob/0199b748aaea3091852d16687c9f9f809061777c/pkg/action/status.go#L48-L82
func (hspm *HelmSDKPackageManager) initStatusClient(actionConfig *action.Configuration, getOptions options.GetOptions) (*action.Status, error) {
statusClient := action.NewStatus(actionConfig)
if getOptions.Revision > 0 {
statusClient.Version = getOptions.Revision
}
return statusClient, nil
}
func convert(releasev1 *releasev1.Release, values release.Values) *release.Release {
resources, err := parseResources(releasev1.Info.Resources)
if err != nil {
log.Warn().
Str("context", "HelmClient").
Str("namespace", releasev1.Namespace).
Str("name", releasev1.Name).
Err(err).Msg("Failed to parse resources")
}
// Parse stack ID from annotations -> int
stackID := 0
if releasev1.Chart != nil && releasev1.Chart.Metadata != nil {
if s, ok := releasev1.Chart.Metadata.Annotations[StackIDAnnotation]; ok && s != "" {
if id, err := strconv.Atoi(s); err == nil {
stackID = id
} else {
log.Warn().
Str("context", "HelmClient").
Str("namespace", releasev1.Namespace).
Str("name", releasev1.Name).
Err(err).Msg("Failed to parse stack id from annotations")
}
}
}
release := &release.Release{
Name: releasev1.Name,
Namespace: releasev1.Namespace,
Version: releasev1.Version,
Info: &release.Info{
Status: release.Status(releasev1.Info.Status),
Notes: releasev1.Info.Notes,
Resources: resources,
Description: releasev1.Info.Description,
LastDeployed: releasev1.Info.LastDeployed,
},
Manifest: releasev1.Manifest,
Chart: release.Chart{
Metadata: &release.Metadata{
Name: releasev1.Chart.Metadata.Name,
Version: releasev1.Chart.Metadata.Version,
AppVersion: releasev1.Chart.Metadata.AppVersion,
},
},
Values: values,
ChartReference: extractChartReferenceAnnotations(releasev1.Chart.Metadata.Annotations),
StackID: stackID,
}
return release
}