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  }