argo-cd

Форк
0
/
state_test.go 
1668 строк · 58.5 Кб
1
package controller
2

3
import (
4
	"encoding/json"
5
	"fmt"
6
	"os"
7
	"testing"
8
	"time"
9

10
	"github.com/argoproj/gitops-engine/pkg/health"
11
	synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
12
	"github.com/argoproj/gitops-engine/pkg/utils/kube"
13
	. "github.com/argoproj/gitops-engine/pkg/utils/testing"
14
	"github.com/imdario/mergo"
15
	"github.com/sirupsen/logrus"
16
	logrustest "github.com/sirupsen/logrus/hooks/test"
17
	"github.com/stretchr/testify/assert"
18
	v1 "k8s.io/api/apps/v1"
19
	corev1 "k8s.io/api/core/v1"
20
	networkingv1 "k8s.io/api/networking/v1"
21
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
23
	"k8s.io/apimachinery/pkg/runtime"
24

25
	"github.com/argoproj/argo-cd/v2/common"
26
	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
27
	argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
28
	"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
29
	"github.com/argoproj/argo-cd/v2/test"
30
	"github.com/argoproj/argo-cd/v2/util/argo"
31
)
32

33
// TestCompareAppStateEmpty tests comparison when both git and live have no objects
34
func TestCompareAppStateEmpty(t *testing.T) {
35
	app := newFakeApp()
36
	data := fakeData{
37
		manifestResponse: &apiclient.ManifestResponse{
38
			Manifests: []string{},
39
			Namespace: test.FakeDestNamespace,
40
			Server:    test.FakeClusterURL,
41
			Revision:  "abc123",
42
		},
43
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
44
	}
45
	ctrl := newFakeController(&data, nil)
46
	sources := make([]argoappv1.ApplicationSource, 0)
47
	sources = append(sources, app.Spec.GetSource())
48
	revisions := make([]string, 0)
49
	revisions = append(revisions, "")
50
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
51
	assert.Nil(t, err)
52
	assert.NotNil(t, compRes)
53
	assert.NotNil(t, compRes.syncStatus)
54
	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
55
	assert.Len(t, compRes.resources, 0)
56
	assert.Len(t, compRes.managedResources, 0)
57
	assert.Len(t, app.Status.Conditions, 0)
58
}
59

60
// TestCompareAppStateRepoError tests the case when CompareAppState notices a repo error
61
func TestCompareAppStateRepoError(t *testing.T) {
62
	app := newFakeApp()
63
	ctrl := newFakeController(&fakeData{manifestResponses: make([]*apiclient.ManifestResponse, 3)}, fmt.Errorf("test repo error"))
64
	sources := make([]argoappv1.ApplicationSource, 0)
65
	sources = append(sources, app.Spec.GetSource())
66
	revisions := make([]string, 0)
67
	revisions = append(revisions, "")
68
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
69
	assert.Nil(t, compRes)
70
	assert.EqualError(t, err, CompareStateRepoError.Error())
71

72
	// expect to still get compare state error to as inside grace period
73
	compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
74
	assert.Nil(t, compRes)
75
	assert.EqualError(t, err, CompareStateRepoError.Error())
76

77
	time.Sleep(10 * time.Second)
78
	// expect to not get error as outside of grace period, but status should be unknown
79
	compRes, err = ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
80
	assert.NotNil(t, compRes)
81
	assert.Nil(t, err)
82
	assert.Equal(t, compRes.syncStatus.Status, argoappv1.SyncStatusCodeUnknown)
83
}
84

85
// TestCompareAppStateNamespaceMetadataDiffers tests comparison when managed namespace metadata differs
86
func TestCompareAppStateNamespaceMetadataDiffers(t *testing.T) {
87
	app := newFakeApp()
88
	app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{
89
		Labels: map[string]string{
90
			"foo": "bar",
91
		},
92
		Annotations: map[string]string{
93
			"foo": "bar",
94
		},
95
	}
96
	app.Status.OperationState = &argoappv1.OperationState{
97
		SyncResult: &argoappv1.SyncOperationResult{},
98
	}
99

100
	data := fakeData{
101
		manifestResponse: &apiclient.ManifestResponse{
102
			Manifests: []string{},
103
			Namespace: test.FakeDestNamespace,
104
			Server:    test.FakeClusterURL,
105
			Revision:  "abc123",
106
		},
107
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
108
	}
109
	ctrl := newFakeController(&data, nil)
110
	sources := make([]argoappv1.ApplicationSource, 0)
111
	sources = append(sources, app.Spec.GetSource())
112
	revisions := make([]string, 0)
113
	revisions = append(revisions, "")
114
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
115
	assert.Nil(t, err)
116
	assert.NotNil(t, compRes)
117
	assert.NotNil(t, compRes.syncStatus)
118
	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
119
	assert.Len(t, compRes.resources, 0)
120
	assert.Len(t, compRes.managedResources, 0)
121
	assert.Len(t, app.Status.Conditions, 0)
122
}
123

124
// TestCompareAppStateNamespaceMetadataDiffers tests comparison when managed namespace metadata differs to live and manifest ns
125
func TestCompareAppStateNamespaceMetadataDiffersToManifest(t *testing.T) {
126
	ns := NewNamespace()
127
	ns.SetName(test.FakeDestNamespace)
128
	ns.SetNamespace(test.FakeDestNamespace)
129
	ns.SetAnnotations(map[string]string{"bar": "bat"})
130

131
	app := newFakeApp()
132
	app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{
133
		Labels: map[string]string{
134
			"foo": "bar",
135
		},
136
		Annotations: map[string]string{
137
			"foo": "bar",
138
		},
139
	}
140
	app.Status.OperationState = &argoappv1.OperationState{
141
		SyncResult: &argoappv1.SyncOperationResult{},
142
	}
143

144
	liveNs := ns.DeepCopy()
145
	liveNs.SetAnnotations(nil)
146

147
	data := fakeData{
148
		manifestResponse: &apiclient.ManifestResponse{
149
			Manifests: []string{toJSON(t, liveNs)},
150
			Namespace: test.FakeDestNamespace,
151
			Server:    test.FakeClusterURL,
152
			Revision:  "abc123",
153
		},
154
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
155
			kube.GetResourceKey(ns): ns,
156
		},
157
	}
158
	ctrl := newFakeController(&data, nil)
159
	sources := make([]argoappv1.ApplicationSource, 0)
160
	sources = append(sources, app.Spec.GetSource())
161
	revisions := make([]string, 0)
162
	revisions = append(revisions, "")
163
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
164
	assert.Nil(t, err)
165
	assert.NotNil(t, compRes)
166
	assert.NotNil(t, compRes.syncStatus)
167
	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
168
	assert.Len(t, compRes.resources, 1)
169
	assert.Len(t, compRes.managedResources, 1)
170
	assert.NotNil(t, compRes.diffResultList)
171
	assert.Len(t, compRes.diffResultList.Diffs, 1)
172

173
	result := NewNamespace()
174
	assert.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result))
175

176
	labels := result.GetLabels()
177
	delete(labels, "kubernetes.io/metadata.name")
178

179
	assert.Equal(t, map[string]string{}, labels)
180
	// Manifests override definitions in managedNamespaceMetadata
181
	assert.Equal(t, map[string]string{"bar": "bat"}, result.GetAnnotations())
182
	assert.Len(t, app.Status.Conditions, 0)
183
}
184

185
// TestCompareAppStateNamespaceMetadata tests comparison when managed namespace metadata differs to live
186
func TestCompareAppStateNamespaceMetadata(t *testing.T) {
187
	ns := NewNamespace()
188
	ns.SetName(test.FakeDestNamespace)
189
	ns.SetNamespace(test.FakeDestNamespace)
190
	ns.SetAnnotations(map[string]string{"bar": "bat"})
191

192
	app := newFakeApp()
193
	app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{
194
		Labels: map[string]string{
195
			"foo": "bar",
196
		},
197
		Annotations: map[string]string{
198
			"foo": "bar",
199
		},
200
	}
201
	app.Status.OperationState = &argoappv1.OperationState{
202
		SyncResult: &argoappv1.SyncOperationResult{},
203
	}
204

205
	data := fakeData{
206
		manifestResponse: &apiclient.ManifestResponse{
207
			Manifests: []string{},
208
			Namespace: test.FakeDestNamespace,
209
			Server:    test.FakeClusterURL,
210
			Revision:  "abc123",
211
		},
212
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
213
			kube.GetResourceKey(ns): ns,
214
		},
215
	}
216
	ctrl := newFakeController(&data, nil)
217
	sources := make([]argoappv1.ApplicationSource, 0)
218
	sources = append(sources, app.Spec.GetSource())
219
	revisions := make([]string, 0)
220
	revisions = append(revisions, "")
221
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
222
	assert.Nil(t, err)
223
	assert.NotNil(t, compRes)
224
	assert.NotNil(t, compRes.syncStatus)
225
	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
226
	assert.Len(t, compRes.resources, 1)
227
	assert.Len(t, compRes.managedResources, 1)
228
	assert.NotNil(t, compRes.diffResultList)
229
	assert.Len(t, compRes.diffResultList.Diffs, 1)
230

231
	result := NewNamespace()
232
	assert.NoError(t, json.Unmarshal(compRes.diffResultList.Diffs[0].PredictedLive, result))
233

234
	labels := result.GetLabels()
235
	delete(labels, "kubernetes.io/metadata.name")
236

237
	assert.Equal(t, map[string]string{"foo": "bar"}, labels)
238
	assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "bar": "bat", "foo": "bar"}, result.GetAnnotations())
239
	assert.Len(t, app.Status.Conditions, 0)
240
}
241

242
// TestCompareAppStateNamespaceMetadataIsTheSame tests comparison when managed namespace metadata is the same
243
func TestCompareAppStateNamespaceMetadataIsTheSame(t *testing.T) {
244
	app := newFakeApp()
245
	app.Spec.SyncPolicy.ManagedNamespaceMetadata = &argoappv1.ManagedNamespaceMetadata{
246
		Labels: map[string]string{
247
			"foo": "bar",
248
		},
249
		Annotations: map[string]string{
250
			"foo": "bar",
251
		},
252
	}
253
	app.Status.OperationState = &argoappv1.OperationState{
254
		SyncResult: &argoappv1.SyncOperationResult{
255
			ManagedNamespaceMetadata: &argoappv1.ManagedNamespaceMetadata{
256
				Labels: map[string]string{
257
					"foo": "bar",
258
				},
259
				Annotations: map[string]string{
260
					"foo": "bar",
261
				},
262
			},
263
		},
264
	}
265

266
	data := fakeData{
267
		manifestResponse: &apiclient.ManifestResponse{
268
			Manifests: []string{},
269
			Namespace: test.FakeDestNamespace,
270
			Server:    test.FakeClusterURL,
271
			Revision:  "abc123",
272
		},
273
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
274
	}
275
	ctrl := newFakeController(&data, nil)
276
	sources := make([]argoappv1.ApplicationSource, 0)
277
	sources = append(sources, app.Spec.GetSource())
278
	revisions := make([]string, 0)
279
	revisions = append(revisions, "")
280
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
281
	assert.Nil(t, err)
282
	assert.NotNil(t, compRes)
283
	assert.NotNil(t, compRes.syncStatus)
284
	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
285
	assert.Len(t, compRes.resources, 0)
286
	assert.Len(t, compRes.managedResources, 0)
287
	assert.Len(t, app.Status.Conditions, 0)
288
}
289

290
// TestCompareAppStateMissing tests when there is a manifest defined in the repo which doesn't exist in live
291
func TestCompareAppStateMissing(t *testing.T) {
292
	app := newFakeApp()
293
	data := fakeData{
294
		apps: []runtime.Object{app},
295
		manifestResponse: &apiclient.ManifestResponse{
296
			Manifests: []string{PodManifest},
297
			Namespace: test.FakeDestNamespace,
298
			Server:    test.FakeClusterURL,
299
			Revision:  "abc123",
300
		},
301
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
302
	}
303
	ctrl := newFakeController(&data, nil)
304
	sources := make([]argoappv1.ApplicationSource, 0)
305
	sources = append(sources, app.Spec.GetSource())
306
	revisions := make([]string, 0)
307
	revisions = append(revisions, "")
308
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
309
	assert.Nil(t, err)
310
	assert.NotNil(t, compRes)
311
	assert.NotNil(t, compRes.syncStatus)
312
	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
313
	assert.Len(t, compRes.resources, 1)
314
	assert.Len(t, compRes.managedResources, 1)
315
	assert.Len(t, app.Status.Conditions, 0)
316
}
317

318
// TestCompareAppStateExtra tests when there is an extra object in live but not defined in git
319
func TestCompareAppStateExtra(t *testing.T) {
320
	pod := NewPod()
321
	pod.SetNamespace(test.FakeDestNamespace)
322
	app := newFakeApp()
323
	key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name}
324
	data := fakeData{
325
		manifestResponse: &apiclient.ManifestResponse{
326
			Manifests: []string{},
327
			Namespace: test.FakeDestNamespace,
328
			Server:    test.FakeClusterURL,
329
			Revision:  "abc123",
330
		},
331
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
332
			key: pod,
333
		},
334
	}
335
	ctrl := newFakeController(&data, nil)
336
	sources := make([]argoappv1.ApplicationSource, 0)
337
	sources = append(sources, app.Spec.GetSource())
338
	revisions := make([]string, 0)
339
	revisions = append(revisions, "")
340
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
341
	assert.Nil(t, err)
342
	assert.NotNil(t, compRes)
343
	assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
344
	assert.Equal(t, 1, len(compRes.resources))
345
	assert.Equal(t, 1, len(compRes.managedResources))
346
	assert.Equal(t, 0, len(app.Status.Conditions))
347
}
348

349
// TestCompareAppStateHook checks that hooks are detected during manifest generation, and not
350
// considered as part of resources when assessing Synced status
351
func TestCompareAppStateHook(t *testing.T) {
352
	pod := NewPod()
353
	pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
354
	podBytes, _ := json.Marshal(pod)
355
	app := newFakeApp()
356
	data := fakeData{
357
		apps: []runtime.Object{app},
358
		manifestResponse: &apiclient.ManifestResponse{
359
			Manifests: []string{string(podBytes)},
360
			Namespace: test.FakeDestNamespace,
361
			Server:    test.FakeClusterURL,
362
			Revision:  "abc123",
363
		},
364
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
365
	}
366
	ctrl := newFakeController(&data, nil)
367
	sources := make([]argoappv1.ApplicationSource, 0)
368
	sources = append(sources, app.Spec.GetSource())
369
	revisions := make([]string, 0)
370
	revisions = append(revisions, "")
371
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
372
	assert.Nil(t, err)
373
	assert.NotNil(t, compRes)
374
	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
375
	assert.Equal(t, 0, len(compRes.resources))
376
	assert.Equal(t, 0, len(compRes.managedResources))
377
	assert.Equal(t, 1, len(compRes.reconciliationResult.Hooks))
378
	assert.Equal(t, 0, len(app.Status.Conditions))
379
}
380

381
// TestCompareAppStateSkipHook checks that skipped resources are detected during manifest generation, and not
382
// considered as part of resources when assessing Synced status
383
func TestCompareAppStateSkipHook(t *testing.T) {
384
	pod := NewPod()
385
	pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "Skip"})
386
	podBytes, _ := json.Marshal(pod)
387
	app := newFakeApp()
388
	data := fakeData{
389
		apps: []runtime.Object{app},
390
		manifestResponse: &apiclient.ManifestResponse{
391
			Manifests: []string{string(podBytes)},
392
			Namespace: test.FakeDestNamespace,
393
			Server:    test.FakeClusterURL,
394
			Revision:  "abc123",
395
		},
396
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
397
	}
398
	ctrl := newFakeController(&data, nil)
399
	sources := make([]argoappv1.ApplicationSource, 0)
400
	sources = append(sources, app.Spec.GetSource())
401
	revisions := make([]string, 0)
402
	revisions = append(revisions, "")
403
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
404
	assert.Nil(t, err)
405
	assert.NotNil(t, compRes)
406
	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
407
	assert.Equal(t, 1, len(compRes.resources))
408
	assert.Equal(t, 1, len(compRes.managedResources))
409
	assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks))
410
	assert.Equal(t, 0, len(app.Status.Conditions))
411
}
412

413
// checks that ignore resources are detected, but excluded from status
414
func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
415
	pod := NewPod()
416
	pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreExtraneous"})
417
	app := newFakeApp()
418
	data := fakeData{
419
		apps: []runtime.Object{app},
420
		manifestResponse: &apiclient.ManifestResponse{
421
			Manifests: []string{},
422
			Namespace: test.FakeDestNamespace,
423
			Server:    test.FakeClusterURL,
424
			Revision:  "abc123",
425
		},
426
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
427
	}
428
	ctrl := newFakeController(&data, nil)
429

430
	sources := make([]argoappv1.ApplicationSource, 0)
431
	sources = append(sources, app.Spec.GetSource())
432
	revisions := make([]string, 0)
433
	revisions = append(revisions, "")
434
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
435
	assert.Nil(t, err)
436

437
	assert.NotNil(t, compRes)
438
	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
439
	assert.Len(t, compRes.resources, 0)
440
	assert.Len(t, compRes.managedResources, 0)
441
	assert.Len(t, app.Status.Conditions, 0)
442
}
443

444
// TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git
445
func TestCompareAppStateExtraHook(t *testing.T) {
446
	pod := NewPod()
447
	pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
448
	pod.SetNamespace(test.FakeDestNamespace)
449
	app := newFakeApp()
450
	key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name}
451
	data := fakeData{
452
		manifestResponse: &apiclient.ManifestResponse{
453
			Manifests: []string{},
454
			Namespace: test.FakeDestNamespace,
455
			Server:    test.FakeClusterURL,
456
			Revision:  "abc123",
457
		},
458
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
459
			key: pod,
460
		},
461
	}
462
	ctrl := newFakeController(&data, nil)
463
	sources := make([]argoappv1.ApplicationSource, 0)
464
	sources = append(sources, app.Spec.GetSource())
465
	revisions := make([]string, 0)
466
	revisions = append(revisions, "")
467
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
468
	assert.Nil(t, err)
469

470
	assert.NotNil(t, compRes)
471
	assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
472
	assert.Equal(t, 1, len(compRes.resources))
473
	assert.Equal(t, 1, len(compRes.managedResources))
474
	assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks))
475
	assert.Equal(t, 0, len(app.Status.Conditions))
476
}
477

478
// TestAppRevisions tests that revisions are properly propagated for a single source app
479
func TestAppRevisionsSingleSource(t *testing.T) {
480
	obj1 := NewPod()
481
	obj1.SetNamespace(test.FakeDestNamespace)
482
	data := fakeData{
483
		manifestResponse: &apiclient.ManifestResponse{
484
			Manifests: []string{toJSON(t, obj1)},
485
			Namespace: test.FakeDestNamespace,
486
			Server:    test.FakeClusterURL,
487
			Revision:  "abc123",
488
		},
489
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
490
	}
491
	ctrl := newFakeController(&data, nil)
492

493
	app := newFakeApp()
494
	revisions := make([]string, 0)
495
	revisions = append(revisions, "")
496
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources())
497
	assert.Nil(t, err)
498
	assert.NotNil(t, compRes)
499
	assert.NotNil(t, compRes.syncStatus)
500
	assert.NotEmpty(t, compRes.syncStatus.Revision)
501
	assert.Len(t, compRes.syncStatus.Revisions, 0)
502
}
503

504
// TestAppRevisions tests that revisions are properly propagated for a multi source app
505
func TestAppRevisionsMultiSource(t *testing.T) {
506
	obj1 := NewPod()
507
	obj1.SetNamespace(test.FakeDestNamespace)
508
	data := fakeData{
509
		manifestResponses: []*apiclient.ManifestResponse{
510
			{
511
				Manifests: []string{toJSON(t, obj1)},
512
				Namespace: test.FakeDestNamespace,
513
				Server:    test.FakeClusterURL,
514
				Revision:  "abc123",
515
			},
516
			{
517
				Manifests: []string{toJSON(t, obj1)},
518
				Namespace: test.FakeDestNamespace,
519
				Server:    test.FakeClusterURL,
520
				Revision:  "def456",
521
			},
522
			{
523
				Manifests: []string{},
524
				Namespace: test.FakeDestNamespace,
525
				Server:    test.FakeClusterURL,
526
				Revision:  "ghi789",
527
			},
528
		},
529
		managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
530
	}
531
	ctrl := newFakeController(&data, nil)
532

533
	app := newFakeMultiSourceApp()
534
	revisions := make([]string, 0)
535
	revisions = append(revisions, "")
536
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, app.Spec.GetSources(), false, false, nil, app.Spec.HasMultipleSources())
537
	assert.Nil(t, err)
538
	assert.NotNil(t, compRes)
539
	assert.NotNil(t, compRes.syncStatus)
540
	assert.Empty(t, compRes.syncStatus.Revision)
541
	assert.Len(t, compRes.syncStatus.Revisions, 3)
542
	assert.Equal(t, "abc123", compRes.syncStatus.Revisions[0])
543
	assert.Equal(t, "def456", compRes.syncStatus.Revisions[1])
544
	assert.Equal(t, "ghi789", compRes.syncStatus.Revisions[2])
545
}
546

547
func toJSON(t *testing.T, obj *unstructured.Unstructured) string {
548
	data, err := json.Marshal(obj)
549
	assert.NoError(t, err)
550
	return string(data)
551
}
552

553
func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
554
	obj1 := NewPod()
555
	obj1.SetNamespace(test.FakeDestNamespace)
556
	obj2 := NewPod()
557
	obj3 := NewPod()
558
	obj3.SetNamespace("kube-system")
559
	obj4 := NewPod()
560
	obj4.SetGenerateName("my-pod")
561
	obj4.SetName("")
562
	obj5 := NewPod()
563
	obj5.SetName("")
564
	obj5.SetGenerateName("my-pod")
565

566
	app := newFakeApp()
567
	data := fakeData{
568
		manifestResponse: &apiclient.ManifestResponse{
569
			Manifests: []string{toJSON(t, obj1), toJSON(t, obj2), toJSON(t, obj3), toJSON(t, obj4), toJSON(t, obj5)},
570
			Namespace: test.FakeDestNamespace,
571
			Server:    test.FakeClusterURL,
572
			Revision:  "abc123",
573
		},
574
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
575
			kube.GetResourceKey(obj1): obj1,
576
			kube.GetResourceKey(obj3): obj3,
577
		},
578
	}
579
	ctrl := newFakeController(&data, nil)
580
	sources := make([]argoappv1.ApplicationSource, 0)
581
	sources = append(sources, app.Spec.GetSource())
582
	revisions := make([]string, 0)
583
	revisions = append(revisions, "")
584
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
585
	assert.Nil(t, err)
586

587
	assert.NotNil(t, compRes)
588
	assert.Equal(t, 1, len(app.Status.Conditions))
589
	assert.NotNil(t, app.Status.Conditions[0].LastTransitionTime)
590
	assert.Equal(t, argoappv1.ApplicationConditionRepeatedResourceWarning, app.Status.Conditions[0].Type)
591
	assert.Equal(t, "Resource /Pod/fake-dest-ns/my-pod appeared 2 times among application resources.", app.Status.Conditions[0].Message)
592
	assert.Equal(t, 4, len(compRes.resources))
593
}
594

595
func TestCompareAppStateManagedNamespaceMetadataWithLiveNsDoesNotGetPruned(t *testing.T) {
596
	app := newFakeApp()
597
	app.Spec.SyncPolicy = &argoappv1.SyncPolicy{
598
		ManagedNamespaceMetadata: &argoappv1.ManagedNamespaceMetadata{
599
			Labels:      nil,
600
			Annotations: nil,
601
		},
602
	}
603

604
	ns := NewNamespace()
605
	ns.SetName(test.FakeDestNamespace)
606
	ns.SetNamespace(test.FakeDestNamespace)
607
	ns.SetAnnotations(map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true"})
608

609
	data := fakeData{
610
		manifestResponse: &apiclient.ManifestResponse{
611
			Manifests: []string{},
612
			Namespace: test.FakeDestNamespace,
613
			Server:    test.FakeClusterURL,
614
			Revision:  "abc123",
615
		},
616
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
617
			kube.GetResourceKey(ns): ns,
618
		},
619
	}
620
	ctrl := newFakeController(&data, nil)
621
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, []string{}, app.Spec.Sources, false, false, nil, false)
622
	assert.Nil(t, err)
623

624
	assert.NotNil(t, compRes)
625
	assert.Equal(t, 0, len(app.Status.Conditions))
626
	assert.NotNil(t, compRes)
627
	assert.NotNil(t, compRes.syncStatus)
628
	// Ensure that ns does not get pruned
629
	assert.NotNil(t, compRes.reconciliationResult.Target[0])
630
	assert.Equal(t, compRes.reconciliationResult.Target[0].GetName(), ns.GetName())
631
	assert.Equal(t, compRes.reconciliationResult.Target[0].GetAnnotations(), ns.GetAnnotations())
632
	assert.Equal(t, compRes.reconciliationResult.Target[0].GetLabels(), ns.GetLabels())
633
	assert.Len(t, compRes.resources, 1)
634
	assert.Len(t, compRes.managedResources, 1)
635
}
636

637
var defaultProj = argoappv1.AppProject{
638
	ObjectMeta: metav1.ObjectMeta{
639
		Name:      "default",
640
		Namespace: test.FakeArgoCDNamespace,
641
	},
642
	Spec: argoappv1.AppProjectSpec{
643
		SourceRepos: []string{"*"},
644
		Destinations: []argoappv1.ApplicationDestination{
645
			{
646
				Server:    "*",
647
				Namespace: "*",
648
			},
649
		},
650
	},
651
}
652

653
func TestSetHealth(t *testing.T) {
654
	app := newFakeApp()
655
	deployment := kube.MustToUnstructured(&v1.Deployment{
656
		TypeMeta: metav1.TypeMeta{
657
			APIVersion: "apps/v1",
658
			Kind:       "Deployment",
659
		},
660
		ObjectMeta: metav1.ObjectMeta{
661
			Name:      "demo",
662
			Namespace: "default",
663
		},
664
	})
665
	ctrl := newFakeController(&fakeData{
666
		apps: []runtime.Object{app, &defaultProj},
667
		manifestResponse: &apiclient.ManifestResponse{
668
			Manifests: []string{},
669
			Namespace: test.FakeDestNamespace,
670
			Server:    test.FakeClusterURL,
671
			Revision:  "abc123",
672
		},
673
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
674
			kube.GetResourceKey(deployment): deployment,
675
		},
676
	}, nil)
677

678
	sources := make([]argoappv1.ApplicationSource, 0)
679
	sources = append(sources, app.Spec.GetSource())
680
	revisions := make([]string, 0)
681
	revisions = append(revisions, "")
682
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
683
	assert.Nil(t, err)
684

685
	assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus.Status)
686
}
687

688
func TestSetHealthSelfReferencedApp(t *testing.T) {
689
	app := newFakeApp()
690
	unstructuredApp := kube.MustToUnstructured(app)
691
	deployment := kube.MustToUnstructured(&v1.Deployment{
692
		TypeMeta: metav1.TypeMeta{
693
			APIVersion: "apps/v1",
694
			Kind:       "Deployment",
695
		},
696
		ObjectMeta: metav1.ObjectMeta{
697
			Name:      "demo",
698
			Namespace: "default",
699
		},
700
	})
701
	ctrl := newFakeController(&fakeData{
702
		apps: []runtime.Object{app, &defaultProj},
703
		manifestResponse: &apiclient.ManifestResponse{
704
			Manifests: []string{},
705
			Namespace: test.FakeDestNamespace,
706
			Server:    test.FakeClusterURL,
707
			Revision:  "abc123",
708
		},
709
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
710
			kube.GetResourceKey(deployment):      deployment,
711
			kube.GetResourceKey(unstructuredApp): unstructuredApp,
712
		},
713
	}, nil)
714

715
	sources := make([]argoappv1.ApplicationSource, 0)
716
	sources = append(sources, app.Spec.GetSource())
717
	revisions := make([]string, 0)
718
	revisions = append(revisions, "")
719
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
720
	assert.Nil(t, err)
721

722
	assert.Equal(t, health.HealthStatusHealthy, compRes.healthStatus.Status)
723
}
724

725
func TestSetManagedResourcesWithOrphanedResources(t *testing.T) {
726
	proj := defaultProj.DeepCopy()
727
	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
728

729
	app := newFakeApp()
730
	ctrl := newFakeController(&fakeData{
731
		apps: []runtime.Object{app, proj},
732
		namespacedResources: map[kube.ResourceKey]namespacedResource{
733
			kube.NewResourceKey("apps", kube.DeploymentKind, app.Namespace, "guestbook"): {
734
				ResourceNode: argoappv1.ResourceNode{
735
					ResourceRef: argoappv1.ResourceRef{Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app.Namespace},
736
				},
737
				AppName: "",
738
			},
739
		},
740
	}, nil)
741

742
	tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)})
743

744
	assert.NoError(t, err)
745
	assert.Equal(t, len(tree.OrphanedNodes), 1)
746
	assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name)
747
	assert.Equal(t, app.Namespace, tree.OrphanedNodes[0].Namespace)
748
}
749

750
func TestSetManagedResourcesWithResourcesOfAnotherApp(t *testing.T) {
751
	proj := defaultProj.DeepCopy()
752
	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
753

754
	app1 := newFakeApp()
755
	app1.Name = "app1"
756
	app2 := newFakeApp()
757
	app2.Name = "app2"
758

759
	ctrl := newFakeController(&fakeData{
760
		apps: []runtime.Object{app1, app2, proj},
761
		namespacedResources: map[kube.ResourceKey]namespacedResource{
762
			kube.NewResourceKey("apps", kube.DeploymentKind, app2.Namespace, "guestbook"): {
763
				ResourceNode: argoappv1.ResourceNode{
764
					ResourceRef: argoappv1.ResourceRef{Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app2.Namespace},
765
				},
766
				AppName: "app2",
767
			},
768
		},
769
	}, nil)
770

771
	tree, err := ctrl.setAppManagedResources(app1, &comparisonResult{managedResources: make([]managedResource, 0)})
772

773
	assert.NoError(t, err)
774
	assert.Equal(t, 0, len(tree.OrphanedNodes))
775
}
776

777
func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
778
	proj := defaultProj.DeepCopy()
779
	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
780

781
	app := newFakeApp()
782

783
	ctrl := newFakeController(&fakeData{
784
		apps: []runtime.Object{app, proj},
785
		configMapData: map[string]string{
786
			"resource.customizations": "invalid setting",
787
		},
788
	}, nil)
789

790
	sources := make([]argoappv1.ApplicationSource, 0)
791
	sources = append(sources, app.Spec.GetSource())
792
	revisions := make([]string, 0)
793
	revisions = append(revisions, "")
794
	compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
795
	assert.Nil(t, err)
796

797
	assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status)
798
	assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
799
}
800

801
func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) {
802
	proj := defaultProj.DeepCopy()
803
	proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
804
	proj.Spec.SourceNamespaces = []string{"default"}
805

806
	app := newFakeApp()
807
	app.Namespace = "default"
808

809
	ctrl := newFakeController(&fakeData{
810
		apps: []runtime.Object{app, proj},
811
		namespacedResources: map[kube.ResourceKey]namespacedResource{
812
			kube.NewResourceKey("apps", kube.DeploymentKind, app.Namespace, "guestbook"): {
813
				ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Group: "apps", Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app.Namespace}},
814
			},
815
			kube.NewResourceKey("", kube.ServiceAccountKind, app.Namespace, "default"): {
816
				ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "default", Namespace: app.Namespace}},
817
			},
818
			kube.NewResourceKey("", kube.ServiceKind, app.Namespace, "kubernetes"): {
819
				ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "kubernetes", Namespace: app.Namespace}},
820
			},
821
		},
822
	}, nil)
823

824
	tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)})
825

826
	assert.NoError(t, err)
827
	assert.Len(t, tree.OrphanedNodes, 1)
828
	assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name)
829
}
830

831
func Test_appStateManager_persistRevisionHistory(t *testing.T) {
832
	app := newFakeApp()
833
	ctrl := newFakeController(&fakeData{
834
		apps: []runtime.Object{app},
835
	}, nil)
836
	manager := ctrl.appStateManager.(*appStateManager)
837
	setRevisionHistoryLimit := func(value int) {
838
		i := int64(value)
839
		app.Spec.RevisionHistoryLimit = &i
840
	}
841
	addHistory := func() {
842
		err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}, v1alpha1.OperationInitiator{})
843
		assert.NoError(t, err)
844
	}
845
	addHistory()
846
	assert.Len(t, app.Status.History, 1)
847
	addHistory()
848
	assert.Len(t, app.Status.History, 2)
849
	addHistory()
850
	assert.Len(t, app.Status.History, 3)
851
	addHistory()
852
	assert.Len(t, app.Status.History, 4)
853
	addHistory()
854
	assert.Len(t, app.Status.History, 5)
855
	addHistory()
856
	assert.Len(t, app.Status.History, 6)
857
	addHistory()
858
	assert.Len(t, app.Status.History, 7)
859
	addHistory()
860
	assert.Len(t, app.Status.History, 8)
861
	addHistory()
862
	assert.Len(t, app.Status.History, 9)
863
	addHistory()
864
	assert.Len(t, app.Status.History, 10)
865
	// default limit is 10
866
	addHistory()
867
	assert.Len(t, app.Status.History, 10)
868
	// increase limit
869
	setRevisionHistoryLimit(11)
870
	addHistory()
871
	assert.Len(t, app.Status.History, 11)
872
	// decrease limit
873
	setRevisionHistoryLimit(9)
874
	addHistory()
875
	assert.Len(t, app.Status.History, 9)
876

877
	metav1NowTime := metav1.NewTime(time.Now())
878
	err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime, v1alpha1.OperationInitiator{})
879
	assert.NoError(t, err)
880
	assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime)
881
}
882

883
// helper function to read contents of a file to string
884
// panics on error
885
func mustReadFile(path string) string {
886
	b, err := os.ReadFile(path)
887
	if err != nil {
888
		panic(err.Error())
889
	}
890
	return string(b)
891
}
892

893
var signedProj = argoappv1.AppProject{
894
	ObjectMeta: metav1.ObjectMeta{
895
		Name:      "default",
896
		Namespace: test.FakeArgoCDNamespace,
897
	},
898
	Spec: argoappv1.AppProjectSpec{
899
		SourceRepos: []string{"*"},
900
		Destinations: []argoappv1.ApplicationDestination{
901
			{
902
				Server:    "*",
903
				Namespace: "*",
904
			},
905
		},
906
		SignatureKeys: []argoappv1.SignatureKey{
907
			{
908
				KeyID: "4AEE18F83AFDEB23",
909
			},
910
		},
911
	},
912
}
913

914
func TestSignedResponseNoSignatureRequired(t *testing.T) {
915
	t.Setenv("ARGOCD_GPG_ENABLED", "true")
916

917
	// We have a good signature response, but project does not require signed commits
918
	{
919
		app := newFakeApp()
920
		data := fakeData{
921
			manifestResponse: &apiclient.ManifestResponse{
922
				Manifests:    []string{},
923
				Namespace:    test.FakeDestNamespace,
924
				Server:       test.FakeClusterURL,
925
				Revision:     "abc123",
926
				VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"),
927
			},
928
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
929
		}
930
		ctrl := newFakeController(&data, nil)
931
		sources := make([]argoappv1.ApplicationSource, 0)
932
		sources = append(sources, app.Spec.GetSource())
933
		revisions := make([]string, 0)
934
		revisions = append(revisions, "")
935
		compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
936
		assert.Nil(t, err)
937
		assert.NotNil(t, compRes)
938
		assert.NotNil(t, compRes.syncStatus)
939
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
940
		assert.Len(t, compRes.resources, 0)
941
		assert.Len(t, compRes.managedResources, 0)
942
		assert.Len(t, app.Status.Conditions, 0)
943
	}
944
	// We have a bad signature response, but project does not require signed commits
945
	{
946
		app := newFakeApp()
947
		data := fakeData{
948
			manifestResponse: &apiclient.ManifestResponse{
949
				Manifests:    []string{},
950
				Namespace:    test.FakeDestNamespace,
951
				Server:       test.FakeClusterURL,
952
				Revision:     "abc123",
953
				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"),
954
			},
955
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
956
		}
957
		ctrl := newFakeController(&data, nil)
958
		sources := make([]argoappv1.ApplicationSource, 0)
959
		sources = append(sources, app.Spec.GetSource())
960
		revisions := make([]string, 0)
961
		revisions = append(revisions, "")
962
		compRes, err := ctrl.appStateManager.CompareAppState(app, &defaultProj, revisions, sources, false, false, nil, false)
963
		assert.Nil(t, err)
964
		assert.NotNil(t, compRes)
965
		assert.NotNil(t, compRes.syncStatus)
966
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
967
		assert.Len(t, compRes.resources, 0)
968
		assert.Len(t, compRes.managedResources, 0)
969
		assert.Len(t, app.Status.Conditions, 0)
970
	}
971
}
972

973
func TestSignedResponseSignatureRequired(t *testing.T) {
974
	t.Setenv("ARGOCD_GPG_ENABLED", "true")
975

976
	// We have a good signature response, valid key, and signing is required - sync!
977
	{
978
		app := newFakeApp()
979
		data := fakeData{
980
			manifestResponse: &apiclient.ManifestResponse{
981
				Manifests:    []string{},
982
				Namespace:    test.FakeDestNamespace,
983
				Server:       test.FakeClusterURL,
984
				Revision:     "abc123",
985
				VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"),
986
			},
987
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
988
		}
989
		ctrl := newFakeController(&data, nil)
990
		sources := make([]argoappv1.ApplicationSource, 0)
991
		sources = append(sources, app.Spec.GetSource())
992
		revisions := make([]string, 0)
993
		revisions = append(revisions, "")
994
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
995
		assert.Nil(t, err)
996
		assert.NotNil(t, compRes)
997
		assert.NotNil(t, compRes.syncStatus)
998
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
999
		assert.Len(t, compRes.resources, 0)
1000
		assert.Len(t, compRes.managedResources, 0)
1001
		assert.Len(t, app.Status.Conditions, 0)
1002
	}
1003
	// We have a bad signature response and signing is required - do not sync
1004
	{
1005
		app := newFakeApp()
1006
		data := fakeData{
1007
			manifestResponse: &apiclient.ManifestResponse{
1008
				Manifests:    []string{},
1009
				Namespace:    test.FakeDestNamespace,
1010
				Server:       test.FakeClusterURL,
1011
				Revision:     "abc123",
1012
				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"),
1013
			},
1014
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1015
		}
1016
		ctrl := newFakeController(&data, nil)
1017
		sources := make([]argoappv1.ApplicationSource, 0)
1018
		sources = append(sources, app.Spec.GetSource())
1019
		revisions := make([]string, 0)
1020
		revisions = append(revisions, "abc123")
1021
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
1022
		assert.Nil(t, err)
1023
		assert.NotNil(t, compRes)
1024
		assert.NotNil(t, compRes.syncStatus)
1025
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
1026
		assert.Len(t, compRes.resources, 0)
1027
		assert.Len(t, compRes.managedResources, 0)
1028
		assert.Len(t, app.Status.Conditions, 1)
1029
	}
1030
	// We have a malformed signature response and signing is required - do not sync
1031
	{
1032
		app := newFakeApp()
1033
		data := fakeData{
1034
			manifestResponse: &apiclient.ManifestResponse{
1035
				Manifests:    []string{},
1036
				Namespace:    test.FakeDestNamespace,
1037
				Server:       test.FakeClusterURL,
1038
				Revision:     "abc123",
1039
				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_malformed1.txt"),
1040
			},
1041
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1042
		}
1043
		ctrl := newFakeController(&data, nil)
1044
		sources := make([]argoappv1.ApplicationSource, 0)
1045
		sources = append(sources, app.Spec.GetSource())
1046
		revisions := make([]string, 0)
1047
		revisions = append(revisions, "abc123")
1048
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
1049
		assert.Nil(t, err)
1050
		assert.NotNil(t, compRes)
1051
		assert.NotNil(t, compRes.syncStatus)
1052
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
1053
		assert.Len(t, compRes.resources, 0)
1054
		assert.Len(t, compRes.managedResources, 0)
1055
		assert.Len(t, app.Status.Conditions, 1)
1056
	}
1057
	// We have no signature response (no signature made) and signing is required - do not sync
1058
	{
1059
		app := newFakeApp()
1060
		data := fakeData{
1061
			manifestResponse: &apiclient.ManifestResponse{
1062
				Manifests:    []string{},
1063
				Namespace:    test.FakeDestNamespace,
1064
				Server:       test.FakeClusterURL,
1065
				Revision:     "abc123",
1066
				VerifyResult: "",
1067
			},
1068
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1069
		}
1070
		ctrl := newFakeController(&data, nil)
1071
		sources := make([]argoappv1.ApplicationSource, 0)
1072
		sources = append(sources, app.Spec.GetSource())
1073
		revisions := make([]string, 0)
1074
		revisions = append(revisions, "abc123")
1075
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
1076
		assert.Nil(t, err)
1077
		assert.NotNil(t, compRes)
1078
		assert.NotNil(t, compRes.syncStatus)
1079
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
1080
		assert.Len(t, compRes.resources, 0)
1081
		assert.Len(t, compRes.managedResources, 0)
1082
		assert.Len(t, app.Status.Conditions, 1)
1083
	}
1084

1085
	// We have a good signature and signing is required, but key is not allowed - do not sync
1086
	{
1087
		app := newFakeApp()
1088
		data := fakeData{
1089
			manifestResponse: &apiclient.ManifestResponse{
1090
				Manifests:    []string{},
1091
				Namespace:    test.FakeDestNamespace,
1092
				Server:       test.FakeClusterURL,
1093
				Revision:     "abc123",
1094
				VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"),
1095
			},
1096
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1097
		}
1098
		ctrl := newFakeController(&data, nil)
1099
		testProj := signedProj
1100
		testProj.Spec.SignatureKeys[0].KeyID = "4AEE18F83AFDEB24"
1101
		sources := make([]argoappv1.ApplicationSource, 0)
1102
		sources = append(sources, app.Spec.GetSource())
1103
		revisions := make([]string, 0)
1104
		revisions = append(revisions, "abc123")
1105
		compRes, err := ctrl.appStateManager.CompareAppState(app, &testProj, revisions, sources, false, false, nil, false)
1106
		assert.Nil(t, err)
1107
		assert.NotNil(t, compRes)
1108
		assert.NotNil(t, compRes.syncStatus)
1109
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
1110
		assert.Len(t, compRes.resources, 0)
1111
		assert.Len(t, compRes.managedResources, 0)
1112
		assert.Len(t, app.Status.Conditions, 1)
1113
		assert.Contains(t, app.Status.Conditions[0].Message, "key is not allowed")
1114
	}
1115
	// Signature required and local manifests supplied - do not sync
1116
	{
1117
		app := newFakeApp()
1118
		data := fakeData{
1119
			manifestResponse: &apiclient.ManifestResponse{
1120
				Manifests:    []string{},
1121
				Namespace:    test.FakeDestNamespace,
1122
				Server:       test.FakeClusterURL,
1123
				Revision:     "abc123",
1124
				VerifyResult: "",
1125
			},
1126
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1127
		}
1128
		// it doesn't matter for our test whether local manifests are valid
1129
		localManifests := []string{"foobar"}
1130
		ctrl := newFakeController(&data, nil)
1131
		sources := make([]argoappv1.ApplicationSource, 0)
1132
		sources = append(sources, app.Spec.GetSource())
1133
		revisions := make([]string, 0)
1134
		revisions = append(revisions, "abc123")
1135
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false)
1136
		assert.Nil(t, err)
1137
		assert.NotNil(t, compRes)
1138
		assert.NotNil(t, compRes.syncStatus)
1139
		assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
1140
		assert.Len(t, compRes.resources, 0)
1141
		assert.Len(t, compRes.managedResources, 0)
1142
		assert.Len(t, app.Status.Conditions, 1)
1143
		assert.Contains(t, app.Status.Conditions[0].Message, "Cannot use local manifests")
1144
	}
1145

1146
	t.Setenv("ARGOCD_GPG_ENABLED", "false")
1147
	// We have a bad signature response and signing would be required, but GPG subsystem is disabled - sync
1148
	{
1149
		app := newFakeApp()
1150
		data := fakeData{
1151
			manifestResponse: &apiclient.ManifestResponse{
1152
				Manifests:    []string{},
1153
				Namespace:    test.FakeDestNamespace,
1154
				Server:       test.FakeClusterURL,
1155
				Revision:     "abc123",
1156
				VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"),
1157
			},
1158
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1159
		}
1160
		ctrl := newFakeController(&data, nil)
1161
		sources := make([]argoappv1.ApplicationSource, 0)
1162
		sources = append(sources, app.Spec.GetSource())
1163
		revisions := make([]string, 0)
1164
		revisions = append(revisions, "abc123")
1165
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, nil, false)
1166
		assert.Nil(t, err)
1167
		assert.NotNil(t, compRes)
1168
		assert.NotNil(t, compRes.syncStatus)
1169
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
1170
		assert.Len(t, compRes.resources, 0)
1171
		assert.Len(t, compRes.managedResources, 0)
1172
		assert.Len(t, app.Status.Conditions, 0)
1173
	}
1174

1175
	// Signature required and local manifests supplied and GPG subsystem is disabled - sync
1176
	{
1177
		app := newFakeApp()
1178
		data := fakeData{
1179
			manifestResponse: &apiclient.ManifestResponse{
1180
				Manifests:    []string{},
1181
				Namespace:    test.FakeDestNamespace,
1182
				Server:       test.FakeClusterURL,
1183
				Revision:     "abc123",
1184
				VerifyResult: "",
1185
			},
1186
			managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
1187
		}
1188
		// it doesn't matter for our test whether local manifests are valid
1189
		localManifests := []string{""}
1190
		ctrl := newFakeController(&data, nil)
1191
		sources := make([]argoappv1.ApplicationSource, 0)
1192
		sources = append(sources, app.Spec.GetSource())
1193
		revisions := make([]string, 0)
1194
		revisions = append(revisions, "abc123")
1195
		compRes, err := ctrl.appStateManager.CompareAppState(app, &signedProj, revisions, sources, false, false, localManifests, false)
1196
		assert.Nil(t, err)
1197
		assert.NotNil(t, compRes)
1198
		assert.NotNil(t, compRes.syncStatus)
1199
		assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
1200
		assert.Len(t, compRes.resources, 0)
1201
		assert.Len(t, compRes.managedResources, 0)
1202
		assert.Len(t, app.Status.Conditions, 0)
1203
	}
1204
}
1205

1206
func TestComparisonResult_GetHealthStatus(t *testing.T) {
1207
	status := &argoappv1.HealthStatus{Status: health.HealthStatusMissing}
1208
	res := comparisonResult{
1209
		healthStatus: status,
1210
	}
1211

1212
	assert.Equal(t, status, res.GetHealthStatus())
1213
}
1214

1215
func TestComparisonResult_GetSyncStatus(t *testing.T) {
1216
	status := &argoappv1.SyncStatus{Status: argoappv1.SyncStatusCodeOutOfSync}
1217
	res := comparisonResult{
1218
		syncStatus: status,
1219
	}
1220

1221
	assert.Equal(t, status, res.GetSyncStatus())
1222
}
1223

1224
func TestIsLiveResourceManaged(t *testing.T) {
1225
	managedObj := kube.MustToUnstructured(&corev1.ConfigMap{
1226
		TypeMeta: metav1.TypeMeta{
1227
			APIVersion: "v1",
1228
			Kind:       "ConfigMap",
1229
		},
1230
		ObjectMeta: metav1.ObjectMeta{
1231
			Name:      "configmap1",
1232
			Namespace: "default",
1233
			Annotations: map[string]string{
1234
				common.AnnotationKeyAppInstance: "guestbook:/ConfigMap:default/configmap1",
1235
			},
1236
		},
1237
	})
1238
	managedObjWithLabel := kube.MustToUnstructured(&corev1.ConfigMap{
1239
		TypeMeta: metav1.TypeMeta{
1240
			APIVersion: "v1",
1241
			Kind:       "ConfigMap",
1242
		},
1243
		ObjectMeta: metav1.ObjectMeta{
1244
			Name:      "configmap1",
1245
			Namespace: "default",
1246
			Labels: map[string]string{
1247
				common.LabelKeyAppInstance: "guestbook",
1248
			},
1249
		},
1250
	})
1251
	unmanagedObjWrongName := kube.MustToUnstructured(&corev1.ConfigMap{
1252
		TypeMeta: metav1.TypeMeta{
1253
			APIVersion: "v1",
1254
			Kind:       "ConfigMap",
1255
		},
1256
		ObjectMeta: metav1.ObjectMeta{
1257
			Name:      "configmap2",
1258
			Namespace: "default",
1259
			Annotations: map[string]string{
1260
				common.AnnotationKeyAppInstance: "guestbook:/ConfigMap:default/configmap1",
1261
			},
1262
		},
1263
	})
1264
	unmanagedObjWrongKind := kube.MustToUnstructured(&corev1.ConfigMap{
1265
		TypeMeta: metav1.TypeMeta{
1266
			APIVersion: "v1",
1267
			Kind:       "ConfigMap",
1268
		},
1269
		ObjectMeta: metav1.ObjectMeta{
1270
			Name:      "configmap2",
1271
			Namespace: "default",
1272
			Annotations: map[string]string{
1273
				common.AnnotationKeyAppInstance: "guestbook:/Service:default/configmap2",
1274
			},
1275
		},
1276
	})
1277
	unmanagedObjWrongGroup := kube.MustToUnstructured(&corev1.ConfigMap{
1278
		TypeMeta: metav1.TypeMeta{
1279
			APIVersion: "v1",
1280
			Kind:       "ConfigMap",
1281
		},
1282
		ObjectMeta: metav1.ObjectMeta{
1283
			Name:      "configmap2",
1284
			Namespace: "default",
1285
			Annotations: map[string]string{
1286
				common.AnnotationKeyAppInstance: "guestbook:apps/ConfigMap:default/configmap2",
1287
			},
1288
		},
1289
	})
1290
	unmanagedObjWrongNamespace := kube.MustToUnstructured(&corev1.ConfigMap{
1291
		TypeMeta: metav1.TypeMeta{
1292
			APIVersion: "v1",
1293
			Kind:       "ConfigMap",
1294
		},
1295
		ObjectMeta: metav1.ObjectMeta{
1296
			Name:      "configmap2",
1297
			Namespace: "default",
1298
			Annotations: map[string]string{
1299
				common.AnnotationKeyAppInstance: "guestbook:/ConfigMap:fakens/configmap2",
1300
			},
1301
		},
1302
	})
1303
	managedWrongAPIGroup := kube.MustToUnstructured(&networkingv1.Ingress{
1304
		TypeMeta: metav1.TypeMeta{
1305
			APIVersion: "networking.k8s.io/v1",
1306
			Kind:       "Ingress",
1307
		},
1308
		ObjectMeta: metav1.ObjectMeta{
1309
			Name:      "some-ingress",
1310
			Namespace: "default",
1311
			Annotations: map[string]string{
1312
				common.AnnotationKeyAppInstance: "guestbook:extensions/Ingress:default/some-ingress",
1313
			},
1314
		},
1315
	})
1316
	ctrl := newFakeController(&fakeData{
1317
		apps: []runtime.Object{app, &defaultProj},
1318
		manifestResponse: &apiclient.ManifestResponse{
1319
			Manifests: []string{},
1320
			Namespace: test.FakeDestNamespace,
1321
			Server:    test.FakeClusterURL,
1322
			Revision:  "abc123",
1323
		},
1324
		managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
1325
			kube.GetResourceKey(managedObj):                 managedObj,
1326
			kube.GetResourceKey(unmanagedObjWrongName):      unmanagedObjWrongName,
1327
			kube.GetResourceKey(unmanagedObjWrongKind):      unmanagedObjWrongKind,
1328
			kube.GetResourceKey(unmanagedObjWrongGroup):     unmanagedObjWrongGroup,
1329
			kube.GetResourceKey(unmanagedObjWrongNamespace): unmanagedObjWrongNamespace,
1330
		},
1331
	}, nil)
1332

1333
	manager := ctrl.appStateManager.(*appStateManager)
1334
	appName := "guestbook"
1335

1336
	t.Run("will return true if trackingid matches the resource", func(t *testing.T) {
1337
		// given
1338
		t.Parallel()
1339
		configObj := managedObj.DeepCopy()
1340

1341
		// then
1342
		assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
1343
		assert.True(t, manager.isSelfReferencedObj(managedObj, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
1344
	})
1345
	t.Run("will return true if tracked with label", func(t *testing.T) {
1346
		// given
1347
		t.Parallel()
1348
		configObj := managedObjWithLabel.DeepCopy()
1349

1350
		// then
1351
		assert.True(t, manager.isSelfReferencedObj(managedObjWithLabel, configObj, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
1352
	})
1353
	t.Run("will handle if trackingId has wrong resource name and config is nil", func(t *testing.T) {
1354
		// given
1355
		t.Parallel()
1356

1357
		// then
1358
		assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
1359
		assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongName, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
1360
	})
1361
	t.Run("will handle if trackingId has wrong resource group and config is nil", func(t *testing.T) {
1362
		// given
1363
		t.Parallel()
1364

1365
		// then
1366
		assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
1367
		assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
1368
	})
1369
	t.Run("will handle if trackingId has wrong kind and config is nil", func(t *testing.T) {
1370
		// given
1371
		t.Parallel()
1372

1373
		// then
1374
		assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
1375
		assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
1376
	})
1377
	t.Run("will handle if trackingId has wrong namespace and config is nil", func(t *testing.T) {
1378
		// given
1379
		t.Parallel()
1380

1381
		// then
1382
		assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
1383
		assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotationAndLabel))
1384
	})
1385
	t.Run("will return true if live is nil", func(t *testing.T) {
1386
		t.Parallel()
1387
		assert.True(t, manager.isSelfReferencedObj(nil, nil, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
1388
	})
1389

1390
	t.Run("will handle upgrade in desired state APIGroup", func(t *testing.T) {
1391
		// given
1392
		t.Parallel()
1393
		config := managedWrongAPIGroup.DeepCopy()
1394
		delete(config.GetAnnotations(), common.AnnotationKeyAppInstance)
1395

1396
		// then
1397
		assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
1398
	})
1399
}
1400

1401
func TestUseDiffCache(t *testing.T) {
1402
	type fixture struct {
1403
		testName             string
1404
		noCache              bool
1405
		manifestInfos        []*apiclient.ManifestResponse
1406
		sources              []argoappv1.ApplicationSource
1407
		app                  *argoappv1.Application
1408
		manifestRevisions    []string
1409
		statusRefreshTimeout time.Duration
1410
		expectedUseCache     bool
1411
		serverSideDiff       bool
1412
	}
1413

1414
	manifestInfos := func(revision string) []*apiclient.ManifestResponse {
1415
		return []*apiclient.ManifestResponse{
1416
			{
1417
				Manifests: []string{
1418
					"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-svc\",\"namespace\":\"httpbin\"},\"spec\":{\"ports\":[{\"name\":\"http-port\",\"port\":7777,\"targetPort\":80},{\"name\":\"test\",\"port\":333}],\"selector\":{\"app\":\"httpbin\"}}}",
1419
					"{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-deployment\",\"namespace\":\"httpbin\"},\"spec\":{\"replicas\":2,\"selector\":{\"matchLabels\":{\"app\":\"httpbin\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"httpbin\"}},\"spec\":{\"containers\":[{\"image\":\"kennethreitz/httpbin\",\"imagePullPolicy\":\"Always\",\"name\":\"httpbin\",\"ports\":[{\"containerPort\":80}]}]}}}}",
1420
				},
1421
				Namespace:    "",
1422
				Server:       "",
1423
				Revision:     revision,
1424
				SourceType:   "Kustomize",
1425
				VerifyResult: "",
1426
			},
1427
		}
1428
	}
1429
	sources := func() []argoappv1.ApplicationSource {
1430
		return []argoappv1.ApplicationSource{
1431
			{
1432
				RepoURL:        "https://some-repo.com",
1433
				Path:           "argocd/httpbin",
1434
				TargetRevision: "HEAD",
1435
			},
1436
		}
1437
	}
1438

1439
	app := func(namespace string, revision string, refresh bool, a *argoappv1.Application) *argoappv1.Application {
1440
		app := &argoappv1.Application{
1441
			ObjectMeta: metav1.ObjectMeta{
1442
				Name:      "httpbin",
1443
				Namespace: namespace,
1444
			},
1445
			Spec: argoappv1.ApplicationSpec{
1446
				Source: &argoappv1.ApplicationSource{
1447
					RepoURL:        "https://some-repo.com",
1448
					Path:           "argocd/httpbin",
1449
					TargetRevision: "HEAD",
1450
				},
1451
				Destination: argoappv1.ApplicationDestination{
1452
					Server:    "https://kubernetes.default.svc",
1453
					Namespace: "httpbin",
1454
				},
1455
				Project: "default",
1456
				SyncPolicy: &argoappv1.SyncPolicy{
1457
					SyncOptions: []string{
1458
						"CreateNamespace=true",
1459
						"ServerSideApply=true",
1460
					},
1461
				},
1462
			},
1463
			Status: argoappv1.ApplicationStatus{
1464
				Resources: []argoappv1.ResourceStatus{},
1465
				Sync: argoappv1.SyncStatus{
1466
					Status: argoappv1.SyncStatusCodeSynced,
1467
					ComparedTo: argoappv1.ComparedTo{
1468
						Source: argoappv1.ApplicationSource{
1469
							RepoURL:        "https://some-repo.com",
1470
							Path:           "argocd/httpbin",
1471
							TargetRevision: "HEAD",
1472
						},
1473
						Destination: argoappv1.ApplicationDestination{
1474
							Server:    "https://kubernetes.default.svc",
1475
							Namespace: "httpbin",
1476
						},
1477
					},
1478
					Revision:  revision,
1479
					Revisions: []string{},
1480
				},
1481
				ReconciledAt: &metav1.Time{
1482
					Time: time.Now().Add(-time.Hour),
1483
				},
1484
			},
1485
		}
1486
		if refresh {
1487
			annotations := make(map[string]string)
1488
			annotations[argoappv1.AnnotationKeyRefresh] = string(argoappv1.RefreshTypeNormal)
1489
			app.SetAnnotations(annotations)
1490
		}
1491
		if a != nil {
1492
			err := mergo.Merge(app, a, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue)
1493
			if err != nil {
1494
				t.Fatalf("error merging app: %s", err)
1495
			}
1496
		}
1497
		return app
1498
	}
1499

1500
	cases := []fixture{
1501
		{
1502
			testName:             "will use diff cache",
1503
			noCache:              false,
1504
			manifestInfos:        manifestInfos("rev1"),
1505
			sources:              sources(),
1506
			app:                  app("httpbin", "rev1", false, nil),
1507
			manifestRevisions:    []string{"rev1"},
1508
			statusRefreshTimeout: time.Hour * 24,
1509
			expectedUseCache:     true,
1510
			serverSideDiff:       false,
1511
		},
1512
		{
1513
			testName:      "will use diff cache for multisource",
1514
			noCache:       false,
1515
			manifestInfos: manifestInfos("rev1"),
1516
			sources:       sources(),
1517
			app: app("httpbin", "", false, &argoappv1.Application{
1518
				Spec: argoappv1.ApplicationSpec{
1519
					Source: nil,
1520
					Sources: argoappv1.ApplicationSources{
1521
						{
1522
							RepoURL: "multisource repo1",
1523
						},
1524
						{
1525
							RepoURL: "multisource repo2",
1526
						},
1527
					},
1528
				},
1529
				Status: argoappv1.ApplicationStatus{
1530
					Resources: []argoappv1.ResourceStatus{},
1531
					Sync: argoappv1.SyncStatus{
1532
						Status: argoappv1.SyncStatusCodeSynced,
1533
						ComparedTo: argoappv1.ComparedTo{
1534
							Source: argoappv1.ApplicationSource{},
1535
							Sources: argoappv1.ApplicationSources{
1536
								{
1537
									RepoURL: "multisource repo1",
1538
								},
1539
								{
1540
									RepoURL: "multisource repo2",
1541
								},
1542
							},
1543
						},
1544
						Revisions: []string{"rev1", "rev2"},
1545
					},
1546
					ReconciledAt: &metav1.Time{
1547
						Time: time.Now().Add(-time.Hour),
1548
					},
1549
				},
1550
			}),
1551
			manifestRevisions:    []string{"rev1", "rev2"},
1552
			statusRefreshTimeout: time.Hour * 24,
1553
			expectedUseCache:     true,
1554
			serverSideDiff:       false,
1555
		},
1556
		{
1557
			testName:             "will return false if nocache is true",
1558
			noCache:              true,
1559
			manifestInfos:        manifestInfos("rev1"),
1560
			sources:              sources(),
1561
			app:                  app("httpbin", "rev1", false, nil),
1562
			manifestRevisions:    []string{"rev1"},
1563
			statusRefreshTimeout: time.Hour * 24,
1564
			expectedUseCache:     false,
1565
			serverSideDiff:       false,
1566
		},
1567
		{
1568
			testName:             "will return false if requested refresh",
1569
			noCache:              false,
1570
			manifestInfos:        manifestInfos("rev1"),
1571
			sources:              sources(),
1572
			app:                  app("httpbin", "rev1", true, nil),
1573
			manifestRevisions:    []string{"rev1"},
1574
			statusRefreshTimeout: time.Hour * 24,
1575
			expectedUseCache:     false,
1576
			serverSideDiff:       false,
1577
		},
1578
		{
1579
			testName:             "will return false if status expired",
1580
			noCache:              false,
1581
			manifestInfos:        manifestInfos("rev1"),
1582
			sources:              sources(),
1583
			app:                  app("httpbin", "rev1", false, nil),
1584
			manifestRevisions:    []string{"rev1"},
1585
			statusRefreshTimeout: time.Minute,
1586
			expectedUseCache:     false,
1587
			serverSideDiff:       false,
1588
		},
1589
		{
1590
			testName:             "will return true if status expired and server-side diff",
1591
			noCache:              false,
1592
			manifestInfos:        manifestInfos("rev1"),
1593
			sources:              sources(),
1594
			app:                  app("httpbin", "rev1", false, nil),
1595
			manifestRevisions:    []string{"rev1"},
1596
			statusRefreshTimeout: time.Minute,
1597
			expectedUseCache:     true,
1598
			serverSideDiff:       true,
1599
		},
1600
		{
1601
			testName:             "will return false if there is a new revision",
1602
			noCache:              false,
1603
			manifestInfos:        manifestInfos("rev1"),
1604
			sources:              sources(),
1605
			app:                  app("httpbin", "rev1", false, nil),
1606
			manifestRevisions:    []string{"rev2"},
1607
			statusRefreshTimeout: time.Hour * 24,
1608
			expectedUseCache:     false,
1609
			serverSideDiff:       false,
1610
		},
1611
		{
1612
			testName:      "will return false if app spec repo changed",
1613
			noCache:       false,
1614
			manifestInfos: manifestInfos("rev1"),
1615
			sources:       sources(),
1616
			app: app("httpbin", "rev1", false, &argoappv1.Application{
1617
				Spec: argoappv1.ApplicationSpec{
1618
					Source: &argoappv1.ApplicationSource{
1619
						RepoURL: "new-repo",
1620
					},
1621
				},
1622
			}),
1623
			manifestRevisions:    []string{"rev1"},
1624
			statusRefreshTimeout: time.Hour * 24,
1625
			expectedUseCache:     false,
1626
			serverSideDiff:       false,
1627
		},
1628
		{
1629
			testName:      "will return false if app spec IgnoreDifferences changed",
1630
			noCache:       false,
1631
			manifestInfos: manifestInfos("rev1"),
1632
			sources:       sources(),
1633
			app: app("httpbin", "rev1", false, &argoappv1.Application{
1634
				Spec: argoappv1.ApplicationSpec{
1635
					IgnoreDifferences: []argoappv1.ResourceIgnoreDifferences{
1636
						{
1637
							Group:             "app/v1",
1638
							Kind:              "application",
1639
							Name:              "httpbin",
1640
							Namespace:         "httpbin",
1641
							JQPathExpressions: []string{"."},
1642
						},
1643
					},
1644
				},
1645
			}),
1646
			manifestRevisions:    []string{"rev1"},
1647
			statusRefreshTimeout: time.Hour * 24,
1648
			expectedUseCache:     false,
1649
			serverSideDiff:       false,
1650
		},
1651
	}
1652

1653
	for _, tc := range cases {
1654
		tc := tc
1655
		t.Run(tc.testName, func(t *testing.T) {
1656
			// Given
1657
			t.Parallel()
1658
			logger, _ := logrustest.NewNullLogger()
1659
			log := logrus.NewEntry(logger)
1660

1661
			// When
1662
			useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, tc.serverSideDiff, log)
1663

1664
			// Then
1665
			assert.Equal(t, useDiffCache, tc.expectedUseCache)
1666
		})
1667
	}
1668
}
1669

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.