Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add GetReleaseChecksum method and update UpgradeRelease signature #583

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

juev
Copy link
Contributor

@juev juev commented Apr 1, 2025

Overview

Transferring the release checksum storage from variables to labels

What this PR does / why we need it

Transfer the storage of checksums from environment variables to release tags

@juev juev added the enhancement New feature or request label Apr 1, 2025
@juev juev requested review from yalosev, ldmonster and Copilot April 1, 2025 12:00
@juev juev self-assigned this Apr 1, 2025
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates the checksum storage mechanism for Helm releases by transferring the checksum from environment variables to release labels, and it adjusts the UpgradeRelease method signatures across multiple packages.

  • Moves checksum storage from variables to release labels in HelmModule
  • Introduces a new GetReleaseChecksum method for both helm3lib and helm3 packages
  • Updates UpgradeRelease method signatures and related calls in tests and client interfaces

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
pkg/module_manager/models/modules/helm.go Updates RunHelmInstall and shouldRunHelmUpgrade to utilize release labels for the module checksum
pkg/helm/helm_test.go Updates test to call the new UpgradeRelease signature with labels
pkg/helm/helm3lib/helm3lib.go Updates UpgradeRelease and introduces GetReleaseChecksum with labels
pkg/helm/helm3/helm3.go Adjusts the UpgradeRelease signature and provides a GetReleaseChecksum implementation
pkg/helm/client/client.go Updates interface definitions to include new labels parameter and GetReleaseChecksum
Comments suppressed due to low confidence (2)

pkg/helm/helm3lib/helm3lib.go:205

  • Ensure that upg.Labels is initialized (e.g., to an empty map) before calling maps.Copy to avoid potential nil pointer issues when labels are provided.
maps.Copy(upg.Labels, labels)

pkg/helm/helm3/helm3.go:142

  • The labels parameter is currently ignored in the Helm3 client UpgradeRelease implementation. Consider handling the moduleChecksum label similarly to the helm3lib version, or remove the unused parameter if not needed.
func (h *Helm3Client) UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, _ map[string]string, namespace string) error {

@juev juev requested a review from Copilot April 1, 2025 13:03
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR transfers storage of release checksums from environment variables to release labels and updates the UpgradeRelease signature across multiple modules. Key changes include:

  • Adding a new GetReleaseChecksum method to retrieve the checksum from release labels.
  • Updating the UpgradeRelease signature to accept a labels parameter instead of a set values slice.
  • Adjusting tests and mocks to use the new method signatures.

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/module_manager/models/modules/helm.go Replaced checksum storage with label-based storage in helm install and upgrade logic.
pkg/helm/test/mock/mock.go Introduced GetReleaseChecksum and updated UpgradeRelease signature.
pkg/helm/helm_test.go Updated test calls to match the new UpgradeRelease signature.
pkg/helm/helm3lib/helm3lib.go Updated UpgradeRelease and upgradeRelease methods to handle labels; added GetReleaseChecksum.
pkg/helm/helm3/helm3.go Updated UpgradeRelease and added GetReleaseChecksum implementation.
pkg/helm/client/client.go Updated HelmClient interface with new method signature for UpgradeRelease and added GetReleaseChecksum.

@juev juev marked this pull request as ready for review April 1, 2025 13:11
@@ -38,7 +38,7 @@ func TestHelm3LibUpgradeDelete(t *testing.T) {

cl := initHelmClient(t)

err := cl.UpgradeRelease("test-release", "testdata/chart", nil, nil, cl.Namespace)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to start write tests for helm methods behaviour.

f.e. add this method for LibClient (not interface) and test with it

// ListReleases returns list of releases
func (h *LibClient) ListReleases() ([]*release.Release, error) {
	l := action.NewList(actionConfig)
	// list all releases regardless of their state
	l.StateMask = action.ListAll
	list, err := l.Run()
	if err != nil {
		return nil, fmt.Errorf("helm list failed: %s", err)
	}

	return list, nil
}

upg.Labels = make(map[string]string)
}
if labels != nil {
maps.Copy(upg.Labels, labels)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't get the clue. why not to pass labels directly to the upgrade cli. Do the upgradeCli modify them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also have to add to the installCli below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to protect myself a little. But yes, you can just ignore the current labels and just make an assignment.

instClient also corrected it.

@juev juev force-pushed the feature/change-checksum-validation branch from 2f29530 to b71a478 Compare April 2, 2025 14:18
juev added 7 commits April 2, 2025 17:23
Signed-off-by: Evsyukov Denis <[email protected]>

diff --git c/pkg/helm/client/client.go i/pkg/helm/client/client.go
index beb810e..b5edbe8 100644
--- c/pkg/helm/client/client.go
+++ i/pkg/helm/client/client.go
@@ -1,12 +1,15 @@
 package client

-import "github.com/flant/addon-operator/pkg/utils"
+import (
+	"github.com/flant/addon-operator/pkg/utils"
+)

 type HelmClient interface {
 	LastReleaseStatus(releaseName string) (string, string, error)
-	UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string) error
+	UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, labels map[string]string, namespace string) error
 	Render(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string, debug bool) (string, error)
 	GetReleaseValues(releaseName string) (utils.Values, error)
+	GetReleaseChecksum(releaseName string) (string, error)
 	DeleteRelease(releaseName string) error
 	ListReleasesNames() ([]string, error)
 	IsReleaseExists(releaseName string) (bool, error)
diff --git c/pkg/helm/helm3/helm3.go i/pkg/helm/helm3/helm3.go
index e396daa..6737cda 100644
--- c/pkg/helm/helm3/helm3.go
+++ i/pkg/helm/helm3/helm3.go
@@ -9,9 +9,10 @@ import (
 	"strings"
 	"time"

-	"github.com/deckhouse/deckhouse/pkg/log"
 	k8syaml "sigs.k8s.io/yaml"

+	"github.com/deckhouse/deckhouse/pkg/log"
+
 	"github.com/flant/addon-operator/pkg/helm/client"
 	"github.com/flant/addon-operator/pkg/utils"
 	"github.com/flant/shell-operator/pkg/executor"
@@ -145,7 +146,7 @@ func (h *Helm3Client) LastReleaseStatus(releaseName string) (string /*revision*/
 	return revision, status, nil
 }

-func (h *Helm3Client) UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, namespace string) error {
+func (h *Helm3Client) UpgradeRelease(releaseName string, chart string, valuesPaths []string, setValues []string, _ map[string]string, namespace string) error {
 	args := []string{
 		"upgrade", releaseName, chart,
 		"--install",
@@ -210,6 +211,20 @@ func (h *Helm3Client) GetReleaseValues(releaseName string) (utils.Values, error)
 	return values, nil
 }

+func (h *Helm3Client) GetReleaseChecksum(releaseName string) (string, error) {
+	args := []string{
+		"get", "manifest", releaseName,
+		"--namespace", h.Namespace,
+	}
+	stdout, stderr, err := h.cmd(args...)
+	if err != nil {
+		return "", fmt.Errorf("cannot get manifest of helm release %s: %s\n%s %s", releaseName, err, stdout, stderr)
+	}
+
+	checksum := utils.CalculateStringsChecksum(stdout)
+	return checksum, nil
+}
+
 func (h *Helm3Client) DeleteRelease(releaseName string) error {
 	h.Logger.Debug("helm release: execute helm uninstall", slog.String("release", releaseName))

diff --git c/pkg/helm/helm3lib/helm3lib.go i/pkg/helm/helm3lib/helm3lib.go
index defee5f..36dd480 100644
--- c/pkg/helm/helm3lib/helm3lib.go
+++ i/pkg/helm/helm3lib/helm3lib.go
@@ -5,14 +5,13 @@ import (
 	"errors"
 	"fmt"
 	"log/slog"
+	"maps"
 	"os"
 	"sort"
 	"strconv"
 	"strings"
 	"time"

-	"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/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -26,6 +25,9 @@ import (
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/client-go/rest"

+	"github.com/deckhouse/deckhouse/pkg/log"
+	logContext "github.com/deckhouse/deckhouse/pkg/log/context"
+
 	"github.com/flant/addon-operator/pkg/helm/client"
 	"github.com/flant/addon-operator/pkg/helm/post_renderer"
 	"github.com/flant/addon-operator/pkg/utils"
@@ -160,15 +162,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, namespace string) error {
-	err := h.upgradeRelease(releaseName, chartName, valuesPaths, setValues, namespace)
+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)
 	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, namespace)
+		return h.upgradeRelease(releaseName, chartName, valuesPaths, setValues, labels, namespace)
 	}
 	h.Logger.Debug("helm release upgraded", slog.String("version", releaseName))
 	return nil
@@ -178,7 +180,7 @@ func (h *LibClient) hasLabelsToApply() bool {
 	return len(h.labels) > 0
 }

-func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesPaths []string, setValues []string, namespace string) error {
+func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesPaths []string, setValues []string, labels map[string]string, namespace string) error {
 	upg := action.NewUpgrade(actionConfig)
 	if namespace != "" {
 		upg.Namespace = namespace
@@ -191,6 +193,8 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
 	upg.SkipCRDs = true
 	upg.MaxHistory = int(options.HistoryMax)
 	upg.Timeout = options.Timeout
+	// copy labels to avoid modifying the original map
+	maps.Copy(upg.Labels, labels)

 	chart, err := loader.Load(chartName)
 	if err != nil {
@@ -356,6 +360,19 @@ func (h *LibClient) GetReleaseValues(releaseName string) (utils.Values, error) {
 	return gv.Run(releaseName)
 }

+func (h *LibClient) GetReleaseChecksum(releaseName string) (string, error) {
+	gv := action.NewGet(actionConfig)
+	rel, err := gv.Run(releaseName)
+	if err != nil {
+		return "", fmt.Errorf("helm get failed: %s", err)
+	}
+	if checksum, ok := rel.Labels["moduleChecksum"]; ok {
+		return checksum, nil
+	}
+
+	return "", fmt.Errorf("moduleChecksum label not found in release %s", releaseName)
+}
+
 func (h *LibClient) DeleteRelease(releaseName string) error {
 	h.Logger.Debug("helm release: execute helm uninstall", slog.String("release", releaseName))

diff --git c/pkg/helm/helm_test.go i/pkg/helm/helm_test.go
index 87458c9..d17f633 100644
--- c/pkg/helm/helm_test.go
+++ i/pkg/helm/helm_test.go
@@ -4,9 +4,10 @@ import (
 	"os"
 	"testing"

-	"github.com/deckhouse/deckhouse/pkg/log"
 	. "github.com/onsi/gomega"

+	"github.com/deckhouse/deckhouse/pkg/log"
+
 	"github.com/flant/addon-operator/pkg/helm/helm3"
 	"github.com/flant/addon-operator/pkg/helm/helm3lib"
 )
@@ -51,7 +52,7 @@ func TestHelmFactory(t *testing.T) {
 		g.Expect(err).ShouldNot(HaveOccurred())
 		g.Expect(isExists).Should(BeFalse(), "should not found release in the empty cluster")

-		err = helmCl.UpgradeRelease("test-release", "helm3lib/testdata/chart", nil, nil, namespace)
+		err = helmCl.UpgradeRelease("test-release", "helm3lib/testdata/chart", nil, nil, nil, namespace)
 		g.Expect(err).ShouldNot(HaveOccurred())

 		releasesAfterUpgrade, err := helmCl.ListReleasesNames()
diff --git c/pkg/module_manager/models/modules/helm.go i/pkg/module_manager/models/modules/helm.go
index 6831c59..e7403fb 100644
--- c/pkg/module_manager/models/modules/helm.go
+++ i/pkg/module_manager/models/modules/helm.go
@@ -11,10 +11,11 @@ import (
 	"strings"
 	"time"

-	"github.com/deckhouse/deckhouse/pkg/log"
 	"github.com/gofrs/uuid/v5"
 	"github.com/kennygrant/sanitize"

+	"github.com/deckhouse/deckhouse/pkg/log"
+
 	"github.com/flant/addon-operator/pkg/helm"
 	"github.com/flant/addon-operator/pkg/helm/client"
 	"github.com/flant/addon-operator/pkg/utils"
@@ -272,7 +273,10 @@ func (hm *HelmModule) RunHelmInstall(logLabels map[string]string) error {
 			helmReleaseName,
 			hm.path,
 			[]string{valuesPath},
-			[]string{fmt.Sprintf("_addonOperatorModuleChecksum=%s", checksum)},
+			[]string{},
+			map[string]string{
+				"moduleChecksum": checksum,
+			},
 			hm.defaultNamespace,
 		)
 	}()
@@ -311,7 +315,7 @@ func (hm *HelmModule) shouldRunHelmUpgrade(helmClient client.HelmClient, release
 	}

 	// Get values for a non-failed release.
-	releaseValues, err := helmClient.GetReleaseValues(releaseName)
+	recordedChecksum, err := helmClient.GetReleaseChecksum(releaseName)
 	if err != nil {
 		logEntry.Debug("helm release get values error, no upgrade",
 			slog.String("release", releaseName),
@@ -319,24 +323,14 @@ func (hm *HelmModule) shouldRunHelmUpgrade(helmClient client.HelmClient, release
 		return false, err
 	}

-	// Run helm upgrade if there is no stored checksum
-	recordedChecksum, hasKey := releaseValues["_addonOperatorModuleChecksum"]
-	if !hasKey {
-		logEntry.Debug("helm release has no saved checksum of values: should run upgrade",
-			slog.String("release", releaseName))
-		return true, nil
-	}
-
 	// Calculate a checksum of current values and compare to a stored checksum.
 	// Run helm upgrade if checksum is changed.
-	if recordedChecksumStr, ok := recordedChecksum.(string); ok {
-		if recordedChecksumStr != checksum {
-			logEntry.Debug("helm release checksum is changed: should run upgrade",
-				slog.String("release", releaseName),
-				slog.String("checksum", recordedChecksumStr),
-				slog.String("newChecksum", checksum))
-			return true, nil
-		}
+	if recordedChecksum != checksum {
+		logEntry.Debug("helm release checksum is changed: should run upgrade",
+			slog.String("release", releaseName),
+			slog.String("checksum", recordedChecksum),
+			slog.String("newChecksum", checksum))
+		return true, nil
 	}

 	// Check if there are absent resources

diff --git c/pkg/helm/helm3/helm3.go i/pkg/helm/helm3/helm3.go
index 03cab1f..6737cda 100644
--- c/pkg/helm/helm3/helm3.go
+++ i/pkg/helm/helm3/helm3.go
@@ -9,9 +9,10 @@ import (
 	"strings"
 	"time"

-	"github.com/deckhouse/deckhouse/pkg/log"
 	k8syaml "sigs.k8s.io/yaml"

+	"github.com/deckhouse/deckhouse/pkg/log"
+
 	"github.com/flant/addon-operator/pkg/helm/client"
 	"github.com/flant/addon-operator/pkg/utils"
 	"github.com/flant/shell-operator/pkg/executor"
diff --git c/pkg/helm/helm3lib/helm3lib.go i/pkg/helm/helm3lib/helm3lib.go
index deb6451..f0a534d 100644
--- c/pkg/helm/helm3lib/helm3lib.go
+++ i/pkg/helm/helm3lib/helm3lib.go
@@ -12,8 +12,6 @@ import (
 	"strings"
 	"time"

-	"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/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -27,6 +25,9 @@ import (
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/client-go/rest"

+	"github.com/deckhouse/deckhouse/pkg/log"
+	logContext "github.com/deckhouse/deckhouse/pkg/log/context"
+
 	"github.com/flant/addon-operator/pkg/helm/client"
 	"github.com/flant/addon-operator/pkg/helm/post_renderer"
 	"github.com/flant/addon-operator/pkg/utils"
@@ -193,12 +194,17 @@ func (h *LibClient) upgradeRelease(releaseName string, chartName string, valuesP
 	upg.MaxHistory = int(options.HistoryMax)
 	upg.Timeout = options.Timeout
 	// copy labels to avoid modifying the original map
+<<<<<<< HEAD
 	if upg.Labels == nil {
 		upg.Labels = make(map[string]string)
 	}
 	if labels != nil {
 		maps.Copy(upg.Labels, labels)
 	}
+||||||| parent of ad2b140 (feat: add GetReleaseChecksum method and update UpgradeRelease signature)
+=======
+	maps.Copy(upg.Labels, labels)
+>>>>>>> ad2b140 (feat: add GetReleaseChecksum method and update UpgradeRelease signature)

 	chart, err := loader.Load(chartName)
 	if err != nil {
@@ -374,6 +380,7 @@ func (h *LibClient) GetReleaseChecksum(releaseName string) (string, error) {
 		return checksum, nil
 	}

+<<<<<<< HEAD
 	// fallback to old behavior
 	releaseValues, err := h.GetReleaseValues(releaseName)
 	if err != nil {
@@ -385,6 +392,9 @@ func (h *LibClient) GetReleaseChecksum(releaseName string) (string, error) {
 		}
 	}

+||||||| parent of ad2b140 (feat: add GetReleaseChecksum method and update UpgradeRelease signature)
+=======
+>>>>>>> ad2b140 (feat: add GetReleaseChecksum method and update UpgradeRelease signature)
 	return "", fmt.Errorf("moduleChecksum label not found in release %s", releaseName)
 }

diff --git c/pkg/helm/helm_test.go i/pkg/helm/helm_test.go
index 168ea1f..d17f633 100644
--- c/pkg/helm/helm_test.go
+++ i/pkg/helm/helm_test.go
@@ -4,9 +4,10 @@ import (
 	"os"
 	"testing"

-	"github.com/deckhouse/deckhouse/pkg/log"
 	. "github.com/onsi/gomega"

+	"github.com/deckhouse/deckhouse/pkg/log"
+
 	"github.com/flant/addon-operator/pkg/helm/helm3"
 	"github.com/flant/addon-operator/pkg/helm/helm3lib"
 )
diff --git c/pkg/module_manager/models/modules/helm.go i/pkg/module_manager/models/modules/helm.go
index 1b0f0b6..e7403fb 100644
--- c/pkg/module_manager/models/modules/helm.go
+++ i/pkg/module_manager/models/modules/helm.go
@@ -11,10 +11,11 @@ import (
 	"strings"
 	"time"

-	"github.com/deckhouse/deckhouse/pkg/log"
 	"github.com/gofrs/uuid/v5"
 	"github.com/kennygrant/sanitize"

+	"github.com/deckhouse/deckhouse/pkg/log"
+
 	"github.com/flant/addon-operator/pkg/helm"
 	"github.com/flant/addon-operator/pkg/helm/client"
 	"github.com/flant/addon-operator/pkg/utils"

Signed-off-by: Evsyukov Denis <[email protected]>
…re to include additional parameters

Signed-off-by: Evsyukov Denis <[email protected]>
@juev juev force-pushed the feature/change-checksum-validation branch from b71a478 to 891a9f6 Compare April 2, 2025 14:24
@juev juev requested review from yalosev and ldmonster April 2, 2025 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants