Skip to content

Commit

Permalink
fix: issue-5041 (operator-framework#5042)
Browse files Browse the repository at this point in the history
Signed-off-by: cndoit18 <cndoit18@outlook.com>
  • Loading branch information
cndoit18 authored Jul 6, 2021
1 parent 2781cf8 commit 57ac0fe
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 12 deletions.
4 changes: 4 additions & 0 deletions changelog/fragments/helm-deepequals.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
entries:
- description: >
For Helm-based operators, fixed release equality comparison such that number values are compared and not their types to avoid unnecessary reconciliations.
kind: bugfix
40 changes: 33 additions & 7 deletions internal/helm/release/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"

jsonpatch "gomodules.xyz/jsonpatch/v3"
Expand Down Expand Up @@ -126,27 +127,52 @@ func (m *manager) Sync(ctx context.Context) error {
m.deployedRelease = deployedRelease
m.isInstalled = true

m.isUpgradeRequired = m.isUpgrade(deployedRelease)

m.isUpgradeRequired, err = m.isUpgrade(deployedRelease)
if err != nil {
return fmt.Errorf("failed to get upgrade status: %w", err)
}
return nil
}

func notFoundErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "not found")
}

func (m manager) isUpgrade(deployedRelease *rpb.Release) bool {
// This is caused by the different logic of loading from local and loading from secret
// For example, the Raw field, which has the tag `json:"-"`, causes the Unmarshal to be lost when it into Release
// We need to make them follow the JSON tag
// see: https://github.com/helm/helm/blob/cf0c6fed519d48101cd69ce01a355125215ee46f/pkg/storage/driver/util.go#L81
func equalJSONStruct(a, b interface{}) (bool, error) {
if reflect.ValueOf(a).IsNil() || reflect.ValueOf(b).IsNil() {
return apiequality.Semantic.DeepEqual(a, b), nil
}

aBuf, bBuf := &bytes.Buffer{}, &bytes.Buffer{}
err := json.NewEncoder(aBuf).Encode(a)
if err != nil {
return false, err
}
err = json.NewEncoder(bBuf).Encode(b)
return aBuf.String() == bBuf.String(), err
}

func (m manager) isUpgrade(deployedRelease *rpb.Release) (bool, error) {
if deployedRelease == nil {
return false
return false, nil
}

// Judging whether to skip updates
skip := m.namespace == deployedRelease.Namespace
skip = skip && m.releaseName == deployedRelease.Name
skip = skip && apiequality.Semantic.DeepEqual(m.chart, deployedRelease.Chart)
skip = skip && apiequality.Semantic.DeepEqual(m.values, deployedRelease.Config)

return !skip
ok, err := equalJSONStruct(m.chart, deployedRelease.Chart)
if err != nil {
return false, err
}
skip = skip && ok

ok, err = equalJSONStruct(m.values, deployedRelease.Config)
return !(skip && ok), err
}

func (m manager) getDeployedRelease() (*rpb.Release, error) {
Expand Down
25 changes: 20 additions & 5 deletions internal/helm/release/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package release

import (
"bytes"
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -247,11 +249,20 @@ func TestManagerisUpgrade(t *testing.T) {
name: "different values",
releaseName: "deployed",
releaseNs: "deployed-ns",
values: map[string]interface{}{"key": "1"},
values: map[string]interface{}{"key": "1", "int": int32(1)},
chart: newTestChart(t, "./testdata/simple"),
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": ""}, "deployed", "deployed-ns"),
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": "", "int": int64(1)}, "deployed", "deployed-ns"),
want: true,
},
{
name: "nil values",
releaseName: "deployed",
releaseNs: "deployed-ns",
values: nil,
chart: newTestChart(t, "./testdata/simple"),
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{}, "deployed", "deployed-ns"),
want: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
Expand All @@ -261,8 +272,9 @@ func TestManagerisUpgrade(t *testing.T) {
values: test.values,
chart: test.chart,
}
isUpgrade := m.isUpgrade(test.deployedRelease)
isUpgrade, err := m.isUpgrade(test.deployedRelease)
assert.Equal(t, test.want, isUpgrade)
assert.Equal(t, nil, err)
})
}
}
Expand All @@ -273,13 +285,16 @@ func newTestChart(t *testing.T, path string) *cpb.Chart {
return chart
}

func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release {
func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release { // nolint: unparam
release := rpb.Mock(&rpb.MockReleaseOptions{
Name: name,
Namespace: namespace,
Chart: chart,
Version: 1,
})

buffer := &bytes.Buffer{}
_ = json.NewEncoder(buffer).Encode(chart)
_ = json.NewDecoder(buffer).Decode(release.Chart)
release.Config = values
return release
}

0 comments on commit 57ac0fe

Please sign in to comment.