Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 93 additions & 23 deletions pkg/helm/helm3lib/helm3lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"log/slog"
"os"
"path/filepath"
"slices"
"sort"
"strconv"
"strings"
Expand All @@ -14,6 +16,7 @@ import (
"github.com/deckhouse/deckhouse/pkg/log"
logContext "github.com/deckhouse/deckhouse/pkg/log/context"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli"
Expand Down Expand Up @@ -164,15 +167,15 @@ func (h *LibClient) LastReleaseStatus(releaseName string) (string /*revision*/,
return strconv.FormatInt(int64(lastRelease.Version), 10), lastRelease.Info.Status.String(), nil
}

func (h *LibClient) UpgradeRelease(releaseName string, chartName string, valuesPaths []string, setValues []string, labels map[string]string, namespace string) error {
err := h.upgradeRelease(releaseName, chartName, valuesPaths, setValues, labels, namespace)
func (h *LibClient) UpgradeRelease(releaseName, modulePath string, valuesPaths []string, setValues []string, labels map[string]string, namespace string) error {
err := h.upgradeRelease(releaseName, modulePath, valuesPaths, setValues, labels, namespace)
if err != nil {
// helm validation can fail because FeatureGate was enabled for example
// handling this case we can reinitialize kubeClient and repeat one more time by backoff
if err := actionConfigInit(h.Logger); err != nil {
return err
}
return h.upgradeRelease(releaseName, chartName, valuesPaths, setValues, labels, namespace)
return h.upgradeRelease(releaseName, modulePath, valuesPaths, setValues, labels, namespace)
}
h.Logger.Debug("helm release upgraded", slog.String("version", releaseName))
return nil
Expand All @@ -182,7 +185,7 @@ func (h *LibClient) hasLabelsToApply() bool {
return len(h.labels) > 0
}

func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesPaths []string, setValues []string, labels map[string]string, namespace string) error {
func (h *LibClient) upgradeRelease(releaseName, modulePath string, valuesPaths []string, setValues []string, labels map[string]string, namespace string) error {
upg := action.NewUpgrade(actionConfig)
if namespace != "" {
upg.Namespace = namespace
Expand All @@ -197,11 +200,6 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
upg.Timeout = options.Timeout
upg.Labels = labels

chart, err := loader.Load(chartName)
if err != nil {
return err
}

var resultValues chartutil.Values

for _, vp := range valuesPaths {
Expand All @@ -224,9 +222,14 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
resultValues = chartutil.CoalesceTables(resultValues, m)
}

loaded, err := loadChart(releaseName, modulePath)
if err != nil {
return err
}

h.Logger.Info("Running helm upgrade for release",
slog.String("release", releaseName),
slog.String("chart", chartName),
slog.String("chart", modulePath),
slog.String("namespace", namespace))
histClient := action.NewHistory(actionConfig)
// Max is not working!!! Sort the final of releases by your own
Expand All @@ -247,7 +250,7 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
instClient.UseReleaseName = true
instClient.Labels = labels

_, err = instClient.Run(chart, resultValues)
_, err = instClient.Run(loaded, resultValues)
return err
}
h.Logger.Debug("old releases found", slog.Int("count", len(releases)))
Expand Down Expand Up @@ -306,13 +309,13 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
}
}

_, err = upg.Run(releaseName, chart, resultValues)
_, err = upg.Run(releaseName, loaded, resultValues)
if err != nil {
return fmt.Errorf("helm upgrade failed: %s\n", err)
}
h.Logger.Info("Helm upgrade successful",
slog.String("release", releaseName),
slog.String("chart", chartName),
slog.String("chart", modulePath),
slog.String("namespace", namespace))

return nil
Expand Down Expand Up @@ -451,12 +454,7 @@ func (h *LibClient) ListReleasesNames() ([]string, error) {
return releases, nil
}

func (h *LibClient) Render(releaseName, chartName string, valuesPaths, setValues []string, _ map[string]string, namespace string, debug bool) (string, error) {
chart, err := loader.Load(chartName)
if err != nil {
return "", err
}

func (h *LibClient) Render(releaseName, modulePath string, valuesPaths, setValues []string, _ map[string]string, namespace string, debug bool) (string, error) {
var resultValues chartutil.Values

for _, vp := range valuesPaths {
Expand All @@ -480,19 +478,24 @@ func (h *LibClient) Render(releaseName, chartName string, valuesPaths, setValues
}

h.Logger.Debug("Render helm templates for chart ...",
slog.String("chart", chartName),
slog.String("chart", modulePath),
slog.String("namespace", namespace))

loaded, err := loadChart(releaseName, modulePath)
if err != nil {
return "", err
}

inst := h.newDryRunInstAction(namespace, releaseName)

rs, err := inst.Run(chart, resultValues)
rs, err := inst.Run(loaded, resultValues)
if err != nil {
// helm render can fail because the CRD were previously created
// handling this case we can reinitialize RESTClient and repeat one more time by backoff
_ = actionConfigInit(h.Logger)
inst = h.newDryRunInstAction(namespace, releaseName)

rs, err = inst.Run(chart, resultValues)
rs, err = inst.Run(loaded, resultValues)
}

if err != nil {
Expand All @@ -506,7 +509,7 @@ func (h *LibClient) Render(releaseName, chartName string, valuesPaths, setValues
rs.Manifest += fmt.Sprintf("\n\n\n%v", err)
}

h.Logger.Info("Render helm templates for chart was successful", slog.String("chart", chartName))
h.Logger.Info("Render helm templates for chart was successful", slog.String("chart", modulePath))

return rs.Manifest, nil
}
Expand Down Expand Up @@ -543,3 +546,70 @@ func (h *LibClient) ListReleases() ([]*release.Release, error) {

return list, nil
}

func loadChart(moduleName, modulePath string) (*chart.Chart, error) {
if _, err := os.Stat(filepath.Join(modulePath, "Chart.yaml")); err == nil {
return loader.Load(modulePath)
}

var files []*loader.BufferedFile

chartYaml := fmt.Sprintf(`
name: %s
version: 0.2.0
`, moduleName)

files = append(files, &loader.BufferedFile{
Name: "Chart.yaml",
Data: []byte(chartYaml),
})

ignored := []string{
"crds",
"docs",
"hooks",
"images",
"lib",
}

err := filepath.Walk(modulePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if info.IsDir() {
if slices.Contains(ignored, info.Name()) {
return filepath.SkipDir
}

return nil
}

relPath, err := filepath.Rel(modulePath, path)
if err != nil {
return err
}

data, err := os.ReadFile(path)
if err != nil {
return err
}

files = append(files, &loader.BufferedFile{
Name: relPath,
Data: data,
})

return nil
})
if err != nil {
return nil, fmt.Errorf("read module files: %w", err)
}

loaded, err := loader.LoadFiles(files)
if err != nil {
return nil, fmt.Errorf("load chart from files: %w", err)
}

return loaded, nil
}
72 changes: 39 additions & 33 deletions pkg/helm/nelm/nelm.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ func (c *NelmClient) LastReleaseStatus(releaseName string) (string, string, erro
return strconv.FormatInt(int64(releaseGetResult.Release.Revision), 10), releaseGetResult.Release.Status.String(), nil
}

func (c *NelmClient) UpgradeRelease(releaseName, chartName string, valuesPaths []string, setValues []string, releaseLabels map[string]string, namespace string) error {
func (c *NelmClient) UpgradeRelease(releaseName, modulePath string, valuesPaths []string, setValues []string, releaseLabels map[string]string, namespace string) error {
logger := c.logger.With(
slog.String("release_name", releaseName),
slog.String("chart", chartName),
slog.String("chart", modulePath),
slog.String("namespace", namespace),
)

Expand Down Expand Up @@ -220,26 +220,29 @@ func (c *NelmClient) UpgradeRelease(releaseName, chartName string, valuesPaths [
}

if err := c.actions.ReleaseInstall(context.TODO(), releaseName, namespace, action.ReleaseInstallOptions{
Chart: chartName,
ExtraLabels: c.labels,
ExtraAnnotations: extraAnnotations,
KubeContext: c.opts.KubeContext,
NoInstallCRDs: true,
ReleaseHistoryLimit: int(c.opts.HistoryMax),
ReleaseLabels: releaseLabels,
ReleaseStorageDriver: c.opts.HelmDriver,
Timeout: c.opts.Timeout,
ValuesFilesPaths: valuesPaths,
ValuesSets: setValues,
ForceAdoption: true,
NoPodLogs: true,
Chart: modulePath,
DefaultChartName: releaseName,
DefaultChartVersion: "0.2.0",
DefaultChartAPIVersion: "v2",
ExtraLabels: c.labels,
ExtraAnnotations: extraAnnotations,
KubeContext: c.opts.KubeContext,
NoInstallCRDs: true,
ReleaseHistoryLimit: int(c.opts.HistoryMax),
ReleaseLabels: releaseLabels,
ReleaseStorageDriver: c.opts.HelmDriver,
Timeout: c.opts.Timeout,
ValuesFilesPaths: valuesPaths,
ValuesSets: setValues,
ForceAdoption: true,
NoPodLogs: true,
}); err != nil {
return fmt.Errorf("install nelm release %q: %w", releaseName, err)
}

logger.Info("Nelm upgrade successful",
slog.String("release", releaseName),
slog.String("chart", chartName),
slog.String("chart", modulePath),
slog.String("namespace", namespace))

return nil
Expand Down Expand Up @@ -360,9 +363,9 @@ func (c *NelmClient) ListReleasesNames() ([]string, error) {
return releaseNames, nil
}

func (c *NelmClient) Render(releaseName, chartName string, valuesPaths, setValues []string, releaseLabels map[string]string, namespace string, debug bool) (string, error) {
func (c *NelmClient) Render(releaseName, modulePath string, valuesPaths, setValues []string, releaseLabels map[string]string, namespace string, debug bool) (string, error) {
c.logger.Debug("Render nelm templates for chart ...",
slog.String("chart", chartName),
slog.String("chart", modulePath),
slog.String("namespace", namespace))

// Add client annotations
Expand All @@ -378,27 +381,30 @@ func (c *NelmClient) Render(releaseName, chartName string, valuesPaths, setValue
}

chartRenderResult, err := c.actions.ChartRender(context.TODO(), action.ChartRenderOptions{
OutputFilePath: "/dev/null", // No output file, we want to return the manifest as a string
Chart: chartName,
ExtraLabels: c.labels,
ExtraAnnotations: extraAnnotations,
KubeContext: c.opts.KubeContext,
ReleaseName: releaseName,
ReleaseNamespace: namespace,
ReleaseStorageDriver: c.opts.HelmDriver,
Remote: true,
ValuesFilesPaths: valuesPaths,
ValuesSets: setValues,
ForceAdoption: true,
OutputFilePath: "/dev/null", // No output file, we want to return the manifest as a string
Chart: modulePath,
DefaultChartName: releaseName,
DefaultChartVersion: "0.2.0",
DefaultChartAPIVersion: "v2",
ExtraLabels: c.labels,
ExtraAnnotations: extraAnnotations,
KubeContext: c.opts.KubeContext,
ReleaseName: releaseName,
ReleaseNamespace: namespace,
ReleaseStorageDriver: c.opts.HelmDriver,
Remote: true,
ValuesFilesPaths: valuesPaths,
ValuesSets: setValues,
ForceAdoption: true,
})
if err != nil {
if !debug {
return "", fmt.Errorf("render nelm chart %q: %w\n\nUse --debug flag to render out invalid YAML", chartName, err)
return "", fmt.Errorf("render nelm chart %q: %w\n\nUse --debug flag to render out invalid YAML", modulePath, err)
}
return "", fmt.Errorf("render nelm chart %q: %w", chartName, err)
return "", fmt.Errorf("render nelm chart %q: %w", modulePath, err)
}

c.logger.Info("Render nelm templates for chart was successful", slog.String("chart", chartName))
c.logger.Info("Render nelm templates for chart was successful", slog.String("chart", modulePath))

var result strings.Builder
for _, resource := range chartRenderResult.Resources {
Expand Down
14 changes: 2 additions & 12 deletions pkg/module_manager/models/modules/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/flant/addon-operator/pkg"
"github.com/flant/addon-operator/pkg/helm"
"github.com/flant/addon-operator/pkg/helm/client"
helm3lib "github.com/flant/addon-operator/pkg/helm/helm3lib"
"github.com/flant/addon-operator/pkg/helm/helm3lib"
"github.com/flant/addon-operator/pkg/utils"
"github.com/flant/kube-client/manifest"
"github.com/flant/shell-operator/pkg/utils/measure"
Expand Down Expand Up @@ -143,7 +143,7 @@ func (hm *HelmModule) isHelmChart() (bool, error) {
// check that templates/ dir exists
_, err = os.Stat(filepath.Join(hm.path, "templates"))
if err == nil {
return true, hm.createChartYaml(chartPath)
return true, nil
}
if os.IsNotExist(err) {
// if templates not exists - it's not a helm module
Expand All @@ -154,16 +154,6 @@ func (hm *HelmModule) isHelmChart() (bool, error) {
return false, err
}

func (hm *HelmModule) createChartYaml(chartPath string) error {
// we already have versions like 0.1.0 or 0.1.1
// to keep helm updatable, we have to increment this version
// new minor version of addon-operator seems reasonable to increase minor version of a helm chart
data := fmt.Sprintf(`name: %s
version: 0.2.0`, hm.name)

return os.WriteFile(chartPath, []byte(data), 0o644)
}

// checkHelmValues returns error if there is a wrong patch or values are not satisfied
// a Helm values contract defined by schemas in 'openapi' directory.
func (hm *HelmModule) checkHelmValues() error {
Expand Down
Loading