Browse Source

Add tests for the "gathering data" phase of releasesync.

Nick Cabatoff 1 year ago
parent
commit
8210a1d6f3

+ 12
- 1
Gopkg.lock View File

@@ -155,6 +155,17 @@
155 155
   packages = ["."]
156 156
   revision = "e89373fe6b4a7413d7acd6da1725b83ef713e6e4"
157 157
 
158
+[[projects]]
159
+  name = "github.com/google/go-cmp"
160
+  packages = [
161
+    "cmp",
162
+    "cmp/internal/diff",
163
+    "cmp/internal/function",
164
+    "cmp/internal/value"
165
+  ]
166
+  revision = "3af367b6b30c263d47e8895973edcca9a49cf029"
167
+  version = "v0.2.0"
168
+
158 169
 [[projects]]
159 170
   branch = "master"
160 171
   name = "github.com/google/gofuzz"
@@ -804,6 +815,6 @@
804 815
 [solve-meta]
805 816
   analyzer-name = "dep"
806 817
   analyzer-version = 1
807
-  inputs-digest = "18e1f9cb0f7c43cf67a0ee5b21699771d4c1474b521b3f9a41d863b0291e3a62"
818
+  inputs-digest = "3cbcf68d471634e600092225d2a648c2e1e1ec89f293cfee1e5751c5ee39e3c2"
808 819
   solver-name = "gps-cdcl"
809 820
   solver-version = 1

+ 4
- 0
Gopkg.toml View File

@@ -32,3 +32,7 @@ required = ["k8s.io/code-generator/cmd/client-gen"]
32 32
 [[constraint]]
33 33
   name = "k8s.io/helm"
34 34
   version = "v2.8.1"
35
+
36
+[[constraint]]
37
+  name = "github.com/google/go-cmp"
38
+  version = "0.2.0"

+ 15
- 0
integrations/helm/release/release.go View File

@@ -34,6 +34,21 @@ type Release struct {
34 34
 	sync.RWMutex
35 35
 }
36 36
 
37
+func (r *Release) ConfigSync() *helmgit.Checkout {
38
+	return r.Repo.ConfigSync
39
+}
40
+
41
+type Releaser interface {
42
+	GetCurrent() (map[string][]DeployInfo, error)
43
+	GetDeployedRelease(name string) (*hapi_release.Release, error)
44
+	Install(checkout *helmgit.Checkout,
45
+		releaseName string,
46
+		fhr ifv1.FluxHelmRelease,
47
+		action Action,
48
+		opts InstallOptions) (hapi_release.Release, error)
49
+	ConfigSync() *helmgit.Checkout
50
+}
51
+
37 52
 type repo struct {
38 53
 	ConfigSync *helmgit.Checkout
39 54
 }

+ 69
- 33
integrations/helm/releasesync/releasesync.go View File

@@ -12,6 +12,8 @@ import (
12 12
 	"path/filepath"
13 13
 	"strings"
14 14
 
15
+	"github.com/pkg/errors"
16
+
15 17
 	protobuf "github.com/golang/protobuf/ptypes/timestamp"
16 18
 	hapi_release "k8s.io/helm/pkg/proto/hapi/release"
17 19
 
@@ -25,28 +27,29 @@ import (
25 27
 )
26 28
 
27 29
 const (
28
-	CustomResourceKind = "FluxHelmRelease"
29
-	syncDelay          = 90
30
+	syncDelay = 90
30 31
 )
31 32
 
32
-type ReleaseFhr struct {
33
+type releaseFhr struct {
33 34
 	RelName string
34
-	FhrName string
35 35
 	Fhr     ifv1.FluxHelmRelease
36 36
 }
37 37
 
38
+// ReleaseChangeSync implements DoReleaseChangeSync to return the cluster to the
39
+// state dictated by Custom Resources after manual Chart release(s).
38 40
 type ReleaseChangeSync struct {
39 41
 	logger  log.Logger
40
-	release *chartrelease.Release
42
+	release chartrelease.Releaser
41 43
 }
42 44
 
45
+// New creates a ReleaseChangeSync.
43 46
 func New(
44 47
 	logger log.Logger,
45
-	release *chartrelease.Release) *ReleaseChangeSync {
48
+	releaser chartrelease.Releaser) *ReleaseChangeSync {
46 49
 
47 50
 	return &ReleaseChangeSync{
48 51
 		logger:  logger,
49
-		release: release,
52
+		release: releaser,
50 53
 	}
51 54
 }
52 55
 
@@ -63,13 +66,14 @@ type chartRelease struct {
63 66
 }
64 67
 
65 68
 // DoReleaseChangeSync returns the cluster to the state dictated by Custom Resources
66
-// after manual Chart release(s)
69
+// after manual Chart release(s).
67 70
 func (rs *ReleaseChangeSync) DoReleaseChangeSync(ifClient ifclientset.Clientset, ns []string) (bool, error) {
68 71
 	ctx, cancel := context.WithTimeout(context.Background(), helmgit.DefaultCloneTimeout)
69 72
 	relsToSync, err := rs.releasesToSync(ctx, ifClient, ns)
70 73
 	cancel()
71 74
 	if err != nil {
72
-		err := fmt.Errorf("Failure to get info about manual chart release changes: %#v", err)
75
+		err = errors.Wrap(err, "getting info about manual chart release changes")
76
+		rs.logger.Log("error", err)
73 77
 		return false, err
74 78
 	}
75 79
 	if len(relsToSync) == 0 {
@@ -80,29 +84,32 @@ func (rs *ReleaseChangeSync) DoReleaseChangeSync(ifClient ifclientset.Clientset,
80 84
 	err = rs.sync(ctx, relsToSync)
81 85
 	cancel()
82 86
 	if err != nil {
83
-		err := fmt.Errorf("Failure to sync cluster after manual chart release changes: %#v", err)
84
-		return false, err
87
+		return false, errors.Wrap(err, "syncing cluster after manual chart release changes")
85 88
 	}
86 89
 
87 90
 	return true, nil
88 91
 }
89 92
 
90 93
 // getCustomResources retrieves FluxHelmRelease resources
91
-//		and outputs them organised by namespace and: Chart release name or Custom Resource name
92
-//						map[namespace] = []ReleaseFhr
93
-func (rs *ReleaseChangeSync) getCustomResources(ifClient ifclientset.Clientset, namespaces []string) (map[string][]ReleaseFhr, error) {
94
-	relInfo := make(map[string][]ReleaseFhr)
94
+// and returns them organised by namespace and chart release name.
95
+// map[namespace] = []releaseFhr.
96
+func (rs *ReleaseChangeSync) getCustomResources(
97
+	ifClient ifclientset.Clientset,
98
+	namespaces []string) (map[string][]releaseFhr, error) {
99
+
100
+	relInfo := make(map[string][]releaseFhr)
95 101
 
96 102
 	for _, ns := range namespaces {
97 103
 		list, err := customresource.GetNSCustomResources(ifClient, ns)
98 104
 		if err != nil {
99
-			rs.logger.Log("error", fmt.Errorf("Failure while retrieving FluxHelmReleases in namespace %s: %v", ns, err))
100
-			return nil, err
105
+			return nil, errors.Wrap(err,
106
+				fmt.Sprintf("retrieving FluxHelmReleases in namespace %s", ns))
101 107
 		}
102
-		rf := []ReleaseFhr{}
108
+
109
+		rf := []releaseFhr{}
103 110
 		for _, fhr := range list.Items {
104 111
 			relName := chartrelease.GetReleaseName(fhr)
105
-			rf = append(rf, ReleaseFhr{RelName: relName, Fhr: fhr})
112
+			rf = append(rf, releaseFhr{RelName: relName, Fhr: fhr})
106 113
 		}
107 114
 		if len(rf) > 0 {
108 115
 			relInfo[ns] = rf
@@ -111,7 +118,13 @@ func (rs *ReleaseChangeSync) getCustomResources(ifClient ifclientset.Clientset,
111 118
 	return relInfo, nil
112 119
 }
113 120
 
114
-func (rs *ReleaseChangeSync) shouldUpgrade(currRel *hapi_release.Release, fhr ifv1.FluxHelmRelease) (bool, error) {
121
+// shouldUpgrade returns true if the current running values or chart
122
+// don't match what the repo says we ought to be running, based on
123
+// doing a dry run install from the chart in the git repo.
124
+func (rs *ReleaseChangeSync) shouldUpgrade(
125
+	currRel *hapi_release.Release,
126
+	fhr ifv1.FluxHelmRelease) (bool, error) {
127
+
115 128
 	if currRel == nil {
116 129
 		return false, fmt.Errorf("No Chart release provided for %v", fhr.GetName())
117 130
 	}
@@ -122,7 +135,7 @@ func (rs *ReleaseChangeSync) shouldUpgrade(currRel *hapi_release.Release, fhr if
122 135
 	// Get the desired release state
123 136
 	opts := chartrelease.InstallOptions{DryRun: true}
124 137
 	tempRelName := strings.Join([]string{currRel.GetName(), "temp"}, "-")
125
-	desRel, err := rs.release.Install(rs.release.Repo.ConfigSync, tempRelName, fhr, "CREATE", opts)
138
+	desRel, err := rs.release.Install(rs.release.ConfigSync(), tempRelName, fhr, "CREATE", opts)
126 139
 	if err != nil {
127 140
 		return false, err
128 141
 	}
@@ -142,8 +155,10 @@ func (rs *ReleaseChangeSync) shouldUpgrade(currRel *hapi_release.Release, fhr if
142 155
 	return false, nil
143 156
 }
144 157
 
145
-// existingReleasesToSync determines which Chart releases need to be deleted/upgraded
146
-// to bring the cluster to the desired state
158
+// addExistingReleasesToSync populates relsToSync (map from namespace
159
+// to chartRelease) with the members of currentReleases that need
160
+// updating because they're diverged from the desired state.  Desired
161
+// state is specified by customResources and what's in our git checkout.
147 162
 func (rs *ReleaseChangeSync) addExistingReleasesToSync(
148 163
 	relsToSync map[string][]chartRelease,
149 164
 	currentReleases map[string]map[string]struct{},
@@ -185,8 +200,9 @@ func (rs *ReleaseChangeSync) addExistingReleasesToSync(
185 200
 	return nil
186 201
 }
187 202
 
188
-// deletedReleasesToSync determines which Chart releases need to be installed
189
-// to bring the cluster to the desired state
203
+// addDeletedReleasesToSync populates relsToSync (map from namespace
204
+// to chartRelease) with chartReleases based on the charts referenced
205
+// in customResources that are absent from currentReleases.
190 206
 func (rs *ReleaseChangeSync) addDeletedReleasesToSync(
191 207
 	relsToSync map[string][]chartRelease,
192 208
 	currentReleases map[string]map[string]struct{},
@@ -224,35 +240,53 @@ func (rs *ReleaseChangeSync) addDeletedReleasesToSync(
224 240
 	return nil
225 241
 }
226 242
 
227
-func (rs *ReleaseChangeSync) releasesToSync(ctx context.Context, ifClient ifclientset.Clientset, ns []string) (map[string][]chartRelease, error) {
243
+// releasesToSync queries Tiller to get all current Helm releases, queries k8s
244
+// custom resources to get all FluxHelmRelease(s), and returns a map from
245
+// namespace to chartRelease(s) that need to be synced.
246
+func (rs *ReleaseChangeSync) releasesToSync(
247
+	ctx context.Context,
248
+	ifClient ifclientset.Clientset,
249
+	ns []string) (map[string][]chartRelease, error) {
250
+
228 251
 	relDepl, err := rs.release.GetCurrent()
229 252
 	if err != nil {
230 253
 		return nil, err
231 254
 	}
232
-	curRels := MappifyDeployInfo(relDepl)
255
+	curRels := mappifyDeployInfo(relDepl)
233 256
 
234 257
 	relCrs, err := rs.getCustomResources(ifClient, ns)
235 258
 	if err != nil {
236 259
 		return nil, err
237 260
 	}
238
-	crs := MappifyReleaseFhrInfo(relCrs)
261
+	crs := mappifyReleaseFhrInfo(relCrs)
239 262
 
240 263
 	relsToSync := make(map[string][]chartRelease)
241
-	rs.addDeletedReleasesToSync(relsToSync, curRels, crs)
242
-	rs.addExistingReleasesToSync(relsToSync, curRels, crs)
264
+
265
+	// Make explicit that we're throwing away errors
266
+	_ = rs.addDeletedReleasesToSync(relsToSync, curRels, crs)
267
+	_ = rs.addExistingReleasesToSync(relsToSync, curRels, crs)
243 268
 
244 269
 	return relsToSync, nil
245 270
 }
246 271
 
247
-func (rs *ReleaseChangeSync) sync(ctx context.Context, releases map[string][]chartRelease) error {
272
+// sync takes a map from namespace to list of chartRelease(s)
273
+// that need to be applied, and attempts to apply them.
274
+// It returns the first error encountered.  A chart missing
275
+// from the repo doesn't count as an error, but will be logged.
276
+func (rs *ReleaseChangeSync) sync(
277
+	ctx context.Context,
278
+	releases map[string][]chartRelease) error {
279
+
280
+	// TODO it's weird that we do a pull here, after we've already decided
281
+	// what to do.  Ask why.
248 282
 	ctx, cancel := context.WithTimeout(ctx, helmgit.DefaultPullTimeout)
249
-	err := rs.release.Repo.ConfigSync.Pull(ctx)
283
+	err := rs.release.ConfigSync().Pull(ctx)
250 284
 	cancel()
251 285
 	if err != nil {
252 286
 		return fmt.Errorf("Failure while pulling repo: %#v", err)
253 287
 	}
254 288
 
255
-	checkout := rs.release.Repo.ConfigSync
289
+	checkout := rs.release.ConfigSync()
256 290
 	chartPathBase := filepath.Join(checkout.Dir, checkout.Config.Path)
257 291
 
258 292
 	opts := chartrelease.InstallOptions{DryRun: false}
@@ -282,6 +316,8 @@ func (rs *ReleaseChangeSync) sync(ctx context.Context, releases map[string][]cha
282 316
 				if err != nil {
283 317
 					return err
284 318
 				}
319
+			default:
320
+				panic(fmt.Sprintf("invalid action %q", chr.action))
285 321
 			}
286 322
 		}
287 323
 	}

+ 353
- 0
integrations/helm/releasesync/releasesync_test.go View File

@@ -0,0 +1,353 @@
1
+package releasesync
2
+
3
+import (
4
+	"fmt"
5
+	"os"
6
+	"testing"
7
+
8
+	proto "github.com/golang/protobuf/proto"
9
+	"github.com/google/go-cmp/cmp"
10
+	"k8s.io/helm/pkg/helm"
11
+	hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"
12
+	hapi_release "k8s.io/helm/pkg/proto/hapi/release"
13
+
14
+	"github.com/go-kit/kit/log"
15
+	ifv1 "github.com/weaveworks/flux/apis/helm.integrations.flux.weave.works/v1alpha2"
16
+	helmgit "github.com/weaveworks/flux/integrations/helm/git"
17
+	"github.com/weaveworks/flux/integrations/helm/release"
18
+	chartrelease "github.com/weaveworks/flux/integrations/helm/release"
19
+)
20
+
21
+type installReq struct {
22
+	checkoutDir string
23
+	releaseName string
24
+	fhr         ifv1.FluxHelmRelease
25
+	action      chartrelease.Action
26
+	opts        chartrelease.InstallOptions
27
+}
28
+
29
+type installResult struct {
30
+	release hapi_release.Release
31
+	err     error
32
+}
33
+
34
+type install struct {
35
+	installReq
36
+	installResult
37
+}
38
+
39
+type mockReleaser struct {
40
+	current    map[string][]chartrelease.DeployInfo
41
+	deployed   map[string]*hapi_release.Release
42
+	configSync *helmgit.Checkout
43
+	installs   []install
44
+}
45
+
46
+func (r *mockReleaser) GetCurrent() (map[string][]chartrelease.DeployInfo, error) {
47
+	if r.current == nil {
48
+		return nil, fmt.Errorf("failed to fetch current releases")
49
+	}
50
+	return r.current, nil
51
+}
52
+
53
+func (r *mockReleaser) GetDeployedRelease(name string) (*hapi_release.Release, error) {
54
+	if _, present := r.deployed[name]; !present {
55
+		return nil, fmt.Errorf("no release hamed %q", name)
56
+	}
57
+	return r.deployed[name], nil
58
+}
59
+
60
+func (r *mockReleaser) ConfigSync() *helmgit.Checkout {
61
+	return r.configSync
62
+}
63
+
64
+func (r *mockReleaser) Install(checkout *helmgit.Checkout,
65
+	releaseName string,
66
+	fhr ifv1.FluxHelmRelease,
67
+	action chartrelease.Action,
68
+	opts chartrelease.InstallOptions) (hapi_release.Release, error) {
69
+	req := installReq{
70
+		checkoutDir: checkout.Dir,
71
+		releaseName: releaseName,
72
+		fhr:         fhr,
73
+		action:      action,
74
+		opts:        opts}
75
+	cmpopts := cmp.AllowUnexported(installReq{})
76
+	for _, i := range r.installs {
77
+		if cmp.Equal(i.installReq, req, cmpopts) {
78
+			return i.installResult.release, i.installResult.err
79
+		}
80
+	}
81
+	return hapi_release.Release{}, fmt.Errorf("unexpected request: %+v", req)
82
+}
83
+
84
+func makeCurRel(ns string, relNames ...string) map[string]map[string]struct{} {
85
+	m := make(map[string]map[string]struct{})
86
+	m[ns] = make(map[string]struct{})
87
+	for _, relName := range relNames {
88
+		m[ns][relName] = struct{}{}
89
+	}
90
+	return m
91
+}
92
+
93
+func mergeCurRels(a, b map[string]map[string]struct{}) map[string]map[string]struct{} {
94
+	m := make(map[string]map[string]struct{})
95
+	for ns := range a {
96
+		m[ns] = a[ns]
97
+	}
98
+	for ns := range b {
99
+		if _, present := m[ns]; present {
100
+			panic("ns '" + ns + "' present in both a and b")
101
+		}
102
+		m[ns] = b[ns]
103
+	}
104
+	return m
105
+}
106
+
107
+func makeCustRes(ns string, relNames ...string) map[string]map[string]ifv1.FluxHelmRelease {
108
+	m := make(map[string]map[string]ifv1.FluxHelmRelease)
109
+	m[ns] = make(map[string]ifv1.FluxHelmRelease)
110
+	for _, relName := range relNames {
111
+		m[ns][relName] = ifv1.FluxHelmRelease{}
112
+	}
113
+	return m
114
+}
115
+
116
+func mergeCustRes(a, b map[string]map[string]ifv1.FluxHelmRelease) map[string]map[string]ifv1.FluxHelmRelease {
117
+	m := make(map[string]map[string]ifv1.FluxHelmRelease)
118
+	for ns := range a {
119
+		m[ns] = a[ns]
120
+	}
121
+	for ns := range b {
122
+		if _, present := m[ns]; present {
123
+			panic("ns '" + ns + "' present in both a and b")
124
+		}
125
+		m[ns] = b[ns]
126
+	}
127
+	return m
128
+}
129
+
130
+func TestAddDeletedReleasesToSync(t *testing.T) {
131
+	var zeromap = make(map[string][]chartRelease)
132
+	var tests = []struct {
133
+		msg             string
134
+		currentReleases map[string]map[string]struct{}
135
+		customResources map[string]map[string]ifv1.FluxHelmRelease
136
+		want            map[string][]chartRelease
137
+	}{
138
+		{
139
+			msg:             "no-op, zero resources",
140
+			currentReleases: makeCurRel("ns1", "r1"),
141
+			customResources: make(map[string]map[string]ifv1.FluxHelmRelease),
142
+			want:            zeromap,
143
+		},
144
+		{
145
+			msg:             "no-op, equality",
146
+			currentReleases: makeCurRel("ns1", "r1"),
147
+			customResources: makeCustRes("ns1", "r1"),
148
+			want:            zeromap,
149
+		},
150
+		{
151
+			msg:             "add missing release",
152
+			currentReleases: makeCurRel("ns1"),
153
+			customResources: makeCustRes("ns1", "r1"),
154
+			want: map[string][]chartRelease{"ns1": []chartRelease{
155
+				chartRelease{releaseName: "r1", action: release.InstallAction}}},
156
+		},
157
+		{
158
+			msg:             "add missing release new namespace",
159
+			currentReleases: makeCurRel("ns1"),
160
+			customResources: makeCustRes("ns2", "r1"),
161
+			want: map[string][]chartRelease{"ns2": []chartRelease{
162
+				chartRelease{releaseName: "r1", action: release.InstallAction}}},
163
+		},
164
+		{
165
+			msg: "add missing releases multi namespace",
166
+			currentReleases: mergeCurRels(makeCurRel("ns1"),
167
+				makeCurRel("ns2", "r2")),
168
+			customResources: mergeCustRes(makeCustRes("ns1", "r1"),
169
+				makeCustRes("ns2", "r2", "r3")),
170
+			want: map[string][]chartRelease{
171
+				"ns1": []chartRelease{chartRelease{releaseName: "r1", action: release.InstallAction}},
172
+				"ns2": []chartRelease{chartRelease{releaseName: "r3", action: release.InstallAction}},
173
+			},
174
+		},
175
+	}
176
+
177
+	opts := cmp.AllowUnexported(chartRelease{})
178
+	rs := New(log.NewLogfmtLogger(os.Stdout), nil)
179
+	for i, test := range tests {
180
+		var got = make(map[string][]chartRelease)
181
+		err := rs.addDeletedReleasesToSync(got, test.currentReleases, test.customResources)
182
+		if err != nil {
183
+			t.Errorf("%d %s: got error: %v", i, test.msg, err)
184
+		}
185
+		if diff := cmp.Diff(got, test.want, opts); diff != "" {
186
+			t.Errorf("%d %s: diff (-got +want)\n%s", i, test.msg, diff)
187
+		}
188
+
189
+	}
190
+}
191
+
192
+func config(vals map[string]string) *hapi_chart.Config {
193
+	pv := make(map[string]*hapi_chart.Value)
194
+	for k, v := range vals {
195
+		pv[k] = &hapi_chart.Value{Value: v}
196
+	}
197
+
198
+	c := &hapi_chart.Config{Values: pv}
199
+	// Marshalling to get c.Raw populated
200
+	data, _ := proto.Marshal(c)
201
+	_ = proto.Unmarshal(data, c)
202
+	return c
203
+}
204
+
205
+func relvals(name string, vals string) *hapi_release.Release {
206
+	rel := helm.ReleaseMock(&helm.MockReleaseOptions{Name: name})
207
+	rel.Config.Raw = vals
208
+	return rel
209
+}
210
+
211
+func relchart(name string, chartname string, chartver string, tmplname string) *hapi_release.Release {
212
+	return helm.ReleaseMock(&helm.MockReleaseOptions{Name: name, Chart: &hapi_chart.Chart{
213
+		Metadata: &hapi_chart.Metadata{
214
+			Name:    chartname,
215
+			Version: chartver,
216
+		},
217
+		Templates: []*hapi_chart.Template{
218
+			{Name: tmplname, Data: []byte(helm.MockManifest)},
219
+		},
220
+	}})
221
+}
222
+
223
+func TestAddExistingReleasesToSync(t *testing.T) {
224
+	var zeromap = make(map[string][]chartRelease)
225
+	var tests = []struct {
226
+		msg             string
227
+		currentReleases map[string]map[string]struct{}
228
+		customResources map[string]map[string]ifv1.FluxHelmRelease
229
+		want            map[string][]chartRelease
230
+		releaser        chartrelease.Releaser
231
+		wanterror       error
232
+	}{
233
+		{
234
+			msg:             "no-op, zero resources",
235
+			currentReleases: makeCurRel("ns1", "r1"),
236
+			customResources: make(map[string]map[string]ifv1.FluxHelmRelease),
237
+			want:            zeromap,
238
+		},
239
+		{
240
+			msg:             "no-op, no overlap",
241
+			currentReleases: makeCurRel("ns1", "r1"),
242
+			customResources: mergeCustRes(
243
+				makeCustRes("ns1", "r2"),
244
+				makeCustRes("ns2", "r1")),
245
+			want: zeromap,
246
+		},
247
+		{
248
+			msg:             "get deployed release fails",
249
+			currentReleases: makeCurRel("ns1", "r1"),
250
+			customResources: makeCustRes("ns1", "r1"),
251
+			releaser:        &mockReleaser{},
252
+			wanterror:       fmt.Errorf("no release hamed %q", "r1"),
253
+		},
254
+		{
255
+			msg:             "dry-run install fails",
256
+			currentReleases: makeCurRel("ns1", "r1"),
257
+			customResources: makeCustRes("ns1", "r1"),
258
+			releaser: &mockReleaser{
259
+				configSync: &helmgit.Checkout{Dir: "dir"},
260
+				deployed: map[string]*hapi_release.Release{
261
+					"r1": relvals("r1", `k1: "v1"`),
262
+				},
263
+				installs: []install{
264
+					dryinst("r1", *relvals("", ""), fmt.Errorf("dry-run failed")),
265
+				},
266
+			},
267
+			wanterror: fmt.Errorf("dry-run failed"),
268
+		},
269
+		{
270
+			msg: "r1 vals changed, r2 unchanged",
271
+			currentReleases: mergeCurRels(
272
+				makeCurRel("ns1", "r1"),
273
+				makeCurRel("ns2", "r2")),
274
+			customResources: mergeCustRes(
275
+				makeCustRes("ns1", "r1"),
276
+				makeCustRes("ns2", "r2")),
277
+			releaser: &mockReleaser{
278
+				configSync: &helmgit.Checkout{Dir: "dir"},
279
+				deployed: map[string]*hapi_release.Release{
280
+					"r1": relvals("r1", `k1: "v1"`),
281
+					"r2": relvals("r2", `k1: "v1"`),
282
+				},
283
+				installs: []install{
284
+					dryinst("r1", *relvals("r1", `k1: "v2"`), nil),
285
+					dryinst("r2", *relvals("r2", `k1: "v1"`), nil),
286
+				},
287
+			},
288
+			want: map[string][]chartRelease{"ns1": []chartRelease{
289
+				chartRelease{releaseName: "r1", action: release.Action("UPDATE")},
290
+			}},
291
+		},
292
+		{
293
+			msg: "r1/r2/r3 charts changed, r4 unchanged",
294
+			currentReleases: mergeCurRels(mergeCurRels(
295
+				makeCurRel("ns1", "r1"),
296
+				makeCurRel("ns2", "r2")),
297
+				makeCurRel("ns3", "r3", "r4")),
298
+			customResources: mergeCustRes(mergeCustRes(
299
+				makeCustRes("ns1", "r1"),
300
+				makeCustRes("ns2", "r2")),
301
+				makeCustRes("ns3", "r3", "r4")),
302
+			releaser: &mockReleaser{
303
+				configSync: &helmgit.Checkout{Dir: "dir"},
304
+				deployed: map[string]*hapi_release.Release{
305
+					"r1": relchart("r1", "c1", "v0.1.0", "templates/foo.tpl"),
306
+					"r2": relchart("r2", "c2", "v0.1.0", "templates/bar.tpl"),
307
+					"r3": relchart("r3", "c3", "v0.1.0", "templates/baz.tpl"),
308
+					"r4": relchart("r4", "c4", "v0.1.0", "templates/qux.tpl"),
309
+				},
310
+				installs: []install{
311
+					dryinst("r1", *relchart("r1", "c-", "v0.1.0", "templates/foo.tpl"), nil),
312
+					dryinst("r2", *relchart("r2", "c2", "v0.1.1", "templates/bar.tpl"), nil),
313
+					dryinst("r3", *relchart("r3", "c3", "v0.1.0", "templates/foo.tpl"), nil),
314
+					dryinst("r4", *relchart("r4", "c4", "v0.1.0", "templates/qux.tpl"), nil),
315
+				},
316
+			},
317
+			want: map[string][]chartRelease{
318
+				"ns1": []chartRelease{chartRelease{releaseName: "r1", action: release.Action("UPDATE")}},
319
+				"ns2": []chartRelease{chartRelease{releaseName: "r2", action: release.Action("UPDATE")}},
320
+				"ns3": []chartRelease{chartRelease{releaseName: "r3", action: release.Action("UPDATE")}},
321
+			},
322
+		},
323
+	}
324
+
325
+	opts := cmp.AllowUnexported(chartRelease{})
326
+	for i, test := range tests {
327
+		rs := New(log.NewLogfmtLogger(os.Stdout), test.releaser)
328
+		var got = make(map[string][]chartRelease)
329
+		err := rs.addExistingReleasesToSync(got, test.currentReleases, test.customResources)
330
+		if fmt.Sprintf("%v", err) != fmt.Sprintf("%v", test.wanterror) {
331
+			t.Errorf("%d %s: got error %q, want error %q", i, test.msg, err, test.wanterror)
332
+		}
333
+		if test.wanterror != nil {
334
+			continue
335
+		}
336
+		if diff := cmp.Diff(got, test.want, opts); diff != "" {
337
+			t.Errorf("%d %s: diff (-got +want)\n%s", i, test.msg, diff)
338
+		}
339
+
340
+	}
341
+}
342
+
343
+func dryinst(relname string, rel hapi_release.Release, err error) install {
344
+	return install{
345
+		installReq{
346
+			checkoutDir: "dir",
347
+			releaseName: relname + "-temp",
348
+			action:      "CREATE",
349
+			opts:        chartrelease.InstallOptions{DryRun: true},
350
+		},
351
+		installResult{rel, err},
352
+	}
353
+}

+ 8
- 2
integrations/helm/releasesync/utils.go View File

@@ -5,7 +5,10 @@ import (
5 5
 	"github.com/weaveworks/flux/integrations/helm/release"
6 6
 )
7 7
 
8
-func MappifyDeployInfo(releases map[string][]release.DeployInfo) map[string]map[string]struct{} {
8
+// mappifyDeployInfo takes a map of namespace -> []DeployInfo,
9
+// returning a map whose keys are the same namespaces
10
+// and whose values are key-only maps holding the DeployInfo names.
11
+func mappifyDeployInfo(releases map[string][]release.DeployInfo) map[string]map[string]struct{} {
9 12
 	deployM := make(map[string]map[string]struct{})
10 13
 
11 14
 	for ns, nsRels := range releases {
@@ -18,7 +21,10 @@ func MappifyDeployInfo(releases map[string][]release.DeployInfo) map[string]map[
18 21
 	return deployM
19 22
 }
20 23
 
21
-func MappifyReleaseFhrInfo(fhrs map[string][]ReleaseFhr) map[string]map[string]ifv1.FluxHelmRelease {
24
+// mappifyReleaseFhrInfo takes a map of namespace -> []releaseFhr,
25
+// returning a map whose keys are the same namespaces
26
+// and whose values are maps of releaseName -> FluxHelmRelease.
27
+func mappifyReleaseFhrInfo(fhrs map[string][]releaseFhr) map[string]map[string]ifv1.FluxHelmRelease {
22 28
 	relFhrM := make(map[string]map[string]ifv1.FluxHelmRelease)
23 29
 
24 30
 	for ns, nsFhrs := range fhrs {

Loading…
Cancel
Save