github.com/codefresh-io/kcfi@v0.0.0-20230301195427-c1578715cc46/pkg/action/helm.go (about)

     1  package action
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/codefresh-io/kcfi/pkg/charts"
     6  	c "github.com/codefresh-io/kcfi/pkg/config"
     7  	"github.com/pkg/errors"
     8  	"github.com/stretchr/objx"
     9  	"os"
    10  	"strings"
    11  	"time"
    12  
    13  	helm "helm.sh/helm/v3/pkg/action"
    14  	"helm.sh/helm/v3/pkg/chart"
    15  	"helm.sh/helm/v3/pkg/chart/loader"
    16  	"helm.sh/helm/v3/pkg/chartutil"
    17  	"helm.sh/helm/v3/pkg/cli"
    18  	"helm.sh/helm/v3/pkg/cli/output"
    19  	"helm.sh/helm/v3/pkg/release"
    20  	"helm.sh/helm/v3/pkg/storage/driver"
    21  )
    22  
    23  type HelmChartOptions struct {
    24  	ChartName string
    25  	baseDir   string
    26  	*helm.ChartPathOptions
    27  }
    28  
    29  func NewHelmChartOptionsFromConfig(chartName string, config map[string]interface{}) (*HelmChartOptions, error) {
    30  	cfgX := objx.New(config)
    31  	baseDir := cfgX.Get(c.KeyBaseDir).String()
    32  
    33  	if chartName == "" {
    34  		return nil, fmt.Errorf("Missing chart name in config")
    35  	}
    36  	helmChartOptions := &helm.ChartPathOptions{
    37  		RepoURL: cfgX.Get(c.KeyHelmRepoURL).String(),
    38  		Version: cfgX.Get(c.KeyHelmVersion).String(),
    39  
    40  		Password: cfgX.Get(c.KeyHelmPassword).String(),
    41  		Username: cfgX.Get(c.KeyHelmUsername).String(),
    42  		Verify:   cfgX.Get(c.KeyHelmVerify).Bool(false),
    43  		CaFile:   cfgX.Get(c.KeyHelmCaFile).String(),
    44  		CertFile: cfgX.Get(c.KeyHelmCertFile).String(),
    45  		KeyFile:  cfgX.Get(c.KeyHelmKeyFile).String(),
    46  		Keyring:  cfgX.Get(c.KeyHelmKeyring).String(),
    47  	}
    48  
    49  	return &HelmChartOptions{
    50  		ChartName:        chartName,
    51  		baseDir:          baseDir,
    52  		ChartPathOptions: helmChartOptions,
    53  	}, nil
    54  }
    55  
    56  func (h *HelmChartOptions) LoadChart() (*chart.Chart, error) {
    57  
    58  	settings := cli.New()
    59  	settings.Debug = c.Debug
    60  	if h.baseDir != "" {
    61  		workingDir, _ := os.Getwd()
    62  		os.Chdir(h.baseDir)
    63  		defer os.Chdir(workingDir)
    64  	}
    65  
    66  	var ch *chart.Chart
    67  	var err error
    68  	if chartPath, err := h.ChartPathOptions.LocateChart(h.ChartName, settings); err == nil {
    69  		debug("Using chart path %s", chartPath)
    70  		ch, err = loader.Load(chartPath)
    71  	} else {
    72  		debug("Using embeded chart %s", h.ChartName)
    73  		ch, err = charts.Load(h.ChartName)
    74  	}
    75  	return ch, err
    76  }
    77  
    78  // DeployHelmRelease - deploy helm release using chart from config
    79  func DeployHelmRelease(releaseName string, chart string, vals map[string]interface{}, cfg *helm.Configuration, client *helm.Upgrade) (*release.Release, error) {
    80  
    81  	info("Deploying release %s of chart %s ...", releaseName, chart)
    82  	helmChartOptions, err := NewHelmChartOptionsFromConfig(chart, vals)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	chartRequested, err := helmChartOptions.LoadChart()
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	if chartRequested == nil || chartRequested.Metadata == nil {
    91  		return nil, fmt.Errorf("Failed to load %s chart. Check helm chart options in config", chart)
    92  	}
    93  
    94  	var release *release.Release
    95  
    96  	// Checking if chart already installed and decide to use install or upgrade helm client
    97  	histClient := helm.NewHistory(cfg)
    98  	histClient.Max = 1
    99  	if _, err := histClient.Run(releaseName); err == driver.ErrReleaseNotFound {
   100  		// Only print this to stdout for table output
   101  
   102  		info("Release %q does not exist. Installing it now.\n", releaseName)
   103  
   104  		instClient := helm.NewInstall(cfg)
   105  		instClient.CreateNamespace = false //TODO
   106  		instClient.ChartPathOptions = client.ChartPathOptions
   107  		instClient.DryRun = client.DryRun
   108  		instClient.DisableHooks = client.DisableHooks
   109  		instClient.SkipCRDs = client.SkipCRDs
   110  		instClient.Timeout = client.Timeout
   111  		instClient.Wait = client.Wait
   112  		instClient.Devel = client.Devel
   113  		instClient.Namespace = client.Namespace
   114  		instClient.Atomic = client.Atomic
   115  		instClient.PostRenderer = client.PostRenderer
   116  		instClient.DisableOpenAPIValidation = client.DisableOpenAPIValidation
   117  		instClient.SubNotes = client.SubNotes
   118  
   119  		instClient.ReleaseName = releaseName
   120  
   121  		if chartRequested.Metadata.Deprecated {
   122  			fmt.Println("WARNING: This chart is deprecated")
   123  		}
   124  		return instClient.Run(chartRequested, vals)
   125  
   126  	} else if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	if req := chartRequested.Metadata.Dependencies; req != nil {
   131  		if err := helm.CheckDependencies(chartRequested, req); err != nil {
   132  			return nil, err
   133  		}
   134  	}
   135  
   136  	release, err = client.Run(releaseName, chartRequested, vals)
   137  	if err != nil {
   138  		return nil, errors.Wrapf(err, "UPGRADE of %s FAILED", releaseName)
   139  	}
   140  	info("Release %q has been upgraded\n", releaseName)
   141  
   142  	return release, nil
   143  }
   144  
   145  // IsHelmReleaseInstalled - returns true if helm release installed
   146  func IsHelmReleaseInstalled(releaseName string, cfg *helm.Configuration) bool {
   147  	histClient := helm.NewHistory(cfg)
   148  	histClient.Max = 1
   149  
   150  	if release, err := histClient.Run(releaseName); release != nil {
   151  		debug("release %s is installed", releaseName)
   152  		return true
   153  	} else {
   154  		debug("query release %s returned error %v", releaseName, err)
   155  		return false
   156  	}
   157  }
   158  
   159  // PrintHelmReleaseInfo - prints helm release
   160  func PrintHelmReleaseInfo(release *release.Release, debug bool) error {
   161  	if release == nil {
   162  		return nil
   163  	}
   164  	info("NAME: %s", release.Name)
   165  	if !release.Info.LastDeployed.IsZero() {
   166  		info("LAST DEPLOYED: %s", release.Info.LastDeployed.Format(time.ANSIC))
   167  	}
   168  	info("NAMESPACE: %s", release.Namespace)
   169  	info("STATUS: %s", release.Info.Status.String())
   170  	info("REVISION: %d", release.Version)
   171  
   172  	out := os.Stdout
   173  	if debug {
   174  		info("USER-SUPPLIED VALUES:")
   175  		err := output.EncodeYAML(out, release.Config)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		// Print an extra newline
   180  		fmt.Fprintln(out)
   181  
   182  		cfg, err := chartutil.CoalesceValues(release.Chart, release.Config)
   183  		if err != nil {
   184  			return err
   185  		}
   186  
   187  		fmt.Fprintln(out, "COMPUTED VALUES:")
   188  		err = output.EncodeYAML(out, cfg.AsMap())
   189  		if err != nil {
   190  			return err
   191  		}
   192  		// Print an extra newline
   193  		fmt.Fprintln(out)
   194  	}
   195  
   196  	if strings.EqualFold(release.Info.Description, "Dry run complete") || debug {
   197  		fmt.Fprintln(out, "HOOKS:")
   198  		for _, h := range release.Hooks {
   199  			fmt.Fprintf(out, "---\n# Source: %s\n%s\n", h.Path, h.Manifest)
   200  		}
   201  		fmt.Fprintf(out, "MANIFEST:\n%s\n", release.Manifest)
   202  	}
   203  
   204  	if len(release.Info.Notes) > 0 {
   205  		fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(release.Info.Notes))
   206  	}
   207  	return nil
   208  }
   209  
   210  func GetReleaseValues(releaseName string, cfg *helm.Configuration) (map[string]interface{}, error) {
   211  	debug("Getting values from the release named \"%s\"", releaseName)
   212  	client := helm.NewGetValues(cfg)
   213  	client.AllValues = true
   214  	return client.Run(releaseName)
   215  }