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 }