Skip to content

Commit 5d40579

Browse files
committed
fix(helm): fix when using the randAlphaNum in Helm, the chart release is constantly upgraded
Signed-off-by: cndoit18 <[email protected]>
1 parent 2ddde63 commit 5d40579

File tree

15 files changed

+190
-22
lines changed

15 files changed

+190
-22
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 a bug where deployed and candidate release comparison was always false when an RNG was used to derive some manifest value, resulting in the chart release constantly upgrading
4+
kind: bugfix

internal/helm/release/manager.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"helm.sh/helm/v3/pkg/storage/driver"
3333
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3434
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
35+
apiequality "k8s.io/apimachinery/pkg/api/equality"
3536
apierrors "k8s.io/apimachinery/pkg/api/errors"
3637
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3738
"k8s.io/apimachinery/pkg/runtime"
@@ -125,14 +126,7 @@ func (m *manager) Sync(ctx context.Context) error {
125126
m.deployedRelease = deployedRelease
126127
m.isInstalled = true
127128

128-
// Get the next candidate release to determine if an upgrade is necessary.
129-
candidateRelease, err := m.getCandidateRelease(m.namespace, m.releaseName, m.chart, m.values)
130-
if err != nil {
131-
return fmt.Errorf("failed to get candidate release: %w", err)
132-
}
133-
if deployedRelease.Manifest != candidateRelease.Manifest {
134-
m.isUpgradeRequired = true
135-
}
129+
m.isUpgradeRequired = m.isUpgrade(deployedRelease)
136130

137131
return nil
138132
}
@@ -141,6 +135,20 @@ func notFoundErr(err error) bool {
141135
return err != nil && strings.Contains(err.Error(), "not found")
142136
}
143137

138+
func (m manager) isUpgrade(deployedRelease *rpb.Release) bool {
139+
if deployedRelease == nil {
140+
return false
141+
}
142+
143+
// Judging whether to skip updates
144+
skip := m.namespace == deployedRelease.Namespace
145+
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)
148+
149+
return !skip
150+
}
151+
144152
func (m manager) getDeployedRelease() (*rpb.Release, error) {
145153
deployedRelease, err := m.storageBackend.Deployed(m.releaseName)
146154
if err != nil {
@@ -152,14 +160,6 @@ func (m manager) getDeployedRelease() (*rpb.Release, error) {
152160
return deployedRelease, nil
153161
}
154162

155-
func (m manager) getCandidateRelease(namespace, name string, chart *cpb.Chart,
156-
values map[string]interface{}) (*rpb.Release, error) {
157-
upgrade := action.NewUpgrade(m.actionConfig)
158-
upgrade.Namespace = namespace
159-
upgrade.DryRun = true
160-
return upgrade.Run(name, chart, values)
161-
}
162-
163163
// InstallRelease performs a Helm release install.
164164
func (m manager) InstallRelease(ctx context.Context, opts ...InstallOption) (*rpb.Release, error) {
165165
install := action.NewInstall(m.actionConfig)

internal/helm/release/manager_test.go

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ package release
1717
import (
1818
"testing"
1919

20-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
21-
apitypes "k8s.io/apimachinery/pkg/types"
22-
"k8s.io/cli-runtime/pkg/resource"
23-
20+
"github.com/stretchr/testify/assert"
21+
cpb "helm.sh/helm/v3/pkg/chart"
22+
lpb "helm.sh/helm/v3/pkg/chart/loader"
23+
rpb "helm.sh/helm/v3/pkg/release"
2424
appsv1 "k8s.io/api/apps/v1"
2525
v1 "k8s.io/api/core/v1"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27-
28-
"github.com/stretchr/testify/assert"
27+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2928
"k8s.io/apimachinery/pkg/runtime"
29+
apitypes "k8s.io/apimachinery/pkg/types"
30+
"k8s.io/cli-runtime/pkg/resource"
3031
)
3132

3233
func newTestUnstructured(containers []interface{}) *unstructured.Unstructured {
@@ -213,3 +214,73 @@ func TestManagerGenerateStrategicMergePatch(t *testing.T) {
213214
assert.Equal(t, test.patch, string(diff))
214215
}
215216
}
217+
218+
func TestManagerisUpgrade(t *testing.T) {
219+
tests := []struct {
220+
name string
221+
releaseName string
222+
releaseNs string
223+
values map[string]interface{}
224+
chart *cpb.Chart
225+
deployedRelease *rpb.Release
226+
want bool
227+
}{
228+
{
229+
name: "ok",
230+
releaseName: "deployed",
231+
releaseNs: "deployed-ns",
232+
values: map[string]interface{}{"key": "value"},
233+
chart: newTestChart(t, "./testdata/simple"),
234+
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": "value"}, "deployed", "deployed-ns"),
235+
want: false,
236+
},
237+
{
238+
name: "different chart",
239+
releaseName: "deployed",
240+
releaseNs: "deployed-ns",
241+
values: map[string]interface{}{"key": "value"},
242+
chart: newTestChart(t, "./testdata/simple"),
243+
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simpledf"), map[string]interface{}{"key": "value"}, "deployed", "deployed-ns"),
244+
want: true,
245+
},
246+
{
247+
name: "different values",
248+
releaseName: "deployed",
249+
releaseNs: "deployed-ns",
250+
values: map[string]interface{}{"key": "1"},
251+
chart: newTestChart(t, "./testdata/simple"),
252+
deployedRelease: newTestRelease(newTestChart(t, "./testdata/simple"), map[string]interface{}{"key": ""}, "deployed", "deployed-ns"),
253+
want: true,
254+
},
255+
}
256+
for _, tt := range tests {
257+
t.Run(tt.name, func(t *testing.T) {
258+
m := manager{
259+
releaseName: tt.releaseName,
260+
namespace: tt.releaseNs,
261+
values: tt.values,
262+
chart: tt.chart,
263+
}
264+
if got := m.isUpgrade(tt.deployedRelease); got != tt.want {
265+
t.Errorf("manager.isUpgrade() = %v, want %v", got, tt.want)
266+
}
267+
})
268+
}
269+
}
270+
271+
func newTestChart(t *testing.T, path string) *cpb.Chart {
272+
chart, err := lpb.Load(path)
273+
assert.Nil(t, err)
274+
return chart
275+
}
276+
277+
func newTestRelease(chart *cpb.Chart, values map[string]interface{}, name, namespace string) *rpb.Release {
278+
release := rpb.Mock(&rpb.MockReleaseOptions{
279+
Name: name,
280+
Namespace: namespace,
281+
Chart: chart,
282+
Version: 1,
283+
})
284+
release.Config = values
285+
return release
286+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*~
18+
# Various IDEs
19+
.project
20+
.idea/
21+
*.tmproj
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
description: a simple charts
3+
name: simple
4+
keywords:
5+
- helm
6+
- operator-sdk
7+
version: 0.0.1+master
8+
appVersion: "master"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A simple charts
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{/* vim: set filetype=mustache: */}}
2+
{{/*
3+
Expand the name of the chart.
4+
*/}}
5+
{{- define "simple.fullname" -}}
6+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7+
{{- end -}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: {{ template "simple.fullname" . }}
5+
namespace: {{ .Release.Namespace }}
6+
type: Opaque
7+
data:
8+
password: {{ randAlphaNum 10 | b64enc | quote }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# simple values
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*~
18+
# Various IDEs
19+
.project
20+
.idea/
21+
*.tmproj

0 commit comments

Comments
 (0)