Skip to content

Commit c2e5f9d

Browse files
fix:issue-5041 (operator-framework#5045)
Signed-off-by: cndoit18 <[email protected]> Co-authored-by: cndoit18 <[email protected]>
1 parent a1996a0 commit c2e5f9d

File tree

3 files changed

+57
-12
lines changed

3 files changed

+57
-12
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
entries:
2+
- description: >
3+
For Helm-based operators, fixed release equality comparison such that number values are compared and not their types to avoid unnecessary reconciliations.
4+
kind: bugfix

internal/helm/release/manager.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"fmt"
23+
"reflect"
2324
"strings"
2425

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

129-
m.isUpgradeRequired = m.isUpgrade(deployedRelease)
130-
130+
m.isUpgradeRequired, err = m.isUpgrade(deployedRelease)
131+
if err != nil {
132+
return fmt.Errorf("failed to get upgrade status: %w", err)
133+
}
131134
return nil
132135
}
133136

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

138-
func (m manager) isUpgrade(deployedRelease *rpb.Release) bool {
141+
// This is caused by the different logic of loading from local and loading from secret
142+
// For example, the Raw field, which has the tag `json:"-"`, causes the Unmarshal to be lost when it into Release
143+
// We need to make them follow the JSON tag
144+
// see: https://github.com/helm/helm/blob/cf0c6fed519d48101cd69ce01a355125215ee46f/pkg/storage/driver/util.go#L81
145+
func equalJSONStruct(a, b interface{}) (bool, error) {
146+
if reflect.ValueOf(a).IsNil() || reflect.ValueOf(b).IsNil() {
147+
return apiequality.Semantic.DeepEqual(a, b), nil
148+
}
149+
150+
aBuf, bBuf := &bytes.Buffer{}, &bytes.Buffer{}
151+
err := json.NewEncoder(aBuf).Encode(a)
152+
if err != nil {
153+
return false, err
154+
}
155+
err = json.NewEncoder(bBuf).Encode(b)
156+
return aBuf.String() == bBuf.String(), err
157+
}
158+
159+
func (m manager) isUpgrade(deployedRelease *rpb.Release) (bool, error) {
139160
if deployedRelease == nil {
140-
return false
161+
return false, nil
141162
}
142163

143164
// Judging whether to skip updates
144165
skip := m.namespace == deployedRelease.Namespace
145166
skip = skip && m.releaseName == deployedRelease.Name
146-
skip = skip && apiequality.Semantic.DeepEqual(m.chart, deployedRelease.Chart)
147-
skip = skip && apiequality.Semantic.DeepEqual(m.values, deployedRelease.Config)
148167

149-
return !skip
168+
ok, err := equalJSONStruct(m.chart, deployedRelease.Chart)
169+
if err != nil {
170+
return false, err
171+
}
172+
skip = skip && ok
173+
174+
ok, err = equalJSONStruct(m.values, deployedRelease.Config)
175+
return !(skip && ok), err
150176
}
151177

152178
func (m manager) getDeployedRelease() (*rpb.Release, error) {

internal/helm/release/manager_test.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package release
1616

1717
import (
18+
"bytes"
19+
"encoding/json"
1820
"testing"
1921

2022
"github.com/stretchr/testify/assert"
@@ -247,11 +249,20 @@ func TestManagerisUpgrade(t *testing.T) {
247249
name: "different values",
248250
releaseName: "deployed",
249251
releaseNs: "deployed-ns",
250-
values: map[string]interface{}{"key": "1"},
252+
values: map[string]interface{}{"key": "1", "int": int32(1)},
251253
chart: newTestChart(t, "./testdata/simple"),
252-
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": ""}, "deployed", "deployed-ns"),
254+
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": "", "int": int64(1)}, "deployed", "deployed-ns"),
253255
want: true,
254256
},
257+
{
258+
name: "nil values",
259+
releaseName: "deployed",
260+
releaseNs: "deployed-ns",
261+
values: nil,
262+
chart: newTestChart(t, "./testdata/simple"),
263+
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{}, "deployed", "deployed-ns"),
264+
want: false,
265+
},
255266
}
256267
for _, test := range tests {
257268
t.Run(test.name, func(t *testing.T) {
@@ -261,8 +272,9 @@ func TestManagerisUpgrade(t *testing.T) {
261272
values: test.values,
262273
chart: test.chart,
263274
}
264-
isUpgrade := m.isUpgrade(test.deployedRelease)
275+
isUpgrade, err := m.isUpgrade(test.deployedRelease)
265276
assert.Equal(t, test.want, isUpgrade)
277+
assert.Equal(t, nil, err)
266278
})
267279
}
268280
}
@@ -273,13 +285,16 @@ func newTestChart(t *testing.T, path string) *cpb.Chart {
273285
return chart
274286
}
275287

276-
func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release {
288+
func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release { // nolint: unparam
277289
release := rpb.Mock(&rpb.MockReleaseOptions{
278290
Name: name,
279291
Namespace: namespace,
280-
Chart: chart,
281292
Version: 1,
282293
})
294+
295+
buffer := &bytes.Buffer{}
296+
_ = json.NewEncoder(buffer).Encode(chart)
297+
_ = json.NewDecoder(buffer).Decode(release.Chart)
283298
release.Config = values
284299
return release
285300
}

0 commit comments

Comments
 (0)