github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/packager/helm/common.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package helm contains operations for working with helm charts. 5 package helm 6 7 import ( 8 "crypto/sha1" 9 "encoding/hex" 10 "fmt" 11 "os" 12 "path" 13 "path/filepath" 14 "strconv" 15 "time" 16 17 "github.com/Racer159/jackal/src/config" 18 "github.com/Racer159/jackal/src/pkg/cluster" 19 "github.com/Racer159/jackal/src/pkg/message" 20 "github.com/Racer159/jackal/src/types" 21 "helm.sh/helm/v3/pkg/action" 22 "helm.sh/helm/v3/pkg/chart" 23 "helm.sh/helm/v3/pkg/cli" 24 ) 25 26 // Helm is a config object for working with helm charts. 27 type Helm struct { 28 chart types.JackalChart 29 chartPath string 30 valuesPath string 31 32 cfg *types.PackagerConfig 33 component types.JackalComponent 34 cluster *cluster.Cluster 35 timeout time.Duration 36 retries int 37 38 kubeVersion string 39 40 chartOverride *chart.Chart 41 valuesOverrides map[string]any 42 43 settings *cli.EnvSettings 44 actionConfig *action.Configuration 45 } 46 47 // Modifier is a function that modifies the Helm config. 48 type Modifier func(*Helm) 49 50 // New returns a new Helm config struct. 51 func New(chart types.JackalChart, chartPath string, valuesPath string, mods ...Modifier) *Helm { 52 h := &Helm{ 53 chart: chart, 54 chartPath: chartPath, 55 valuesPath: valuesPath, 56 timeout: config.JackalDefaultTimeout, 57 } 58 59 for _, mod := range mods { 60 mod(h) 61 } 62 63 return h 64 } 65 66 // NewClusterOnly returns a new Helm config struct geared toward interacting with the cluster (not packages) 67 func NewClusterOnly(cfg *types.PackagerConfig, cluster *cluster.Cluster) *Helm { 68 return &Helm{ 69 cfg: cfg, 70 cluster: cluster, 71 timeout: config.JackalDefaultTimeout, 72 retries: config.JackalDefaultRetries, 73 } 74 } 75 76 // NewFromJackalManifest generates a helm chart and config from a given Jackal manifest. 77 func NewFromJackalManifest(manifest types.JackalManifest, manifestPath, packageName, componentName string, mods ...Modifier) (h *Helm, err error) { 78 spinner := message.NewProgressSpinner("Starting helm chart generation %s", manifest.Name) 79 defer spinner.Stop() 80 81 // Generate a new chart. 82 tmpChart := new(chart.Chart) 83 tmpChart.Metadata = new(chart.Metadata) 84 85 // Generate a hashed chart name. 86 rawChartName := fmt.Sprintf("raw-%s-%s-%s", packageName, componentName, manifest.Name) 87 hasher := sha1.New() 88 hasher.Write([]byte(rawChartName)) 89 tmpChart.Metadata.Name = rawChartName 90 sha1ReleaseName := hex.EncodeToString(hasher.Sum(nil)) 91 92 // This is fun, increment forward in a semver-way using epoch so helm doesn't cry. 93 tmpChart.Metadata.Version = fmt.Sprintf("0.1.%d", config.GetStartTime()) 94 tmpChart.Metadata.APIVersion = chart.APIVersionV1 95 96 // Add the manifest files so helm does its thing. 97 for _, file := range manifest.Files { 98 spinner.Updatef("Processing %s", file) 99 manifest := path.Join(manifestPath, file) 100 data, err := os.ReadFile(manifest) 101 if err != nil { 102 return h, fmt.Errorf("unable to read manifest file %s: %w", manifest, err) 103 } 104 105 // Escape all chars and then wrap in {{ }}. 106 txt := strconv.Quote(string(data)) 107 data = []byte("{{" + txt + "}}") 108 109 tmpChart.Templates = append(tmpChart.Templates, &chart.File{Name: manifest, Data: data}) 110 } 111 112 // Generate the struct to pass to InstallOrUpgradeChart(). 113 h = &Helm{ 114 chart: types.JackalChart{ 115 Name: tmpChart.Metadata.Name, 116 // Preserve the jackal prefix for chart names to match v0.22.x and earlier behavior. 117 ReleaseName: fmt.Sprintf("jackal-%s", sha1ReleaseName), 118 Version: tmpChart.Metadata.Version, 119 Namespace: manifest.Namespace, 120 NoWait: manifest.NoWait, 121 }, 122 chartOverride: tmpChart, 123 timeout: config.JackalDefaultTimeout, 124 } 125 126 for _, mod := range mods { 127 mod(h) 128 } 129 130 spinner.Success() 131 132 return h, nil 133 } 134 135 // WithDeployInfo adds the necessary information to deploy a given chart 136 func WithDeployInfo(component types.JackalComponent, cfg *types.PackagerConfig, cluster *cluster.Cluster, valuesOverrides map[string]any, timeout time.Duration, retries int) Modifier { 137 return func(h *Helm) { 138 h.component = component 139 h.cfg = cfg 140 h.cluster = cluster 141 h.valuesOverrides = valuesOverrides 142 h.timeout = timeout 143 h.retries = retries 144 } 145 } 146 147 // WithKubeVersion sets the Kube version for templating the chart 148 func WithKubeVersion(kubeVersion string) Modifier { 149 return func(h *Helm) { 150 h.kubeVersion = kubeVersion 151 } 152 } 153 154 // WithPackageConfig sets the packager config for the chart 155 func WithPackageConfig(cfg *types.PackagerConfig) Modifier { 156 return func(h *Helm) { 157 h.cfg = cfg 158 } 159 } 160 161 // StandardName generates a predictable full path for a helm chart for Jackal. 162 func StandardName(destination string, chart types.JackalChart) string { 163 return filepath.Join(destination, chart.Name+"-"+chart.Version) 164 } 165 166 // StandardValuesName generates a predictable full path for the values file for a helm chart for jackal 167 func StandardValuesName(destination string, chart types.JackalChart, idx int) string { 168 return fmt.Sprintf("%s-%d", StandardName(destination, chart), idx) 169 }