github.com/argoproj/argo-cd@v1.8.7/controller/state_test.go (about) 1 package controller 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 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/stretchr/testify/assert" 15 v1 "k8s.io/api/apps/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 18 "k8s.io/apimachinery/pkg/runtime" 19 20 "github.com/argoproj/argo-cd/common" 21 argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 22 "github.com/argoproj/argo-cd/reposerver/apiclient" 23 "github.com/argoproj/argo-cd/test" 24 ) 25 26 // TestCompareAppStateEmpty tests comparison when both git and live have no objects 27 func TestCompareAppStateEmpty(t *testing.T) { 28 app := newFakeApp() 29 data := fakeData{ 30 manifestResponse: &apiclient.ManifestResponse{ 31 Manifests: []string{}, 32 Namespace: test.FakeDestNamespace, 33 Server: test.FakeClusterURL, 34 Revision: "abc123", 35 }, 36 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 37 } 38 ctrl := newFakeController(&data) 39 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 40 assert.NotNil(t, compRes) 41 assert.NotNil(t, compRes.syncStatus) 42 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 43 assert.Len(t, compRes.resources, 0) 44 assert.Len(t, compRes.managedResources, 0) 45 assert.Len(t, app.Status.Conditions, 0) 46 } 47 48 // TestCompareAppStateMissing tests when there is a manifest defined in the repo which doesn't exist in live 49 func TestCompareAppStateMissing(t *testing.T) { 50 app := newFakeApp() 51 data := fakeData{ 52 apps: []runtime.Object{app}, 53 manifestResponse: &apiclient.ManifestResponse{ 54 Manifests: []string{PodManifest}, 55 Namespace: test.FakeDestNamespace, 56 Server: test.FakeClusterURL, 57 Revision: "abc123", 58 }, 59 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 60 } 61 ctrl := newFakeController(&data) 62 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 63 assert.NotNil(t, compRes) 64 assert.NotNil(t, compRes.syncStatus) 65 assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) 66 assert.Len(t, compRes.resources, 1) 67 assert.Len(t, compRes.managedResources, 1) 68 assert.Len(t, app.Status.Conditions, 0) 69 } 70 71 // TestCompareAppStateExtra tests when there is an extra object in live but not defined in git 72 func TestCompareAppStateExtra(t *testing.T) { 73 pod := NewPod() 74 pod.SetNamespace(test.FakeDestNamespace) 75 app := newFakeApp() 76 key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name} 77 data := fakeData{ 78 manifestResponse: &apiclient.ManifestResponse{ 79 Manifests: []string{}, 80 Namespace: test.FakeDestNamespace, 81 Server: test.FakeClusterURL, 82 Revision: "abc123", 83 }, 84 managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ 85 key: pod, 86 }, 87 } 88 ctrl := newFakeController(&data) 89 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 90 assert.NotNil(t, compRes) 91 assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status) 92 assert.Equal(t, 1, len(compRes.resources)) 93 assert.Equal(t, 1, len(compRes.managedResources)) 94 assert.Equal(t, 0, len(app.Status.Conditions)) 95 } 96 97 // TestCompareAppStateHook checks that hooks are detected during manifest generation, and not 98 // considered as part of resources when assessing Synced status 99 func TestCompareAppStateHook(t *testing.T) { 100 pod := NewPod() 101 pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"}) 102 podBytes, _ := json.Marshal(pod) 103 app := newFakeApp() 104 data := fakeData{ 105 apps: []runtime.Object{app}, 106 manifestResponse: &apiclient.ManifestResponse{ 107 Manifests: []string{string(podBytes)}, 108 Namespace: test.FakeDestNamespace, 109 Server: test.FakeClusterURL, 110 Revision: "abc123", 111 }, 112 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 113 } 114 ctrl := newFakeController(&data) 115 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 116 assert.NotNil(t, compRes) 117 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 118 assert.Equal(t, 0, len(compRes.resources)) 119 assert.Equal(t, 0, len(compRes.managedResources)) 120 assert.Equal(t, 1, len(compRes.reconciliationResult.Hooks)) 121 assert.Equal(t, 0, len(app.Status.Conditions)) 122 } 123 124 // TestCompareAppStateSkipHook checks that skipped resources are detected during manifest generation, and not 125 // considered as part of resources when assessing Synced status 126 func TestCompareAppStateSkipHook(t *testing.T) { 127 pod := NewPod() 128 pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "Skip"}) 129 podBytes, _ := json.Marshal(pod) 130 app := newFakeApp() 131 data := fakeData{ 132 apps: []runtime.Object{app}, 133 manifestResponse: &apiclient.ManifestResponse{ 134 Manifests: []string{string(podBytes)}, 135 Namespace: test.FakeDestNamespace, 136 Server: test.FakeClusterURL, 137 Revision: "abc123", 138 }, 139 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 140 } 141 ctrl := newFakeController(&data) 142 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 143 assert.NotNil(t, compRes) 144 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 145 assert.Equal(t, 1, len(compRes.resources)) 146 assert.Equal(t, 1, len(compRes.managedResources)) 147 assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks)) 148 assert.Equal(t, 0, len(app.Status.Conditions)) 149 } 150 151 // checks that ignore resources are detected, but excluded from status 152 func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) { 153 pod := NewPod() 154 pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreExtraneous"}) 155 app := newFakeApp() 156 data := fakeData{ 157 apps: []runtime.Object{app}, 158 manifestResponse: &apiclient.ManifestResponse{ 159 Manifests: []string{}, 160 Namespace: test.FakeDestNamespace, 161 Server: test.FakeClusterURL, 162 Revision: "abc123", 163 }, 164 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 165 } 166 ctrl := newFakeController(&data) 167 168 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 169 170 assert.NotNil(t, compRes) 171 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 172 assert.Len(t, compRes.resources, 0) 173 assert.Len(t, compRes.managedResources, 0) 174 assert.Len(t, app.Status.Conditions, 0) 175 } 176 177 // TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git 178 func TestCompareAppStateExtraHook(t *testing.T) { 179 pod := NewPod() 180 pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"}) 181 pod.SetNamespace(test.FakeDestNamespace) 182 app := newFakeApp() 183 key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name} 184 data := fakeData{ 185 manifestResponse: &apiclient.ManifestResponse{ 186 Manifests: []string{}, 187 Namespace: test.FakeDestNamespace, 188 Server: test.FakeClusterURL, 189 Revision: "abc123", 190 }, 191 managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ 192 key: pod, 193 }, 194 } 195 ctrl := newFakeController(&data) 196 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 197 198 assert.NotNil(t, compRes) 199 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 200 assert.Equal(t, 1, len(compRes.resources)) 201 assert.Equal(t, 1, len(compRes.managedResources)) 202 assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks)) 203 assert.Equal(t, 0, len(app.Status.Conditions)) 204 } 205 206 func toJSON(t *testing.T, obj *unstructured.Unstructured) string { 207 data, err := json.Marshal(obj) 208 assert.NoError(t, err) 209 return string(data) 210 } 211 212 func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) { 213 obj1 := NewPod() 214 obj1.SetNamespace(test.FakeDestNamespace) 215 obj2 := NewPod() 216 obj3 := NewPod() 217 obj3.SetNamespace("kube-system") 218 obj4 := NewPod() 219 obj4.SetGenerateName("my-pod") 220 obj4.SetName("") 221 obj5 := NewPod() 222 obj5.SetName("") 223 obj5.SetGenerateName("my-pod") 224 225 app := newFakeApp() 226 data := fakeData{ 227 manifestResponse: &apiclient.ManifestResponse{ 228 Manifests: []string{toJSON(t, obj1), toJSON(t, obj2), toJSON(t, obj3), toJSON(t, obj4), toJSON(t, obj5)}, 229 Namespace: test.FakeDestNamespace, 230 Server: test.FakeClusterURL, 231 Revision: "abc123", 232 }, 233 managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ 234 kube.GetResourceKey(obj1): obj1, 235 kube.GetResourceKey(obj3): obj3, 236 }, 237 } 238 ctrl := newFakeController(&data) 239 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 240 241 assert.NotNil(t, compRes) 242 assert.Equal(t, 1, len(app.Status.Conditions)) 243 assert.NotNil(t, app.Status.Conditions[0].LastTransitionTime) 244 assert.Equal(t, argoappv1.ApplicationConditionRepeatedResourceWarning, app.Status.Conditions[0].Type) 245 assert.Equal(t, "Resource /Pod/fake-dest-ns/my-pod appeared 2 times among application resources.", app.Status.Conditions[0].Message) 246 assert.Equal(t, 4, len(compRes.resources)) 247 } 248 249 var defaultProj = argoappv1.AppProject{ 250 ObjectMeta: metav1.ObjectMeta{ 251 Name: "default", 252 Namespace: test.FakeArgoCDNamespace, 253 }, 254 Spec: argoappv1.AppProjectSpec{ 255 SourceRepos: []string{"*"}, 256 Destinations: []argoappv1.ApplicationDestination{ 257 { 258 Server: "*", 259 Namespace: "*", 260 }, 261 }, 262 }, 263 } 264 265 func TestSetHealth(t *testing.T) { 266 app := newFakeApp() 267 deployment := kube.MustToUnstructured(&v1.Deployment{ 268 TypeMeta: metav1.TypeMeta{ 269 APIVersion: "apps/v1beta1", 270 Kind: "Deployment", 271 }, 272 ObjectMeta: metav1.ObjectMeta{ 273 Name: "demo", 274 Namespace: "default", 275 }, 276 }) 277 ctrl := newFakeController(&fakeData{ 278 apps: []runtime.Object{app, &defaultProj}, 279 manifestResponse: &apiclient.ManifestResponse{ 280 Manifests: []string{}, 281 Namespace: test.FakeDestNamespace, 282 Server: test.FakeClusterURL, 283 Revision: "abc123", 284 }, 285 managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ 286 kube.GetResourceKey(deployment): deployment, 287 }, 288 }) 289 290 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 291 292 assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy) 293 } 294 295 func TestSetHealthSelfReferencedApp(t *testing.T) { 296 app := newFakeApp() 297 unstructuredApp := kube.MustToUnstructured(app) 298 deployment := kube.MustToUnstructured(&v1.Deployment{ 299 TypeMeta: metav1.TypeMeta{ 300 APIVersion: "apps/v1beta1", 301 Kind: "Deployment", 302 }, 303 ObjectMeta: metav1.ObjectMeta{ 304 Name: "demo", 305 Namespace: "default", 306 }, 307 }) 308 ctrl := newFakeController(&fakeData{ 309 apps: []runtime.Object{app, &defaultProj}, 310 manifestResponse: &apiclient.ManifestResponse{ 311 Manifests: []string{}, 312 Namespace: test.FakeDestNamespace, 313 Server: test.FakeClusterURL, 314 Revision: "abc123", 315 }, 316 managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ 317 kube.GetResourceKey(deployment): deployment, 318 kube.GetResourceKey(unstructuredApp): unstructuredApp, 319 }, 320 }) 321 322 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 323 324 assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy) 325 } 326 327 func TestSetManagedResourcesWithOrphanedResources(t *testing.T) { 328 proj := defaultProj.DeepCopy() 329 proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} 330 331 app := newFakeApp() 332 ctrl := newFakeController(&fakeData{ 333 apps: []runtime.Object{app, proj}, 334 namespacedResources: map[kube.ResourceKey]namespacedResource{ 335 kube.NewResourceKey("apps", kube.DeploymentKind, app.Namespace, "guestbook"): { 336 ResourceNode: argoappv1.ResourceNode{ 337 ResourceRef: argoappv1.ResourceRef{Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app.Namespace}, 338 }, 339 AppName: "", 340 }, 341 }, 342 }) 343 344 tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)}) 345 346 assert.NoError(t, err) 347 assert.Equal(t, len(tree.OrphanedNodes), 1) 348 assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name) 349 assert.Equal(t, app.Namespace, tree.OrphanedNodes[0].Namespace) 350 } 351 352 func TestSetManagedResourcesWithResourcesOfAnotherApp(t *testing.T) { 353 proj := defaultProj.DeepCopy() 354 proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} 355 356 app1 := newFakeApp() 357 app1.Name = "app1" 358 app2 := newFakeApp() 359 app2.Name = "app2" 360 361 ctrl := newFakeController(&fakeData{ 362 apps: []runtime.Object{app1, app2, proj}, 363 namespacedResources: map[kube.ResourceKey]namespacedResource{ 364 kube.NewResourceKey("apps", kube.DeploymentKind, app2.Namespace, "guestbook"): { 365 ResourceNode: argoappv1.ResourceNode{ 366 ResourceRef: argoappv1.ResourceRef{Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app2.Namespace}, 367 }, 368 AppName: "app2", 369 }, 370 }, 371 }) 372 373 tree, err := ctrl.setAppManagedResources(app1, &comparisonResult{managedResources: make([]managedResource, 0)}) 374 375 assert.NoError(t, err) 376 assert.Equal(t, len(tree.OrphanedNodes), 0) 377 } 378 379 func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) { 380 proj := defaultProj.DeepCopy() 381 proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} 382 383 app := newFakeApp() 384 385 ctrl := newFakeController(&fakeData{ 386 apps: []runtime.Object{app, proj}, 387 configMapData: map[string]string{ 388 "resource.customizations": "invalid setting", 389 }, 390 }) 391 392 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 393 394 assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status) 395 assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status) 396 } 397 398 func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) { 399 proj := defaultProj.DeepCopy() 400 proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} 401 402 app := newFakeApp() 403 app.Namespace = "default" 404 405 ctrl := newFakeController(&fakeData{ 406 apps: []runtime.Object{app, proj}, 407 namespacedResources: map[kube.ResourceKey]namespacedResource{ 408 kube.NewResourceKey("apps", kube.DeploymentKind, app.Namespace, "guestbook"): { 409 ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Group: "apps", Kind: kube.DeploymentKind, Name: "guestbook", Namespace: app.Namespace}}, 410 }, 411 kube.NewResourceKey("", kube.ServiceAccountKind, app.Namespace, "default"): { 412 ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "default", Namespace: app.Namespace}}, 413 }, 414 kube.NewResourceKey("", kube.ServiceKind, app.Namespace, "kubernetes"): { 415 ResourceNode: argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: kube.ServiceAccountKind, Name: "kubernetes", Namespace: app.Namespace}}, 416 }, 417 }, 418 }) 419 420 tree, err := ctrl.setAppManagedResources(app, &comparisonResult{managedResources: make([]managedResource, 0)}) 421 422 assert.NoError(t, err) 423 assert.Len(t, tree.OrphanedNodes, 1) 424 assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name) 425 } 426 427 func Test_appStateManager_persistRevisionHistory(t *testing.T) { 428 app := newFakeApp() 429 ctrl := newFakeController(&fakeData{ 430 apps: []runtime.Object{app}, 431 }) 432 manager := ctrl.appStateManager.(*appStateManager) 433 setRevisionHistoryLimit := func(value int) { 434 i := int64(value) 435 app.Spec.RevisionHistoryLimit = &i 436 } 437 addHistory := func() { 438 err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, metav1.Time{}) 439 assert.NoError(t, err) 440 } 441 addHistory() 442 assert.Len(t, app.Status.History, 1) 443 addHistory() 444 assert.Len(t, app.Status.History, 2) 445 addHistory() 446 assert.Len(t, app.Status.History, 3) 447 addHistory() 448 assert.Len(t, app.Status.History, 4) 449 addHistory() 450 assert.Len(t, app.Status.History, 5) 451 addHistory() 452 assert.Len(t, app.Status.History, 6) 453 addHistory() 454 assert.Len(t, app.Status.History, 7) 455 addHistory() 456 assert.Len(t, app.Status.History, 8) 457 addHistory() 458 assert.Len(t, app.Status.History, 9) 459 addHistory() 460 assert.Len(t, app.Status.History, 10) 461 // default limit is 10 462 addHistory() 463 assert.Len(t, app.Status.History, 10) 464 // increase limit 465 setRevisionHistoryLimit(11) 466 addHistory() 467 assert.Len(t, app.Status.History, 11) 468 // decrease limit 469 setRevisionHistoryLimit(9) 470 addHistory() 471 assert.Len(t, app.Status.History, 9) 472 473 metav1NowTime := metav1.NewTime(time.Now()) 474 err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, metav1NowTime) 475 assert.NoError(t, err) 476 assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime) 477 } 478 479 // helper function to read contents of a file to string 480 // panics on error 481 func mustReadFile(path string) string { 482 b, err := ioutil.ReadFile(path) 483 if err != nil { 484 panic(err.Error()) 485 } 486 return string(b) 487 } 488 489 var signedProj = argoappv1.AppProject{ 490 ObjectMeta: metav1.ObjectMeta{ 491 Name: "default", 492 Namespace: test.FakeArgoCDNamespace, 493 }, 494 Spec: argoappv1.AppProjectSpec{ 495 SourceRepos: []string{"*"}, 496 Destinations: []argoappv1.ApplicationDestination{ 497 { 498 Server: "*", 499 Namespace: "*", 500 }, 501 }, 502 SignatureKeys: []argoappv1.SignatureKey{ 503 { 504 KeyID: "4AEE18F83AFDEB23", 505 }, 506 }, 507 }, 508 } 509 510 func TestSignedResponseNoSignatureRequired(t *testing.T) { 511 oldval := os.Getenv("ARGOCD_GPG_ENABLED") 512 os.Setenv("ARGOCD_GPG_ENABLED", "true") 513 defer os.Setenv("ARGOCD_GPG_ENABLED", oldval) 514 // We have a good signature response, but project does not require signed commits 515 { 516 app := newFakeApp() 517 data := fakeData{ 518 manifestResponse: &apiclient.ManifestResponse{ 519 Manifests: []string{}, 520 Namespace: test.FakeDestNamespace, 521 Server: test.FakeClusterURL, 522 Revision: "abc123", 523 VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"), 524 }, 525 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 526 } 527 ctrl := newFakeController(&data) 528 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 529 assert.NotNil(t, compRes) 530 assert.NotNil(t, compRes.syncStatus) 531 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 532 assert.Len(t, compRes.resources, 0) 533 assert.Len(t, compRes.managedResources, 0) 534 assert.Len(t, app.Status.Conditions, 0) 535 } 536 // We have a bad signature response, but project does not require signed commits 537 { 538 app := newFakeApp() 539 data := fakeData{ 540 manifestResponse: &apiclient.ManifestResponse{ 541 Manifests: []string{}, 542 Namespace: test.FakeDestNamespace, 543 Server: test.FakeClusterURL, 544 Revision: "abc123", 545 VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"), 546 }, 547 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 548 } 549 ctrl := newFakeController(&data) 550 compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil) 551 assert.NotNil(t, compRes) 552 assert.NotNil(t, compRes.syncStatus) 553 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 554 assert.Len(t, compRes.resources, 0) 555 assert.Len(t, compRes.managedResources, 0) 556 assert.Len(t, app.Status.Conditions, 0) 557 } 558 } 559 560 func TestSignedResponseSignatureRequired(t *testing.T) { 561 oldval := os.Getenv("ARGOCD_GPG_ENABLED") 562 os.Setenv("ARGOCD_GPG_ENABLED", "true") 563 defer os.Setenv("ARGOCD_GPG_ENABLED", oldval) 564 565 // We have a good signature response, valid key, and signing is required - sync! 566 { 567 app := newFakeApp() 568 data := fakeData{ 569 manifestResponse: &apiclient.ManifestResponse{ 570 Manifests: []string{}, 571 Namespace: test.FakeDestNamespace, 572 Server: test.FakeClusterURL, 573 Revision: "abc123", 574 VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"), 575 }, 576 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 577 } 578 ctrl := newFakeController(&data) 579 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "", app.Spec.Source, false, nil) 580 assert.NotNil(t, compRes) 581 assert.NotNil(t, compRes.syncStatus) 582 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 583 assert.Len(t, compRes.resources, 0) 584 assert.Len(t, compRes.managedResources, 0) 585 assert.Len(t, app.Status.Conditions, 0) 586 } 587 // We have a bad signature response and signing is required - do not sync 588 { 589 app := newFakeApp() 590 data := fakeData{ 591 manifestResponse: &apiclient.ManifestResponse{ 592 Manifests: []string{}, 593 Namespace: test.FakeDestNamespace, 594 Server: test.FakeClusterURL, 595 Revision: "abc123", 596 VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"), 597 }, 598 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 599 } 600 ctrl := newFakeController(&data) 601 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil) 602 assert.NotNil(t, compRes) 603 assert.NotNil(t, compRes.syncStatus) 604 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 605 assert.Len(t, compRes.resources, 0) 606 assert.Len(t, compRes.managedResources, 0) 607 assert.Len(t, app.Status.Conditions, 1) 608 } 609 // We have a malformed signature response and signing is required - do not sync 610 { 611 app := newFakeApp() 612 data := fakeData{ 613 manifestResponse: &apiclient.ManifestResponse{ 614 Manifests: []string{}, 615 Namespace: test.FakeDestNamespace, 616 Server: test.FakeClusterURL, 617 Revision: "abc123", 618 VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_malformed1.txt"), 619 }, 620 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 621 } 622 ctrl := newFakeController(&data) 623 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil) 624 assert.NotNil(t, compRes) 625 assert.NotNil(t, compRes.syncStatus) 626 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 627 assert.Len(t, compRes.resources, 0) 628 assert.Len(t, compRes.managedResources, 0) 629 assert.Len(t, app.Status.Conditions, 1) 630 } 631 // We have no signature response (no signature made) and signing is required - do not sync 632 { 633 app := newFakeApp() 634 data := fakeData{ 635 manifestResponse: &apiclient.ManifestResponse{ 636 Manifests: []string{}, 637 Namespace: test.FakeDestNamespace, 638 Server: test.FakeClusterURL, 639 Revision: "abc123", 640 VerifyResult: "", 641 }, 642 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 643 } 644 ctrl := newFakeController(&data) 645 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil) 646 assert.NotNil(t, compRes) 647 assert.NotNil(t, compRes.syncStatus) 648 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 649 assert.Len(t, compRes.resources, 0) 650 assert.Len(t, compRes.managedResources, 0) 651 assert.Len(t, app.Status.Conditions, 1) 652 } 653 654 // We have a good signature and signing is required, but key is not allowed - do not sync 655 { 656 app := newFakeApp() 657 data := fakeData{ 658 manifestResponse: &apiclient.ManifestResponse{ 659 Manifests: []string{}, 660 Namespace: test.FakeDestNamespace, 661 Server: test.FakeClusterURL, 662 Revision: "abc123", 663 VerifyResult: mustReadFile("../util/gpg/testdata/good_signature.txt"), 664 }, 665 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 666 } 667 ctrl := newFakeController(&data) 668 testProj := signedProj 669 testProj.Spec.SignatureKeys[0].KeyID = "4AEE18F83AFDEB24" 670 compRes := ctrl.appStateManager.CompareAppState(app, &testProj, "abc123", app.Spec.Source, false, nil) 671 assert.NotNil(t, compRes) 672 assert.NotNil(t, compRes.syncStatus) 673 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 674 assert.Len(t, compRes.resources, 0) 675 assert.Len(t, compRes.managedResources, 0) 676 assert.Len(t, app.Status.Conditions, 1) 677 assert.Contains(t, app.Status.Conditions[0].Message, "key is not allowed") 678 } 679 // Signature required and local manifests supplied - do not sync 680 { 681 app := newFakeApp() 682 data := fakeData{ 683 manifestResponse: &apiclient.ManifestResponse{ 684 Manifests: []string{}, 685 Namespace: test.FakeDestNamespace, 686 Server: test.FakeClusterURL, 687 Revision: "abc123", 688 VerifyResult: "", 689 }, 690 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 691 } 692 // it doesn't matter for our test whether local manifests are valid 693 localManifests := []string{"foobar"} 694 ctrl := newFakeController(&data) 695 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, localManifests) 696 assert.NotNil(t, compRes) 697 assert.NotNil(t, compRes.syncStatus) 698 assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status) 699 assert.Len(t, compRes.resources, 0) 700 assert.Len(t, compRes.managedResources, 0) 701 assert.Len(t, app.Status.Conditions, 1) 702 assert.Contains(t, app.Status.Conditions[0].Message, "Cannot use local manifests") 703 } 704 705 os.Setenv("ARGOCD_GPG_ENABLED", "false") 706 // We have a bad signature response and signing would be required, but GPG subsystem is disabled - sync 707 { 708 app := newFakeApp() 709 data := fakeData{ 710 manifestResponse: &apiclient.ManifestResponse{ 711 Manifests: []string{}, 712 Namespace: test.FakeDestNamespace, 713 Server: test.FakeClusterURL, 714 Revision: "abc123", 715 VerifyResult: mustReadFile("../util/gpg/testdata/bad_signature_bad.txt"), 716 }, 717 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 718 } 719 ctrl := newFakeController(&data) 720 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil) 721 assert.NotNil(t, compRes) 722 assert.NotNil(t, compRes.syncStatus) 723 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 724 assert.Len(t, compRes.resources, 0) 725 assert.Len(t, compRes.managedResources, 0) 726 assert.Len(t, app.Status.Conditions, 0) 727 } 728 729 // Signature required and local manifests supplied and GPG subystem is disabled - sync 730 { 731 app := newFakeApp() 732 data := fakeData{ 733 manifestResponse: &apiclient.ManifestResponse{ 734 Manifests: []string{}, 735 Namespace: test.FakeDestNamespace, 736 Server: test.FakeClusterURL, 737 Revision: "abc123", 738 VerifyResult: "", 739 }, 740 managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), 741 } 742 // it doesn't matter for our test whether local manifests are valid 743 localManifests := []string{""} 744 ctrl := newFakeController(&data) 745 compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, localManifests) 746 assert.NotNil(t, compRes) 747 assert.NotNil(t, compRes.syncStatus) 748 assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) 749 assert.Len(t, compRes.resources, 0) 750 assert.Len(t, compRes.managedResources, 0) 751 assert.Len(t, app.Status.Conditions, 0) 752 } 753 754 } 755 756 func TestComparisonResult_GetHealthStatus(t *testing.T) { 757 status := &argoappv1.HealthStatus{Status: health.HealthStatusMissing} 758 res := comparisonResult{ 759 healthStatus: status, 760 } 761 762 assert.Equal(t, status, res.GetHealthStatus()) 763 } 764 765 func TestComparisonResult_GetSyncStatus(t *testing.T) { 766 status := &argoappv1.SyncStatus{Status: argoappv1.SyncStatusCodeOutOfSync} 767 res := comparisonResult{ 768 syncStatus: status, 769 } 770 771 assert.Equal(t, status, res.GetSyncStatus()) 772 }