Files
Olares/cli/pkg/utils/helm.go

259 lines
7.6 KiB
Go

package utils
import (
"context"
"fmt"
"os"
"time"
"helm.sh/helm/v3/pkg/storage/driver"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/pkg/errors"
"helm.sh/helm/v3/pkg/action"
helmLoader "helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/release"
"k8s.io/client-go/rest"
)
// Config helm client config.
type Config struct {
ActionCfg *action.Configuration
Settings *cli.EnvSettings
}
func debug(format string, v ...interface{}) {
if false {
format = fmt.Sprintf("[helm] [debug] %s\n", format)
logger.Debug(fmt.Sprintf(format, v...))
}
}
// InitConfig initializes the configuration for executing actions.
func InitConfig(kubeConfig *rest.Config, namespace string) (*action.Configuration, *cli.EnvSettings, error) {
actionConfig := new(action.Configuration)
var settings = cli.New()
helmDriver := os.Getenv("HELM_DRIVER")
settings.KubeAPIServer = kubeConfig.Host
settings.KubeToken = kubeConfig.BearerToken
settings.KubeInsecureSkipTLSVerify = true
settings.SetNamespace(namespace)
if err := actionConfig.Init(settings.RESTClientGetter(), namespace, helmDriver, debug); err != nil {
logger.Error(err, "helm config init error")
return nil, nil, err
}
return actionConfig, settings, nil
}
// InstallCharts installs helm chart using action config and environment settings.
func InstallCharts(ctx context.Context, actionConfig *action.Configuration, settings *cli.EnvSettings,
appName, chartsName, repoURL, namespace string, vals map[string]interface{}) error {
instClient := action.NewInstall(actionConfig)
if namespace == "" {
instClient.CreateNamespace = false
} else {
instClient.CreateNamespace = true
}
instClient.Namespace = namespace
instClient.Timeout = 300 * time.Second
if repoURL != "" {
instClient.RepoURL = repoURL
}
r, err := runInstall(ctx, []string{appName, chartsName}, instClient, settings, vals)
if err != nil {
// todo uninstall
// delete failed install
// do not need delete release, helm will delete it automatically if failed
// if r != nil {
// deleteCli := hin.newUninstallClient(hin.actionconfig)
// errDel := hin.runUninstall(hin.app.AppName, deleteCli)
// if errDel != nil {
// ctrl.Log.Error(errDel, "delete the app error", "appname", hin.app.AppName, "namespace", hin.app.Namespace)
// }
// }
return err
}
logReleaseInfo(r)
return nil
}
// UpgradeCharts upgrades helm chart using action config and environment settings.
func UpgradeCharts(ctx context.Context, actionConfig *action.Configuration, settings *cli.EnvSettings,
appName, chartName, repoURL, namespace string, vals map[string]interface{}, reuseValue bool) error {
client := action.NewUpgrade(actionConfig)
client.Namespace = namespace
client.Timeout = 300 * time.Second
if reuseValue {
client.ReuseValues = true
}
if repoURL != "" {
client.RepoURL = repoURL
}
r, err := runUpgrade(ctx, []string{appName, chartName}, client, settings, vals)
if err != nil {
if !errors.Is(err, driver.ErrNoDeployedReleases) {
return err
}
return InstallCharts(ctx, actionConfig, settings, appName, chartName, repoURL, namespace, vals)
}
logReleaseUpgrade(r)
return nil
}
// UninstallCharts upgrades helm chart using action config.
func UninstallCharts(cfg *action.Configuration, releaseName string) error {
uninstall := action.NewUninstall(cfg)
uninstall.KeepHistory = false
r, err := uninstall.Run(releaseName)
if err != nil {
if r != nil && r.Release != nil && r.Release.Info != nil &&
r.Release.Info.Status == release.StatusUninstalled {
return nil
}
return err
}
logUninstallReleaseInfo(r)
return nil
}
// RollbackCharts rollback helm chart using action config.
func RollbackCharts(cfg *action.Configuration, releaseName string) error {
rollback := action.NewRollback(cfg)
err := rollback.Run(releaseName)
if err != nil {
return err
}
return nil
}
func runUpgrade(ctx context.Context, args []string, client *action.Upgrade, settings *cli.EnvSettings, vals map[string]interface{}) (*release.Release, error) {
if client.Version == "" && client.Devel {
client.Version = ">0.0.0-0"
}
cp, err := client.ChartPathOptions.LocateChart(args[1], settings)
if err != nil {
return nil, err
}
p := getter.All(settings)
chartRequested, err := helmLoader.Load(cp)
if err != nil {
return nil, err
}
if req := chartRequested.Metadata.Dependencies; req != nil {
if err := action.CheckDependencies(chartRequested, req); err != nil {
if client.DependencyUpdate {
man := &downloader.Manager{
ChartPath: cp,
Keyring: client.ChartPathOptions.Keyring,
SkipUpdate: false,
Getters: p,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
}
if err := man.Update(); err != nil {
return nil, err
}
// Reload the chart with the updated Chart.lock file.
if chartRequested, err = helmLoader.Load(cp); err != nil {
return nil, errors.Wrap(err, "failed reloading chart after repo update")
}
} else {
return nil, err
}
}
}
return client.RunWithContext(ctx, args[0], chartRequested, vals)
}
func runInstall(ctx context.Context, args []string, client *action.Install, settings *cli.EnvSettings, vals map[string]interface{}) (*release.Release, error) {
if client.Version == "" && client.Devel {
client.Version = ">0.0.0-0"
}
name, c, err := client.NameAndChart(args)
if err != nil {
return nil, err
}
client.ReleaseName = name
cp, err := client.ChartPathOptions.LocateChart(c, settings)
if err != nil {
return nil, err
}
p := getter.All(settings)
// Check chart dependencies to make sure all are present in /charts
chartRequested, err := helmLoader.Load(cp)
if err != nil {
return nil, err
}
if req := chartRequested.Metadata.Dependencies; req != nil {
if err := action.CheckDependencies(chartRequested, req); err != nil {
if client.DependencyUpdate {
man := &downloader.Manager{
ChartPath: cp,
Keyring: client.ChartPathOptions.Keyring,
SkipUpdate: false,
Getters: p,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
}
if err := man.Update(); err != nil {
return nil, err
}
// Reload the chart with the updated Chart.lock file.
if chartRequested, err = helmLoader.Load(cp); err != nil {
return nil, errors.Wrap(err, "failed reloading chart after repo update")
}
} else {
return nil, err
}
}
}
return client.RunWithContext(ctx, chartRequested, vals)
}
// ReleaseName returns application release name.
func ReleaseName(appname, owner string) string {
return fmt.Sprintf("%s-%s", appname, owner)
}
func logReleaseInfo(release *release.Release) {
logger.Infow("[helm] app installed success",
"NAME", release.Name,
"LAST DEPLOYED", release.Info.LastDeployed.Format(time.ANSIC),
"NAMESPACE", release.Namespace,
"STATUS", release.Info.Status.String(),
"REVISION", release.Version)
}
func logUninstallReleaseInfo(release *release.UninstallReleaseResponse) {
logger.Infow("[helm] app uninstalled success",
"NAME", release.Release.Name,
"NAMESPACE", release.Release.Namespace,
"INFO", release.Info)
}
func logReleaseUpgrade(release *release.Release) {
logger.Infow("[helm] app upgrade success",
"NAME", release.Name,
"LAST DEPLOYED", release.Info.LastDeployed.Format(time.ANSIC),
"NAMESPACE", release.Namespace,
"STATUS", release.Info.Status.String(),
"REVISION", release.Version)
}