github.com/argoproj/argo-cd/v3@v3.2.1/cmd/argocd/commands/app_test.go (about) 1 package commands 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "os" 10 "slices" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/argoproj/gitops-engine/pkg/health" 16 "github.com/argoproj/gitops-engine/pkg/utils/kube" 17 "github.com/coreos/go-oidc/v3/oidc" 18 "github.com/google/go-cmp/cmp" 19 "github.com/google/go-cmp/cmp/cmpopts" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 "golang.org/x/oauth2" 23 "google.golang.org/grpc" 24 corev1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/util/intstr" 29 "k8s.io/apimachinery/pkg/watch" 30 "sigs.k8s.io/yaml" 31 32 argocdclient "github.com/argoproj/argo-cd/v3/pkg/apiclient" 33 accountpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/account" 34 applicationpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/application" 35 applicationsetpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/applicationset" 36 certificatepkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/certificate" 37 clusterpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster" 38 gpgkeypkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/gpgkey" 39 notificationpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/notification" 40 projectpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/project" 41 repocredspkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/repocreds" 42 repositorypkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/repository" 43 sessionpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/session" 44 settingspkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/settings" 45 versionpkg "github.com/argoproj/argo-cd/v3/pkg/apiclient/version" 46 "github.com/argoproj/argo-cd/v3/pkg/apis/application" 47 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 48 "github.com/argoproj/argo-cd/v3/reposerver/apiclient" 49 ) 50 51 func Test_getInfos(t *testing.T) { 52 testCases := []struct { 53 name string 54 infos []string 55 expectedInfos []*v1alpha1.Info 56 }{ 57 { 58 name: "empty", 59 infos: []string{}, 60 expectedInfos: []*v1alpha1.Info{}, 61 }, 62 { 63 name: "simple key value", 64 infos: []string{"key1=value1", "key2=value2"}, 65 expectedInfos: []*v1alpha1.Info{ 66 {Name: "key1", Value: "value1"}, 67 {Name: "key2", Value: "value2"}, 68 }, 69 }, 70 } 71 72 for _, testCase := range testCases { 73 t.Run(testCase.name, func(t *testing.T) { 74 infos := getInfos(testCase.infos) 75 assert.Len(t, infos, len(testCase.expectedInfos)) 76 sort := func(a, b *v1alpha1.Info) bool { return a.Name < b.Name } 77 assert.Empty(t, cmp.Diff(testCase.expectedInfos, infos, cmpopts.SortSlices(sort))) 78 }) 79 } 80 } 81 82 func Test_getRefreshType(t *testing.T) { 83 refreshTypeNormal := string(v1alpha1.RefreshTypeNormal) 84 refreshTypeHard := string(v1alpha1.RefreshTypeHard) 85 testCases := []struct { 86 refresh bool 87 hardRefresh bool 88 expected *string 89 }{ 90 {false, false, nil}, 91 {false, true, &refreshTypeHard}, 92 {true, false, &refreshTypeNormal}, 93 {true, true, &refreshTypeHard}, 94 } 95 96 for _, testCase := range testCases { 97 t.Run(fmt.Sprintf("hardRefresh=%t refresh=%t", testCase.hardRefresh, testCase.refresh), func(t *testing.T) { 98 refreshType := getRefreshType(testCase.refresh, testCase.hardRefresh) 99 if testCase.expected == nil { 100 assert.Nil(t, refreshType) 101 } else { 102 assert.NotNil(t, refreshType) 103 assert.Equal(t, *testCase.expected, *refreshType) 104 } 105 }) 106 } 107 } 108 109 func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { 110 histories := v1alpha1.RevisionHistories{} 111 112 histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) 113 histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) 114 histories = append(histories, v1alpha1.RevisionHistory{ID: 3}) 115 116 status := v1alpha1.ApplicationStatus{ 117 Resources: nil, 118 Sync: v1alpha1.SyncStatus{}, 119 Health: v1alpha1.AppHealthStatus{}, 120 History: histories, 121 Conditions: nil, 122 ReconciledAt: nil, 123 OperationState: nil, 124 ObservedAt: nil, 125 SourceType: "", 126 Summary: v1alpha1.ApplicationSummary{}, 127 } 128 129 application := v1alpha1.Application{ 130 Status: status, 131 } 132 133 history, err := findRevisionHistory(&application, -1) 134 require.NoError(t, err, "Find revision history should fail without errors") 135 require.NotNil(t, history, "History should be found") 136 } 137 138 func TestPrintTreeViewAppGet(t *testing.T) { 139 var nodes [3]v1alpha1.ResourceNode 140 nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} 141 nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} 142 nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} 143 nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} 144 nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} 145 146 nodeMapping := make(map[string]v1alpha1.ResourceNode) 147 mapParentToChild := make(map[string][]string) 148 parentNode := make(map[string]struct{}) 149 150 for _, node := range nodes { 151 nodeMapping[node.UID] = node 152 153 if len(node.ParentRefs) > 0 { 154 _, ok := mapParentToChild[node.ParentRefs[0].UID] 155 if !ok { 156 var temp []string 157 mapParentToChild[node.ParentRefs[0].UID] = temp 158 } 159 mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) 160 } else { 161 parentNode[node.UID] = struct{}{} 162 } 163 } 164 165 output, _ := captureOutput(func() error { 166 printTreeView(nodeMapping, mapParentToChild, parentNode, nil) 167 return nil 168 }) 169 170 assert.Contains(t, output, "Pod") 171 assert.Contains(t, output, "ReplicaSet") 172 assert.Contains(t, output, "Rollout") 173 assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") 174 } 175 176 func TestPrintTreeViewDetailedAppGet(t *testing.T) { 177 var nodes [3]v1alpha1.ResourceNode 178 nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} 179 nodes[0].Health = &v1alpha1.HealthStatus{Status: "Degraded", Message: "Readiness Gate failed"} 180 nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} 181 nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} 182 nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} 183 nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} 184 185 nodeMapping := make(map[string]v1alpha1.ResourceNode) 186 mapParentToChild := make(map[string][]string) 187 parentNode := make(map[string]struct{}) 188 189 for _, node := range nodes { 190 nodeMapping[node.UID] = node 191 192 if len(node.ParentRefs) > 0 { 193 _, ok := mapParentToChild[node.ParentRefs[0].UID] 194 if !ok { 195 var temp []string 196 mapParentToChild[node.ParentRefs[0].UID] = temp 197 } 198 mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) 199 } else { 200 parentNode[node.UID] = struct{}{} 201 } 202 } 203 204 output, _ := captureOutput(func() error { 205 printTreeViewDetailed(nodeMapping, mapParentToChild, parentNode, nil) 206 return nil 207 }) 208 209 assert.Contains(t, output, "Pod") 210 assert.Contains(t, output, "ReplicaSet") 211 assert.Contains(t, output, "Rollout") 212 assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") 213 assert.Contains(t, output, "Degraded") 214 assert.Contains(t, output, "Readiness Gate failed") 215 } 216 217 func TestFindRevisionHistoryWithoutPassedIdWithMultipleSources(t *testing.T) { 218 histories := v1alpha1.RevisionHistories{} 219 220 histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) 221 histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) 222 histories = append(histories, v1alpha1.RevisionHistory{ID: 3}) 223 224 status := v1alpha1.ApplicationStatus{ 225 Resources: nil, 226 Sync: v1alpha1.SyncStatus{}, 227 Health: v1alpha1.AppHealthStatus{}, 228 History: histories, 229 Conditions: nil, 230 ReconciledAt: nil, 231 OperationState: nil, 232 ObservedAt: nil, 233 SourceType: "", 234 Summary: v1alpha1.ApplicationSummary{}, 235 } 236 237 application := v1alpha1.Application{ 238 Status: status, 239 } 240 241 history, err := findRevisionHistory(&application, -1) 242 require.NoError(t, err, "Find revision history should fail without errors") 243 require.NotNil(t, history, "History should be found") 244 } 245 246 func TestDefaultWaitOptions(t *testing.T) { 247 watch := watchOpts{ 248 sync: false, 249 health: false, 250 operation: false, 251 suspended: false, 252 } 253 opts := getWatchOpts(watch) 254 assert.True(t, opts.sync) 255 assert.True(t, opts.health) 256 assert.True(t, opts.operation) 257 assert.False(t, opts.suspended) 258 } 259 260 func TestOverrideWaitOptions(t *testing.T) { 261 watch := watchOpts{ 262 sync: true, 263 health: false, 264 operation: false, 265 suspended: false, 266 } 267 opts := getWatchOpts(watch) 268 assert.True(t, opts.sync) 269 assert.False(t, opts.health) 270 assert.False(t, opts.operation) 271 assert.False(t, opts.suspended) 272 } 273 274 func TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) { 275 histories := v1alpha1.RevisionHistories{} 276 277 status := v1alpha1.ApplicationStatus{ 278 Resources: nil, 279 Sync: v1alpha1.SyncStatus{}, 280 Health: v1alpha1.AppHealthStatus{}, 281 History: histories, 282 Conditions: nil, 283 ReconciledAt: nil, 284 OperationState: nil, 285 ObservedAt: nil, 286 SourceType: "", 287 Summary: v1alpha1.ApplicationSummary{}, 288 } 289 290 application := v1alpha1.Application{ 291 Status: status, 292 } 293 294 history, err := findRevisionHistory(&application, -1) 295 296 require.Error(t, err, "Find revision history should fail with errors") 297 require.Nil(t, history, "History should be empty") 298 require.EqualError(t, err, "application '' should have at least two successful deployments", "Find revision history should fail with correct error message") 299 } 300 301 func TestFindRevisionHistoryWithPassedId(t *testing.T) { 302 histories := v1alpha1.RevisionHistories{} 303 304 histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) 305 histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) 306 histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"}) 307 308 status := v1alpha1.ApplicationStatus{ 309 Resources: nil, 310 Sync: v1alpha1.SyncStatus{}, 311 Health: v1alpha1.AppHealthStatus{}, 312 History: histories, 313 Conditions: nil, 314 ReconciledAt: nil, 315 OperationState: nil, 316 ObservedAt: nil, 317 SourceType: "", 318 Summary: v1alpha1.ApplicationSummary{}, 319 } 320 321 application := v1alpha1.Application{ 322 Status: status, 323 } 324 325 history, err := findRevisionHistory(&application, 3) 326 require.NoError(t, err, "Find revision history should fail without errors") 327 require.NotNil(t, history, "History should be found") 328 require.Equal(t, "123", history.Revision, "Failed to find correct history with correct revision") 329 } 330 331 func TestFindRevisionHistoryWithPassedIdThatNotExist(t *testing.T) { 332 histories := v1alpha1.RevisionHistories{} 333 334 histories = append(histories, v1alpha1.RevisionHistory{ID: 1}) 335 histories = append(histories, v1alpha1.RevisionHistory{ID: 2}) 336 histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"}) 337 338 status := v1alpha1.ApplicationStatus{ 339 Resources: nil, 340 Sync: v1alpha1.SyncStatus{}, 341 Health: v1alpha1.AppHealthStatus{}, 342 History: histories, 343 Conditions: nil, 344 ReconciledAt: nil, 345 OperationState: nil, 346 ObservedAt: nil, 347 SourceType: "", 348 Summary: v1alpha1.ApplicationSummary{}, 349 } 350 351 application := v1alpha1.Application{ 352 Status: status, 353 } 354 355 history, err := findRevisionHistory(&application, 4) 356 357 require.Error(t, err, "Find revision history should fail with errors") 358 require.Nil(t, history, "History should be not found") 359 require.EqualError(t, err, "application '' does not have deployment id '4' in history", "Find revision history should fail with correct error message") 360 } 361 362 func Test_groupObjsByKey(t *testing.T) { 363 localObjs := []*unstructured.Unstructured{ 364 { 365 Object: map[string]any{ 366 "apiVersion": "v1", 367 "kind": "Pod", 368 "metadata": map[string]any{ 369 "name": "pod-name", 370 "namespace": "default", 371 }, 372 }, 373 }, 374 { 375 Object: map[string]any{ 376 "apiVersion": "apiextensions.k8s.io/v1", 377 "kind": "CustomResourceDefinition", 378 "metadata": map[string]any{ 379 "name": "certificates.cert-manager.io", 380 }, 381 }, 382 }, 383 } 384 liveObjs := []*unstructured.Unstructured{ 385 { 386 Object: map[string]any{ 387 "apiVersion": "v1", 388 "kind": "Pod", 389 "metadata": map[string]any{ 390 "name": "pod-name", 391 "namespace": "default", 392 }, 393 }, 394 }, 395 { 396 Object: map[string]any{ 397 "apiVersion": "apiextensions.k8s.io/v1", 398 "kind": "CustomResourceDefinition", 399 "metadata": map[string]any{ 400 "name": "certificates.cert-manager.io", 401 }, 402 }, 403 }, 404 } 405 406 expected := map[kube.ResourceKey]*unstructured.Unstructured{ 407 {Group: "", Kind: "Pod", Namespace: "default", Name: "pod-name"}: localObjs[0], 408 {Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition", Namespace: "", Name: "certificates.cert-manager.io"}: localObjs[1], 409 } 410 411 objByKey := groupObjsByKey(localObjs, liveObjs, "default") 412 assert.Equal(t, expected, objByKey) 413 } 414 415 func TestFormatSyncPolicy(t *testing.T) { 416 t.Run("Policy not defined", func(t *testing.T) { 417 app := v1alpha1.Application{} 418 419 policy := formatSyncPolicy(app) 420 421 require.Equalf(t, "Manual", policy, "Incorrect policy %q, should be Manual", policy) 422 }) 423 424 t.Run("Auto policy", func(t *testing.T) { 425 app := v1alpha1.Application{ 426 Spec: v1alpha1.ApplicationSpec{ 427 SyncPolicy: &v1alpha1.SyncPolicy{ 428 Automated: &v1alpha1.SyncPolicyAutomated{}, 429 }, 430 }, 431 } 432 433 policy := formatSyncPolicy(app) 434 435 require.Equalf(t, "Auto", policy, "Incorrect policy %q, should be Auto", policy) 436 }) 437 438 t.Run("Auto policy with prune", func(t *testing.T) { 439 app := v1alpha1.Application{ 440 Spec: v1alpha1.ApplicationSpec{ 441 SyncPolicy: &v1alpha1.SyncPolicy{ 442 Automated: &v1alpha1.SyncPolicyAutomated{ 443 Prune: true, 444 }, 445 }, 446 }, 447 } 448 449 policy := formatSyncPolicy(app) 450 451 require.Equalf(t, "Auto-Prune", policy, "Incorrect policy %q, should be Auto-Prune", policy) 452 }) 453 } 454 455 func TestFormatConditionSummary(t *testing.T) { 456 t.Run("No conditions are defined", func(t *testing.T) { 457 app := v1alpha1.Application{ 458 Spec: v1alpha1.ApplicationSpec{ 459 SyncPolicy: &v1alpha1.SyncPolicy{ 460 Automated: &v1alpha1.SyncPolicyAutomated{ 461 Prune: true, 462 }, 463 }, 464 }, 465 } 466 467 summary := formatConditionsSummary(app) 468 require.Equalf(t, "<none>", summary, "Incorrect summary %q, should be <none>", summary) 469 }) 470 471 t.Run("Few conditions are defined", func(t *testing.T) { 472 app := v1alpha1.Application{ 473 Status: v1alpha1.ApplicationStatus{ 474 Conditions: []v1alpha1.ApplicationCondition{ 475 { 476 Type: "type1", 477 }, 478 { 479 Type: "type1", 480 }, 481 { 482 Type: "type2", 483 }, 484 }, 485 }, 486 } 487 488 summary := formatConditionsSummary(app) 489 require.Equalf(t, "type1(2),type2", summary, "Incorrect summary %q, should be type1(2),type2", summary) 490 }) 491 492 t.Run("Conditions are sorted for idempotent summary", func(t *testing.T) { 493 app := v1alpha1.Application{ 494 Status: v1alpha1.ApplicationStatus{ 495 Conditions: []v1alpha1.ApplicationCondition{ 496 { 497 Type: "type2", 498 }, 499 { 500 Type: "type1", 501 }, 502 { 503 Type: "type1", 504 }, 505 }, 506 }, 507 } 508 509 summary := formatConditionsSummary(app) 510 require.Equalf(t, "type1(2),type2", summary, "Incorrect summary %q, should be type1(2),type2", summary) 511 }) 512 } 513 514 func TestPrintOperationResult(t *testing.T) { 515 t.Run("Operation state is empty", func(t *testing.T) { 516 output, _ := captureOutput(func() error { 517 printOperationResult(nil) 518 return nil 519 }) 520 521 require.Emptyf(t, output, "Incorrect print operation output %q, should be ''", output) 522 }) 523 524 t.Run("Operation state sync result is not empty", func(t *testing.T) { 525 time := metav1.Date(2020, time.November, 10, 23, 0, 0, 0, time.UTC) 526 output, _ := captureOutput(func() error { 527 printOperationResult(&v1alpha1.OperationState{ 528 SyncResult: &v1alpha1.SyncOperationResult{Revision: "revision"}, 529 FinishedAt: &time, 530 }) 531 return nil 532 }) 533 534 expectation := "Operation: Sync\nSync Revision: revision\nPhase: \nStart: 0001-01-01 00:00:00 +0000 UTC\nFinished: 2020-11-10 23:00:00 +0000 UTC\nDuration: 2333448h16m18.871345152s\n" 535 require.Equalf(t, output, expectation, "Incorrect print operation output %q, should be %q", output, expectation) 536 }) 537 538 t.Run("Operation state sync result with message is not empty", func(t *testing.T) { 539 time := metav1.Date(2020, time.November, 10, 23, 0, 0, 0, time.UTC) 540 output, _ := captureOutput(func() error { 541 printOperationResult(&v1alpha1.OperationState{ 542 SyncResult: &v1alpha1.SyncOperationResult{Revision: "revision"}, 543 FinishedAt: &time, 544 Message: "test", 545 }) 546 return nil 547 }) 548 549 expectation := "Operation: Sync\nSync Revision: revision\nPhase: \nStart: 0001-01-01 00:00:00 +0000 UTC\nFinished: 2020-11-10 23:00:00 +0000 UTC\nDuration: 2333448h16m18.871345152s\nMessage: test\n" 550 require.Equalf(t, output, expectation, "Incorrect print operation output %q, should be %q", output, expectation) 551 }) 552 } 553 554 func TestPrintApplicationHistoryTable(t *testing.T) { 555 histories := []v1alpha1.RevisionHistory{ 556 { 557 ID: 1, 558 Source: v1alpha1.ApplicationSource{ 559 TargetRevision: "1", 560 RepoURL: "test", 561 }, 562 }, 563 { 564 ID: 2, 565 Source: v1alpha1.ApplicationSource{ 566 TargetRevision: "2", 567 RepoURL: "test", 568 }, 569 }, 570 { 571 ID: 3, 572 Source: v1alpha1.ApplicationSource{ 573 TargetRevision: "3", 574 RepoURL: "test", 575 }, 576 }, 577 } 578 579 output, _ := captureOutput(func() error { 580 printApplicationHistoryTable(histories) 581 return nil 582 }) 583 584 expectation := "SOURCE test\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1\n2 0001-01-01 00:00:00 +0000 UTC 2\n3 0001-01-01 00:00:00 +0000 UTC 3\n" 585 586 require.Equalf(t, output, expectation, "Incorrect print operation output %q, should be %q", output, expectation) 587 } 588 589 func TestPrintApplicationHistoryTableWithMultipleSources(t *testing.T) { 590 histories := []v1alpha1.RevisionHistory{ 591 { 592 ID: 0, 593 Source: v1alpha1.ApplicationSource{ 594 TargetRevision: "0", 595 RepoURL: "test", 596 }, 597 }, 598 { 599 ID: 1, 600 Revisions: []string{ 601 "1a", 602 "1b", 603 }, 604 // added Source just for testing the fuction 605 Source: v1alpha1.ApplicationSource{ 606 TargetRevision: "-1", 607 RepoURL: "ignore", 608 }, 609 Sources: v1alpha1.ApplicationSources{ 610 v1alpha1.ApplicationSource{ 611 RepoURL: "test-1", 612 TargetRevision: "1a", 613 }, 614 v1alpha1.ApplicationSource{ 615 RepoURL: "test-2", 616 TargetRevision: "1b", 617 }, 618 }, 619 }, 620 { 621 ID: 2, 622 Revisions: []string{ 623 "2a", 624 "2b", 625 }, 626 Sources: v1alpha1.ApplicationSources{ 627 v1alpha1.ApplicationSource{ 628 RepoURL: "test-1", 629 TargetRevision: "2a", 630 }, 631 v1alpha1.ApplicationSource{ 632 RepoURL: "test-2", 633 TargetRevision: "2b", 634 }, 635 }, 636 }, 637 { 638 ID: 3, 639 Revisions: []string{ 640 "3a", 641 "3b", 642 }, 643 Sources: v1alpha1.ApplicationSources{ 644 v1alpha1.ApplicationSource{ 645 RepoURL: "test-1", 646 TargetRevision: "3a", 647 }, 648 v1alpha1.ApplicationSource{ 649 RepoURL: "test-2", 650 TargetRevision: "3b", 651 }, 652 }, 653 }, 654 } 655 656 output, _ := captureOutput(func() error { 657 printApplicationHistoryTable(histories) 658 return nil 659 }) 660 661 expectation := "SOURCE test\nID DATE REVISION\n0 0001-01-01 00:00:00 +0000 UTC 0\n\nSOURCE test-1\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1a\n2 0001-01-01 00:00:00 +0000 UTC 2a\n3 0001-01-01 00:00:00 +0000 UTC 3a\n\nSOURCE test-2\nID DATE REVISION\n1 0001-01-01 00:00:00 +0000 UTC 1b\n2 0001-01-01 00:00:00 +0000 UTC 2b\n3 0001-01-01 00:00:00 +0000 UTC 3b\n" 662 663 require.Equalf(t, output, expectation, "Incorrect print operation output %q, should be %q", output, expectation) 664 } 665 666 func TestPrintAppSummaryTable(t *testing.T) { 667 output, _ := captureOutput(func() error { 668 app := &v1alpha1.Application{ 669 ObjectMeta: metav1.ObjectMeta{ 670 Name: "test", 671 Namespace: "argocd", 672 }, 673 Spec: v1alpha1.ApplicationSpec{ 674 SyncPolicy: &v1alpha1.SyncPolicy{ 675 Automated: &v1alpha1.SyncPolicyAutomated{ 676 Prune: true, 677 }, 678 }, 679 Project: "default", 680 Destination: v1alpha1.ApplicationDestination{Server: "local", Namespace: "argocd"}, 681 Source: &v1alpha1.ApplicationSource{ 682 RepoURL: "test", 683 TargetRevision: "master", 684 Path: "/test", 685 Helm: &v1alpha1.ApplicationSourceHelm{ 686 ValueFiles: []string{"path1", "path2"}, 687 }, 688 Kustomize: &v1alpha1.ApplicationSourceKustomize{NamePrefix: "prefix"}, 689 }, 690 }, 691 Status: v1alpha1.ApplicationStatus{ 692 Sync: v1alpha1.SyncStatus{ 693 Status: v1alpha1.SyncStatusCodeOutOfSync, 694 }, 695 Health: v1alpha1.AppHealthStatus{ 696 Status: health.HealthStatusProgressing, 697 }, 698 }, 699 } 700 701 windows := &v1alpha1.SyncWindows{ 702 { 703 Kind: "allow", 704 Schedule: "0 0 * * *", 705 Duration: "24h", 706 Applications: []string{ 707 "*-prod", 708 }, 709 ManualSync: true, 710 }, 711 { 712 Kind: "deny", 713 Schedule: "0 0 * * *", 714 Duration: "24h", 715 Namespaces: []string{ 716 "default", 717 }, 718 }, 719 { 720 Kind: "allow", 721 Schedule: "0 0 * * *", 722 Duration: "24h", 723 Clusters: []string{ 724 "in-cluster", 725 "cluster1", 726 }, 727 }, 728 } 729 730 printAppSummaryTable(app, "url", windows) 731 return nil 732 }) 733 734 expectation := `Name: argocd/test 735 Project: default 736 Server: local 737 Namespace: argocd 738 URL: url 739 Source: 740 - Repo: test 741 Target: master 742 Path: /test 743 Helm Values: path1,path2 744 Name Prefix: prefix 745 SyncWindow: Sync Denied 746 Assigned Windows: allow:0 0 * * *:24h,deny:0 0 * * *:24h,allow:0 0 * * *:24h 747 Sync Policy: Automated (Prune) 748 Sync Status: OutOfSync from master 749 Health Status: Progressing 750 ` 751 assert.Equalf(t, expectation, output, "Incorrect print app summary output %q, should be %q", output, expectation) 752 } 753 754 func TestPrintAppSummaryTable_MultipleSources(t *testing.T) { 755 output, _ := captureOutput(func() error { 756 app := &v1alpha1.Application{ 757 ObjectMeta: metav1.ObjectMeta{ 758 Name: "test", 759 Namespace: "argocd", 760 }, 761 Spec: v1alpha1.ApplicationSpec{ 762 SyncPolicy: &v1alpha1.SyncPolicy{ 763 Automated: &v1alpha1.SyncPolicyAutomated{ 764 Prune: true, 765 }, 766 }, 767 Project: "default", 768 Destination: v1alpha1.ApplicationDestination{Server: "local", Namespace: "argocd"}, 769 Sources: v1alpha1.ApplicationSources{ 770 { 771 RepoURL: "test", 772 TargetRevision: "master", 773 Path: "/test", 774 Helm: &v1alpha1.ApplicationSourceHelm{ 775 ValueFiles: []string{"path1", "path2"}, 776 }, 777 Kustomize: &v1alpha1.ApplicationSourceKustomize{NamePrefix: "prefix"}, 778 }, { 779 RepoURL: "test2", 780 TargetRevision: "master2", 781 Path: "/test2", 782 }, 783 }, 784 }, 785 Status: v1alpha1.ApplicationStatus{ 786 Sync: v1alpha1.SyncStatus{ 787 Status: v1alpha1.SyncStatusCodeOutOfSync, 788 }, 789 Health: v1alpha1.AppHealthStatus{ 790 Status: health.HealthStatusProgressing, 791 }, 792 }, 793 } 794 795 windows := &v1alpha1.SyncWindows{ 796 { 797 Kind: "allow", 798 Schedule: "0 0 * * *", 799 Duration: "24h", 800 Applications: []string{ 801 "*-prod", 802 }, 803 ManualSync: true, 804 }, 805 { 806 Kind: "deny", 807 Schedule: "0 0 * * *", 808 Duration: "24h", 809 Namespaces: []string{ 810 "default", 811 }, 812 }, 813 { 814 Kind: "allow", 815 Schedule: "0 0 * * *", 816 Duration: "24h", 817 Clusters: []string{ 818 "in-cluster", 819 "cluster1", 820 }, 821 }, 822 } 823 824 printAppSummaryTable(app, "url", windows) 825 return nil 826 }) 827 828 expectation := `Name: argocd/test 829 Project: default 830 Server: local 831 Namespace: argocd 832 URL: url 833 Sources: 834 - Repo: test 835 Target: master 836 Path: /test 837 Helm Values: path1,path2 838 Name Prefix: prefix 839 - Repo: test2 840 Target: master2 841 Path: /test2 842 SyncWindow: Sync Denied 843 Assigned Windows: allow:0 0 * * *:24h,deny:0 0 * * *:24h,allow:0 0 * * *:24h 844 Sync Policy: Automated (Prune) 845 Sync Status: OutOfSync from master 846 Health Status: Progressing 847 ` 848 assert.Equalf(t, expectation, output, "Incorrect print app summary output %q, should be %q", output, expectation) 849 } 850 851 func TestPrintAppConditions(t *testing.T) { 852 output, _ := captureOutput(func() error { 853 app := &v1alpha1.Application{ 854 Status: v1alpha1.ApplicationStatus{ 855 Conditions: []v1alpha1.ApplicationCondition{ 856 { 857 Type: v1alpha1.ApplicationConditionDeletionError, 858 Message: "test", 859 }, 860 { 861 Type: v1alpha1.ApplicationConditionExcludedResourceWarning, 862 Message: "test2", 863 }, 864 { 865 Type: v1alpha1.ApplicationConditionRepeatedResourceWarning, 866 Message: "test3", 867 }, 868 }, 869 }, 870 } 871 printAppConditions(os.Stdout, app) 872 return nil 873 }) 874 expectation := "CONDITION\tMESSAGE\tLAST TRANSITION\nDeletionError\ttest\t<nil>\nExcludedResourceWarning\ttest2\t<nil>\nRepeatedResourceWarning\ttest3\t<nil>\n" 875 require.Equalf(t, output, expectation, "Incorrect print app conditions output %q, should be %q", output, expectation) 876 } 877 878 func TestPrintParams(t *testing.T) { 879 testCases := []struct { 880 name string 881 app *v1alpha1.Application 882 sourcePosition int 883 expectedOutput string 884 }{ 885 { 886 name: "Single Source application with valid helm parameters", 887 app: &v1alpha1.Application{ 888 Spec: v1alpha1.ApplicationSpec{ 889 Source: &v1alpha1.ApplicationSource{ 890 Helm: &v1alpha1.ApplicationSourceHelm{ 891 Parameters: []v1alpha1.HelmParameter{ 892 { 893 Name: "name1", 894 Value: "value1", 895 }, 896 { 897 Name: "name2", 898 Value: "value2", 899 }, 900 { 901 Name: "name3", 902 Value: "value3", 903 }, 904 }, 905 }, 906 }, 907 }, 908 }, 909 sourcePosition: -1, 910 expectedOutput: "\n\nNAME VALUE\nname1 value1\nname2 value2\nname3 value3\n", 911 }, 912 { 913 name: "Multi-source application with a valid Source Position", 914 app: &v1alpha1.Application{ 915 Spec: v1alpha1.ApplicationSpec{ 916 Sources: []v1alpha1.ApplicationSource{ 917 { 918 Helm: &v1alpha1.ApplicationSourceHelm{ 919 Parameters: []v1alpha1.HelmParameter{ 920 { 921 Name: "nameA", 922 Value: "valueA", 923 }, 924 }, 925 }, 926 }, 927 { 928 Helm: &v1alpha1.ApplicationSourceHelm{ 929 Parameters: []v1alpha1.HelmParameter{ 930 { 931 Name: "nameB", 932 Value: "valueB", 933 }, 934 }, 935 }, 936 }, 937 }, 938 }, 939 }, 940 sourcePosition: 1, 941 expectedOutput: "\n\nNAME VALUE\nnameA valueA\n", 942 }, 943 } 944 945 for _, tc := range testCases { 946 t.Run(tc.name, func(t *testing.T) { 947 output, _ := captureOutput(func() error { 948 printParams(tc.app, tc.sourcePosition) 949 return nil 950 }) 951 952 require.Equalf(t, tc.expectedOutput, output, "Incorrect print params output %q, should be %q\n", output, tc.expectedOutput) 953 }) 954 } 955 } 956 957 func TestAppUrlDefault(t *testing.T) { 958 t.Run("Plain text", func(t *testing.T) { 959 result := appURLDefault(argocdclient.NewClientOrDie(&argocdclient.ClientOptions{ 960 ServerAddr: "localhost:80", 961 PlainText: true, 962 }), "test") 963 expectation := "http://localhost:80/applications/test" 964 require.Equalf(t, result, expectation, "Incorrect url %q, should be %q", result, expectation) 965 }) 966 t.Run("https", func(t *testing.T) { 967 result := appURLDefault(argocdclient.NewClientOrDie(&argocdclient.ClientOptions{ 968 ServerAddr: "localhost:443", 969 PlainText: false, 970 }), "test") 971 expectation := "https://localhost/applications/test" 972 require.Equalf(t, result, expectation, "Incorrect url %q, should be %q", result, expectation) 973 }) 974 } 975 976 func TestTruncateString(t *testing.T) { 977 result := truncateString("argocdtool", 2) 978 expectation := "ar..." 979 require.Equalf(t, result, expectation, "Incorrect truncate string %q, should be %q", result, expectation) 980 } 981 982 func TestGetService(t *testing.T) { 983 t.Run("Server", func(t *testing.T) { 984 app := &v1alpha1.Application{ 985 Spec: v1alpha1.ApplicationSpec{ 986 Destination: v1alpha1.ApplicationDestination{ 987 Server: "test-server", 988 }, 989 }, 990 } 991 result := getServer(app) 992 expectation := "test-server" 993 require.Equal(t, result, expectation, "Incorrect server %q, should be %q", result, expectation) 994 }) 995 t.Run("Name", func(t *testing.T) { 996 app := &v1alpha1.Application{ 997 Spec: v1alpha1.ApplicationSpec{ 998 Destination: v1alpha1.ApplicationDestination{ 999 Name: "test-name", 1000 }, 1001 }, 1002 } 1003 result := getServer(app) 1004 expectation := "test-name" 1005 require.Equal(t, result, expectation, "Incorrect server name %q, should be %q", result, expectation) 1006 }) 1007 } 1008 1009 func TestTargetObjects(t *testing.T) { 1010 resources := []*v1alpha1.ResourceDiff{ 1011 { 1012 TargetState: "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"name\":\"test-helm-guestbook\",\"namespace\":\"argocd\"},\"spec\":{\"selector\":{\"app\":\"helm-guestbook\",\"release\":\"test\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}", 1013 }, 1014 { 1015 TargetState: "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"name\":\"test-helm-guestbook\",\"namespace\":\"ns\"},\"spec\":{\"selector\":{\"app\":\"helm-guestbook\",\"release\":\"test\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}", 1016 }, 1017 } 1018 objects, err := targetObjects(resources) 1019 require.NoError(t, err, "operation should finish without error") 1020 require.Lenf(t, objects, 2, "incorrect number of objects %v, should be 2", len(objects)) 1021 require.Equalf(t, "test-helm-guestbook", objects[0].GetName(), "incorrect name %q, should be %q", objects[0].GetName(), "test-helm-guestbook") 1022 } 1023 1024 func TestTargetObjects_invalid(t *testing.T) { 1025 resources := []*v1alpha1.ResourceDiff{{TargetState: "{"}} 1026 _, err := targetObjects(resources) 1027 assert.Error(t, err) 1028 } 1029 1030 func TestCheckForDeleteEvent(t *testing.T) { 1031 fakeClient := new(fakeAcdClient) 1032 1033 checkForDeleteEvent(t.Context(), fakeClient, "testApp") 1034 } 1035 1036 func TestPrintApplicationNames(t *testing.T) { 1037 output, _ := captureOutput(func() error { 1038 app := &v1alpha1.Application{ 1039 ObjectMeta: metav1.ObjectMeta{ 1040 Name: "test", 1041 }, 1042 } 1043 printApplicationNames([]v1alpha1.Application{*app, *app}) 1044 return nil 1045 }) 1046 expectation := "test\ntest\n" 1047 require.Equalf(t, output, expectation, "Incorrect print params output %q, should be %q", output, expectation) 1048 } 1049 1050 func Test_unset(t *testing.T) { 1051 kustomizeSource := &v1alpha1.ApplicationSource{ 1052 Kustomize: &v1alpha1.ApplicationSourceKustomize{ 1053 IgnoreMissingComponents: true, 1054 NamePrefix: "some-prefix", 1055 NameSuffix: "some-suffix", 1056 Version: "123", 1057 Images: v1alpha1.KustomizeImages{ 1058 "old1=new:tag", 1059 "old2=new:tag", 1060 }, 1061 Replicas: []v1alpha1.KustomizeReplica{ 1062 { 1063 Name: "my-deployment", 1064 Count: intstr.FromInt(2), 1065 }, 1066 { 1067 Name: "my-statefulset", 1068 Count: intstr.FromInt(4), 1069 }, 1070 }, 1071 }, 1072 } 1073 1074 helmSource := &v1alpha1.ApplicationSource{ 1075 Helm: &v1alpha1.ApplicationSourceHelm{ 1076 IgnoreMissingValueFiles: true, 1077 Parameters: []v1alpha1.HelmParameter{ 1078 { 1079 Name: "name-1", 1080 Value: "value-1", 1081 }, 1082 { 1083 Name: "name-2", 1084 Value: "value-2", 1085 }, 1086 }, 1087 PassCredentials: true, 1088 ValuesObject: &runtime.RawExtension{Raw: []byte("some: yaml")}, 1089 ValueFiles: []string{ 1090 "values-1.yaml", 1091 "values-2.yaml", 1092 }, 1093 }, 1094 } 1095 1096 pluginSource := &v1alpha1.ApplicationSource{ 1097 Plugin: &v1alpha1.ApplicationSourcePlugin{ 1098 Env: v1alpha1.Env{ 1099 { 1100 Name: "env-1", 1101 Value: "env-value-1", 1102 }, 1103 { 1104 Name: "env-2", 1105 Value: "env-value-2", 1106 }, 1107 }, 1108 }, 1109 } 1110 1111 assert.Equal(t, "some-prefix", kustomizeSource.Kustomize.NamePrefix) 1112 updated, nothingToUnset := unset(kustomizeSource, unsetOpts{namePrefix: true}) 1113 assert.Empty(t, kustomizeSource.Kustomize.NamePrefix) 1114 assert.True(t, updated) 1115 assert.False(t, nothingToUnset) 1116 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{namePrefix: true}) 1117 assert.False(t, updated) 1118 assert.False(t, nothingToUnset) 1119 1120 assert.Equal(t, "some-suffix", kustomizeSource.Kustomize.NameSuffix) 1121 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{nameSuffix: true}) 1122 assert.Empty(t, kustomizeSource.Kustomize.NameSuffix) 1123 assert.True(t, updated) 1124 assert.False(t, nothingToUnset) 1125 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{nameSuffix: true}) 1126 assert.False(t, updated) 1127 assert.False(t, nothingToUnset) 1128 1129 assert.Equal(t, "123", kustomizeSource.Kustomize.Version) 1130 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeVersion: true}) 1131 assert.Empty(t, kustomizeSource.Kustomize.Version) 1132 assert.True(t, updated) 1133 assert.False(t, nothingToUnset) 1134 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeVersion: true}) 1135 assert.False(t, updated) 1136 assert.False(t, nothingToUnset) 1137 1138 assert.Len(t, kustomizeSource.Kustomize.Images, 2) 1139 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeImages: []string{"old1=new:tag"}}) 1140 assert.Len(t, kustomizeSource.Kustomize.Images, 1) 1141 assert.True(t, updated) 1142 assert.False(t, nothingToUnset) 1143 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeImages: []string{"old1=new:tag"}}) 1144 assert.False(t, updated) 1145 assert.False(t, nothingToUnset) 1146 1147 assert.Len(t, kustomizeSource.Kustomize.Replicas, 2) 1148 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}}) 1149 assert.Len(t, kustomizeSource.Kustomize.Replicas, 1) 1150 assert.True(t, updated) 1151 assert.False(t, nothingToUnset) 1152 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{kustomizeReplicas: []string{"my-deployment"}}) 1153 assert.False(t, updated) 1154 assert.False(t, nothingToUnset) 1155 1156 assert.True(t, kustomizeSource.Kustomize.IgnoreMissingComponents) 1157 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{ignoreMissingComponents: true}) 1158 assert.False(t, kustomizeSource.Kustomize.IgnoreMissingComponents) 1159 assert.True(t, updated) 1160 assert.False(t, nothingToUnset) 1161 updated, nothingToUnset = unset(kustomizeSource, unsetOpts{ignoreMissingComponents: true}) 1162 assert.False(t, updated) 1163 assert.False(t, nothingToUnset) 1164 1165 assert.Len(t, helmSource.Helm.Parameters, 2) 1166 updated, nothingToUnset = unset(helmSource, unsetOpts{parameters: []string{"name-1"}}) 1167 assert.Len(t, helmSource.Helm.Parameters, 1) 1168 assert.True(t, updated) 1169 assert.False(t, nothingToUnset) 1170 updated, nothingToUnset = unset(helmSource, unsetOpts{parameters: []string{"name-1"}}) 1171 assert.False(t, updated) 1172 assert.False(t, nothingToUnset) 1173 1174 assert.Len(t, helmSource.Helm.ValueFiles, 2) 1175 updated, nothingToUnset = unset(helmSource, unsetOpts{valuesFiles: []string{"values-1.yaml"}}) 1176 assert.Len(t, helmSource.Helm.ValueFiles, 1) 1177 assert.True(t, updated) 1178 assert.False(t, nothingToUnset) 1179 updated, nothingToUnset = unset(helmSource, unsetOpts{valuesFiles: []string{"values-1.yaml"}}) 1180 assert.False(t, updated) 1181 assert.False(t, nothingToUnset) 1182 1183 assert.Equal(t, "some: yaml", helmSource.Helm.ValuesString()) 1184 updated, nothingToUnset = unset(helmSource, unsetOpts{valuesLiteral: true}) 1185 assert.Empty(t, helmSource.Helm.ValuesString()) 1186 assert.True(t, updated) 1187 assert.False(t, nothingToUnset) 1188 updated, nothingToUnset = unset(helmSource, unsetOpts{valuesLiteral: true}) 1189 assert.False(t, updated) 1190 assert.False(t, nothingToUnset) 1191 1192 assert.True(t, helmSource.Helm.IgnoreMissingValueFiles) 1193 updated, nothingToUnset = unset(helmSource, unsetOpts{ignoreMissingValueFiles: true}) 1194 assert.False(t, helmSource.Helm.IgnoreMissingValueFiles) 1195 assert.True(t, updated) 1196 assert.False(t, nothingToUnset) 1197 updated, nothingToUnset = unset(helmSource, unsetOpts{ignoreMissingValueFiles: true}) 1198 assert.False(t, updated) 1199 assert.False(t, nothingToUnset) 1200 1201 assert.True(t, helmSource.Helm.PassCredentials) 1202 updated, nothingToUnset = unset(helmSource, unsetOpts{passCredentials: true}) 1203 assert.False(t, helmSource.Helm.PassCredentials) 1204 assert.True(t, updated) 1205 assert.False(t, nothingToUnset) 1206 updated, nothingToUnset = unset(helmSource, unsetOpts{passCredentials: true}) 1207 assert.False(t, updated) 1208 assert.False(t, nothingToUnset) 1209 1210 assert.Len(t, pluginSource.Plugin.Env, 2) 1211 updated, nothingToUnset = unset(pluginSource, unsetOpts{pluginEnvs: []string{"env-1"}}) 1212 assert.Len(t, pluginSource.Plugin.Env, 1) 1213 assert.True(t, updated) 1214 assert.False(t, nothingToUnset) 1215 updated, nothingToUnset = unset(pluginSource, unsetOpts{pluginEnvs: []string{"env-1"}}) 1216 assert.False(t, updated) 1217 assert.False(t, nothingToUnset) 1218 } 1219 1220 func Test_unset_nothingToUnset(t *testing.T) { 1221 t.Parallel() 1222 1223 testCases := []struct { 1224 name string 1225 source v1alpha1.ApplicationSource 1226 }{ 1227 {"kustomize", v1alpha1.ApplicationSource{Kustomize: &v1alpha1.ApplicationSourceKustomize{}}}, 1228 {"helm", v1alpha1.ApplicationSource{Helm: &v1alpha1.ApplicationSourceHelm{}}}, 1229 {"plugin", v1alpha1.ApplicationSource{Plugin: &v1alpha1.ApplicationSourcePlugin{}}}, 1230 } 1231 1232 for _, testCase := range testCases { 1233 testCaseCopy := testCase 1234 1235 t.Run(testCaseCopy.name, func(t *testing.T) { 1236 t.Parallel() 1237 1238 updated, nothingToUnset := unset(&testCaseCopy.source, unsetOpts{}) 1239 assert.False(t, updated) 1240 assert.True(t, nothingToUnset) 1241 }) 1242 } 1243 } 1244 1245 func TestFilterAppResources(t *testing.T) { 1246 // App resources 1247 var ( 1248 appReplicaSet1 = v1alpha1.ResourceStatus{ 1249 Group: "apps", 1250 Kind: "ReplicaSet", 1251 Namespace: "default", 1252 Name: "replicaSet-name1", 1253 } 1254 appReplicaSet2 = v1alpha1.ResourceStatus{ 1255 Group: "apps", 1256 Kind: "ReplicaSet", 1257 Namespace: "default", 1258 Name: "replicaSet-name2", 1259 } 1260 appJob = v1alpha1.ResourceStatus{ 1261 Group: "batch", 1262 Kind: "Job", 1263 Namespace: "default", 1264 Name: "job-name", 1265 } 1266 appService1 = v1alpha1.ResourceStatus{ 1267 Group: "", 1268 Kind: "Service", 1269 Namespace: "default", 1270 Name: "service-name1", 1271 } 1272 appService2 = v1alpha1.ResourceStatus{ 1273 Group: "", 1274 Kind: "Service", 1275 Namespace: "default", 1276 Name: "service-name2", 1277 } 1278 appDeployment = v1alpha1.ResourceStatus{ 1279 Group: "apps", 1280 Kind: "Deployment", 1281 Namespace: "default", 1282 Name: "deployment-name", 1283 } 1284 ) 1285 app := v1alpha1.Application{ 1286 Status: v1alpha1.ApplicationStatus{ 1287 Resources: []v1alpha1.ResourceStatus{ 1288 appReplicaSet1, appReplicaSet2, appJob, appService1, appService2, appDeployment, 1289 }, 1290 }, 1291 } 1292 // Resource filters 1293 var ( 1294 blankValues = v1alpha1.SyncOperationResource{ 1295 Group: "", 1296 Kind: "", 1297 Name: "", 1298 Namespace: "", 1299 Exclude: false, 1300 } 1301 // *:*:* 1302 includeAllResources = v1alpha1.SyncOperationResource{ 1303 Group: "*", 1304 Kind: "*", 1305 Name: "*", 1306 Namespace: "", 1307 Exclude: false, 1308 } 1309 // !*:*:* 1310 excludeAllResources = v1alpha1.SyncOperationResource{ 1311 Group: "*", 1312 Kind: "*", 1313 Name: "*", 1314 Namespace: "", 1315 Exclude: true, 1316 } 1317 // *:Service:* 1318 includeAllServiceResources = v1alpha1.SyncOperationResource{ 1319 Group: "*", 1320 Kind: "Service", 1321 Name: "*", 1322 Namespace: "", 1323 Exclude: false, 1324 } 1325 // !*:Service:* 1326 excludeAllServiceResources = v1alpha1.SyncOperationResource{ 1327 Group: "*", 1328 Kind: "Service", 1329 Name: "*", 1330 Namespace: "", 1331 Exclude: true, 1332 } 1333 // apps:ReplicaSet:* 1334 includeAllReplicaSetResource = v1alpha1.SyncOperationResource{ 1335 Group: "apps", 1336 Kind: "ReplicaSet", 1337 Name: "*", 1338 Namespace: "", 1339 Exclude: false, 1340 } 1341 // apps:ReplicaSet:replicaSet-name1 1342 includeReplicaSet1Resource = v1alpha1.SyncOperationResource{ 1343 Group: "apps", 1344 Kind: "ReplicaSet", 1345 Name: "replicaSet-name1", 1346 Namespace: "", 1347 Exclude: false, 1348 } 1349 // !apps:ReplicaSet:replicaSet-name2 1350 excludeReplicaSet2Resource = v1alpha1.SyncOperationResource{ 1351 Group: "apps", 1352 Kind: "ReplicaSet", 1353 Name: "replicaSet-name2", 1354 Namespace: "", 1355 Exclude: true, 1356 } 1357 ) 1358 1359 // Filtered resources 1360 var ( 1361 replicaSet1 = v1alpha1.SyncOperationResource{ 1362 Group: "apps", 1363 Kind: "ReplicaSet", 1364 Namespace: "default", 1365 Name: "replicaSet-name1", 1366 } 1367 replicaSet2 = v1alpha1.SyncOperationResource{ 1368 Group: "apps", 1369 Kind: "ReplicaSet", 1370 Namespace: "default", 1371 Name: "replicaSet-name2", 1372 } 1373 job = v1alpha1.SyncOperationResource{ 1374 Group: "batch", 1375 Kind: "Job", 1376 Namespace: "default", 1377 Name: "job-name", 1378 } 1379 service1 = v1alpha1.SyncOperationResource{ 1380 Group: "", 1381 Kind: "Service", 1382 Namespace: "default", 1383 Name: "service-name1", 1384 } 1385 service2 = v1alpha1.SyncOperationResource{ 1386 Group: "", 1387 Kind: "Service", 1388 Namespace: "default", 1389 Name: "service-name2", 1390 } 1391 deployment = v1alpha1.SyncOperationResource{ 1392 Group: "apps", 1393 Kind: "Deployment", 1394 Namespace: "default", 1395 Name: "deployment-name", 1396 } 1397 ) 1398 tests := []struct { 1399 testName string 1400 selectedResources []*v1alpha1.SyncOperationResource 1401 expectedResult []*v1alpha1.SyncOperationResource 1402 }{ 1403 // --resource apps:ReplicaSet:replicaSet-name1 --resource *:Service:* 1404 { 1405 testName: "Include ReplicaSet replicaSet-name1 resource and all service resources", 1406 selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources, &includeReplicaSet1Resource}, 1407 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &service1, &service2}, 1408 }, 1409 // --resource apps:ReplicaSet:replicaSet-name1 --resource !*:Service:* 1410 { 1411 testName: "Include ReplicaSet replicaSet-name1 resource and exclude all service resources", 1412 selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources, &includeReplicaSet1Resource}, 1413 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, 1414 }, 1415 // --resource !apps:ReplicaSet:replicaSet-name2 --resource !*:Service:* 1416 { 1417 testName: "Exclude ReplicaSet replicaSet-name2 resource and all service resources", 1418 selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource, &excludeAllServiceResources}, 1419 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &deployment}, 1420 }, 1421 // --resource !apps:ReplicaSet:replicaSet-name2 1422 { 1423 testName: "Exclude ReplicaSet replicaSet-name2 resource", 1424 selectedResources: []*v1alpha1.SyncOperationResource{&excludeReplicaSet2Resource}, 1425 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &job, &service1, &service2, &deployment}, 1426 }, 1427 // --resource apps:ReplicaSet:replicaSet-name1 1428 { 1429 testName: "Include ReplicaSet replicaSet-name1 resource", 1430 selectedResources: []*v1alpha1.SyncOperationResource{&includeReplicaSet1Resource}, 1431 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, 1432 }, 1433 // --resource apps:ReplicaSet:* --resource !apps:ReplicaSet:replicaSet-name2 1434 { 1435 testName: "Include All ReplicaSet resource and exclude replicaSet-name1 resource", 1436 selectedResources: []*v1alpha1.SyncOperationResource{&includeAllReplicaSetResource, &excludeReplicaSet2Resource}, 1437 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1}, 1438 }, 1439 // --resource !*:Service:* 1440 { 1441 testName: "Exclude Service resources", 1442 selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllServiceResources}, 1443 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &deployment}, 1444 }, 1445 // --resource *:Service:* 1446 { 1447 testName: "Include Service resources", 1448 selectedResources: []*v1alpha1.SyncOperationResource{&includeAllServiceResources}, 1449 expectedResult: []*v1alpha1.SyncOperationResource{&service1, &service2}, 1450 }, 1451 // --resource !*:*:* 1452 { 1453 testName: "Exclude all resources", 1454 selectedResources: []*v1alpha1.SyncOperationResource{&excludeAllResources}, 1455 expectedResult: nil, 1456 }, 1457 // --resource *:*:* 1458 { 1459 testName: "Include all resources", 1460 selectedResources: []*v1alpha1.SyncOperationResource{&includeAllResources}, 1461 expectedResult: []*v1alpha1.SyncOperationResource{&replicaSet1, &replicaSet2, &job, &service1, &service2, &deployment}, 1462 }, 1463 { 1464 testName: "No Filters", 1465 selectedResources: []*v1alpha1.SyncOperationResource{&blankValues}, 1466 expectedResult: nil, 1467 }, 1468 { 1469 testName: "Empty Filter", 1470 selectedResources: []*v1alpha1.SyncOperationResource{}, 1471 expectedResult: nil, 1472 }, 1473 } 1474 1475 for _, test := range tests { 1476 t.Run(test.testName, func(t *testing.T) { 1477 filteredResources := filterAppResources(&app, test.selectedResources) 1478 assert.Equal(t, test.expectedResult, filteredResources) 1479 }) 1480 } 1481 } 1482 1483 func TestParseSelectedResources(t *testing.T) { 1484 resources := []string{ 1485 "v1alpha:Application:test", 1486 "v1alpha:Application:namespace/test", 1487 "!v1alpha:Application:test", 1488 "apps:Deployment:default/test", 1489 "!*:*:*", 1490 } 1491 operationResources, err := parseSelectedResources(resources) 1492 require.NoError(t, err) 1493 assert.Len(t, operationResources, 5) 1494 assert.Equal(t, v1alpha1.SyncOperationResource{ 1495 Namespace: "", 1496 Name: "test", 1497 Kind: application.ApplicationKind, 1498 Group: "v1alpha", 1499 }, *operationResources[0]) 1500 assert.Equal(t, v1alpha1.SyncOperationResource{ 1501 Namespace: "namespace", 1502 Name: "test", 1503 Kind: application.ApplicationKind, 1504 Group: "v1alpha", 1505 }, *operationResources[1]) 1506 assert.Equal(t, v1alpha1.SyncOperationResource{ 1507 Namespace: "", 1508 Name: "test", 1509 Kind: "Application", 1510 Group: "v1alpha", 1511 Exclude: true, 1512 }, *operationResources[2]) 1513 assert.Equal(t, v1alpha1.SyncOperationResource{ 1514 Namespace: "default", 1515 Name: "test", 1516 Kind: "Deployment", 1517 Group: "apps", 1518 Exclude: false, 1519 }, *operationResources[3]) 1520 assert.Equal(t, v1alpha1.SyncOperationResource{ 1521 Namespace: "", 1522 Name: "*", 1523 Kind: "*", 1524 Group: "*", 1525 Exclude: true, 1526 }, *operationResources[4]) 1527 } 1528 1529 func TestParseSelectedResourcesIncorrect(t *testing.T) { 1530 resources := []string{"v1alpha:test", "v1alpha:Application:namespace/test"} 1531 _, err := parseSelectedResources(resources) 1532 assert.ErrorContains(t, err, "v1alpha:test") 1533 } 1534 1535 func TestParseSelectedResourcesIncorrectNamespace(t *testing.T) { 1536 resources := []string{"v1alpha:Application:namespace/test/unknown"} 1537 _, err := parseSelectedResources(resources) 1538 assert.ErrorContains(t, err, "v1alpha:Application:namespace/test/unknown") 1539 } 1540 1541 func TestParseSelectedResourcesEmptyList(t *testing.T) { 1542 var resources []string 1543 operationResources, err := parseSelectedResources(resources) 1544 require.NoError(t, err) 1545 assert.Empty(t, operationResources) 1546 } 1547 1548 func TestPrintApplicationTableNotWide(t *testing.T) { 1549 output, err := captureOutput(func() error { 1550 app := &v1alpha1.Application{ 1551 ObjectMeta: metav1.ObjectMeta{ 1552 Name: "app-name", 1553 }, 1554 Spec: v1alpha1.ApplicationSpec{ 1555 Destination: v1alpha1.ApplicationDestination{ 1556 Server: "http://localhost:8080", 1557 Namespace: "default", 1558 }, 1559 Project: "prj", 1560 }, 1561 Status: v1alpha1.ApplicationStatus{ 1562 Sync: v1alpha1.SyncStatus{ 1563 Status: "OutOfSync", 1564 }, 1565 Health: v1alpha1.AppHealthStatus{ 1566 Status: "Healthy", 1567 }, 1568 }, 1569 } 1570 output := "table" 1571 printApplicationTable([]v1alpha1.Application{*app, *app}, &output) 1572 return nil 1573 }) 1574 require.NoError(t, err) 1575 expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none>\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none>\n" 1576 assert.Equal(t, output, expectation) 1577 } 1578 1579 func TestPrintApplicationTableWide(t *testing.T) { 1580 output, err := captureOutput(func() error { 1581 app := &v1alpha1.Application{ 1582 ObjectMeta: metav1.ObjectMeta{ 1583 Name: "app-name", 1584 }, 1585 Spec: v1alpha1.ApplicationSpec{ 1586 Destination: v1alpha1.ApplicationDestination{ 1587 Server: "http://localhost:8080", 1588 Namespace: "default", 1589 }, 1590 Source: &v1alpha1.ApplicationSource{ 1591 RepoURL: "https://github.com/argoproj/argocd-example-apps", 1592 Path: "guestbook", 1593 TargetRevision: "123", 1594 }, 1595 Project: "prj", 1596 }, 1597 Status: v1alpha1.ApplicationStatus{ 1598 Sync: v1alpha1.SyncStatus{ 1599 Status: "OutOfSync", 1600 }, 1601 Health: v1alpha1.AppHealthStatus{ 1602 Status: "Healthy", 1603 }, 1604 }, 1605 } 1606 output := "wide" 1607 printApplicationTable([]v1alpha1.Application{*app, *app}, &output) 1608 return nil 1609 }) 1610 require.NoError(t, err) 1611 expectation := "NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none> https://github.com/argoproj/argocd-example-apps guestbook 123\napp-name http://localhost:8080 default prj OutOfSync Healthy Manual <none> https://github.com/argoproj/argocd-example-apps guestbook 123\n" 1612 assert.Equal(t, output, expectation) 1613 } 1614 1615 func TestResourceStateKey(t *testing.T) { 1616 rst := resourceState{ 1617 Group: "group", 1618 Kind: "kind", 1619 Namespace: "namespace", 1620 Name: "name", 1621 } 1622 1623 key := rst.Key() 1624 assert.Equal(t, "group/kind/namespace/name", key) 1625 } 1626 1627 func TestFormatItems(t *testing.T) { 1628 rst := resourceState{ 1629 Group: "group", 1630 Kind: "kind", 1631 Namespace: "namespace", 1632 Name: "name", 1633 Status: "status", 1634 Health: "health", 1635 Hook: "hook", 1636 Message: "message", 1637 } 1638 items := rst.FormatItems() 1639 assert.Equal(t, "group", items[1]) 1640 assert.Equal(t, "kind", items[2]) 1641 assert.Equal(t, "namespace", items[3]) 1642 assert.Equal(t, "name", items[4]) 1643 assert.Equal(t, "status", items[5]) 1644 assert.Equal(t, "health", items[6]) 1645 assert.Equal(t, "hook", items[7]) 1646 assert.Equal(t, "message", items[8]) 1647 } 1648 1649 func TestMerge(t *testing.T) { 1650 rst := resourceState{ 1651 Group: "group", 1652 Kind: "kind", 1653 Namespace: "namespace", 1654 Name: "name", 1655 Status: "status", 1656 Health: "health", 1657 Hook: "hook", 1658 Message: "message", 1659 } 1660 1661 rstNew := resourceState{ 1662 Group: "group", 1663 Kind: "kind", 1664 Namespace: "namespace", 1665 Name: "name", 1666 Status: "status", 1667 Health: "health", 1668 Hook: "hook2", 1669 Message: "message2", 1670 } 1671 1672 updated := rst.Merge(&rstNew) 1673 assert.True(t, updated) 1674 assert.Equal(t, rstNew.Hook, rst.Hook) 1675 assert.Equal(t, rstNew.Message, rst.Message) 1676 assert.Equal(t, rstNew.Status, rst.Status) 1677 } 1678 1679 func TestMergeWitoutUpdate(t *testing.T) { 1680 rst := resourceState{ 1681 Group: "group", 1682 Kind: "kind", 1683 Namespace: "namespace", 1684 Name: "name", 1685 Status: "status", 1686 Health: "health", 1687 Hook: "hook", 1688 Message: "message", 1689 } 1690 1691 rstNew := resourceState{ 1692 Group: "group", 1693 Kind: "kind", 1694 Namespace: "namespace", 1695 Name: "name", 1696 Status: "status", 1697 Health: "health", 1698 Hook: "hook", 1699 Message: "message", 1700 } 1701 1702 updated := rst.Merge(&rstNew) 1703 assert.False(t, updated) 1704 } 1705 1706 func TestCheckResourceStatus(t *testing.T) { 1707 t.Run("Degraded, Suspended and health status passed", func(t *testing.T) { 1708 res := checkResourceStatus(watchOpts{ 1709 suspended: true, 1710 health: true, 1711 degraded: true, 1712 }, string(health.HealthStatusHealthy), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1713 assert.True(t, res) 1714 }) 1715 t.Run("Degraded, Suspended and health status failed", func(t *testing.T) { 1716 res := checkResourceStatus(watchOpts{ 1717 suspended: true, 1718 health: true, 1719 degraded: true, 1720 }, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1721 assert.False(t, res) 1722 }) 1723 t.Run("Suspended and health status passed", func(t *testing.T) { 1724 res := checkResourceStatus(watchOpts{ 1725 suspended: true, 1726 health: true, 1727 }, string(health.HealthStatusHealthy), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1728 assert.True(t, res) 1729 }) 1730 t.Run("Suspended and health status failed", func(t *testing.T) { 1731 res := checkResourceStatus(watchOpts{ 1732 suspended: true, 1733 health: true, 1734 }, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1735 assert.False(t, res) 1736 }) 1737 t.Run("Suspended passed", func(t *testing.T) { 1738 res := checkResourceStatus(watchOpts{ 1739 suspended: true, 1740 health: false, 1741 }, string(health.HealthStatusSuspended), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1742 assert.True(t, res) 1743 }) 1744 t.Run("Suspended failed", func(t *testing.T) { 1745 res := checkResourceStatus(watchOpts{ 1746 suspended: true, 1747 health: false, 1748 }, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1749 assert.False(t, res) 1750 }) 1751 t.Run("Health passed", func(t *testing.T) { 1752 res := checkResourceStatus(watchOpts{ 1753 suspended: false, 1754 health: true, 1755 }, string(health.HealthStatusHealthy), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1756 assert.True(t, res) 1757 }) 1758 t.Run("Health failed", func(t *testing.T) { 1759 res := checkResourceStatus(watchOpts{ 1760 suspended: false, 1761 health: true, 1762 }, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1763 assert.False(t, res) 1764 }) 1765 t.Run("Synced passed", func(t *testing.T) { 1766 res := checkResourceStatus(watchOpts{}, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1767 assert.True(t, res) 1768 }) 1769 t.Run("Synced failed", func(t *testing.T) { 1770 res := checkResourceStatus(watchOpts{}, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeOutOfSync), &v1alpha1.Operation{}, true) 1771 assert.True(t, res) 1772 }) 1773 t.Run("Degraded passed", func(t *testing.T) { 1774 res := checkResourceStatus(watchOpts{ 1775 suspended: false, 1776 health: false, 1777 degraded: true, 1778 }, string(health.HealthStatusDegraded), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1779 assert.True(t, res) 1780 }) 1781 t.Run("Degraded failed", func(t *testing.T) { 1782 res := checkResourceStatus(watchOpts{ 1783 suspended: false, 1784 health: false, 1785 degraded: true, 1786 }, string(health.HealthStatusProgressing), string(v1alpha1.SyncStatusCodeSynced), &v1alpha1.Operation{}, true) 1787 assert.False(t, res) 1788 }) 1789 } 1790 1791 func Test_hasAppChanged(t *testing.T) { 1792 type args struct { 1793 appReq *v1alpha1.Application 1794 appRes *v1alpha1.Application 1795 upsert bool 1796 } 1797 tests := []struct { 1798 name string 1799 args args 1800 want bool 1801 }{ 1802 { 1803 name: "App has changed - Labels, Annotations, Finalizers empty", 1804 args: args{ 1805 appReq: testApp("foo", "default", map[string]string{}, map[string]string{}, []string{}), 1806 appRes: testApp("foo", "foo", nil, nil, nil), 1807 upsert: true, 1808 }, 1809 want: true, 1810 }, 1811 { 1812 name: "App unchanged - Labels, Annotations, Finalizers populated", 1813 args: args{ 1814 appReq: testApp("foo", "default", map[string]string{"foo": "bar"}, map[string]string{"foo": "bar"}, []string{"foo"}), 1815 appRes: testApp("foo", "default", map[string]string{"foo": "bar"}, map[string]string{"foo": "bar"}, []string{"foo"}), 1816 upsert: true, 1817 }, 1818 want: false, 1819 }, 1820 { 1821 name: "Apps unchanged - Using empty maps/list locally versus server returning nil", 1822 args: args{ 1823 appReq: testApp("foo", "default", map[string]string{}, map[string]string{}, []string{}), 1824 appRes: testApp("foo", "default", nil, nil, nil), 1825 upsert: true, 1826 }, 1827 want: false, 1828 }, 1829 { 1830 name: "App unchanged - Using empty project locally versus server returning default", 1831 args: args{ 1832 appReq: testApp("foo", "", map[string]string{}, map[string]string{}, []string{}), 1833 appRes: testApp("foo", "default", nil, nil, nil), 1834 }, 1835 want: false, 1836 }, 1837 { 1838 name: "App unchanged - From upsert=false", 1839 args: args{ 1840 appReq: testApp("foo", "foo", map[string]string{}, map[string]string{}, []string{}), 1841 appRes: testApp("foo", "default", nil, nil, nil), 1842 upsert: false, 1843 }, 1844 want: false, 1845 }, 1846 } 1847 for _, tt := range tests { 1848 t.Run(tt.name, func(t *testing.T) { 1849 got := hasAppChanged(tt.args.appReq, tt.args.appRes, tt.args.upsert) 1850 assert.Equalf(t, tt.want, got, "hasAppChanged() = %v, want %v", got, tt.want) 1851 }) 1852 } 1853 } 1854 1855 func testApp(name, project string, labels map[string]string, annotations map[string]string, finalizers []string) *v1alpha1.Application { 1856 return &v1alpha1.Application{ 1857 ObjectMeta: metav1.ObjectMeta{ 1858 Name: name, 1859 Labels: labels, 1860 Annotations: annotations, 1861 Finalizers: finalizers, 1862 }, 1863 Spec: v1alpha1.ApplicationSpec{ 1864 Source: &v1alpha1.ApplicationSource{ 1865 RepoURL: "https://github.com/argoproj/argocd-example-apps.git", 1866 }, 1867 Project: project, 1868 }, 1869 } 1870 } 1871 1872 func TestWaitOnApplicationStatus_JSON_YAML_WideOutput(t *testing.T) { 1873 acdClient := &customAcdClient{&fakeAcdClient{}} 1874 ctx := t.Context() 1875 var selectResource []*v1alpha1.SyncOperationResource 1876 watch := watchOpts{ 1877 sync: false, 1878 health: false, 1879 operation: true, 1880 suspended: false, 1881 } 1882 watch = getWatchOpts(watch) 1883 1884 output, err := captureOutput(func() error { 1885 _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 0, watch, selectResource, "json") 1886 return nil 1887 }, 1888 ) 1889 require.NoError(t, err) 1890 assert.True(t, json.Valid([]byte(output))) 1891 1892 output, err = captureOutput(func() error { 1893 _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 0, watch, selectResource, "yaml") 1894 return nil 1895 }) 1896 1897 require.NoError(t, err) 1898 err = yaml.Unmarshal([]byte(output), &v1alpha1.Application{}) 1899 require.NoError(t, err) 1900 1901 output, _ = captureOutput(func() error { 1902 _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 0, watch, selectResource, "") 1903 return nil 1904 }) 1905 timeStr := time.Now().Format("2006-01-02T15:04:05-07:00") 1906 1907 expectation := `TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE 1908 %s Service default service-name1 Synced Healthy 1909 %s apps Deployment default test Synced Healthy 1910 1911 Name: argocd/test 1912 Project: default 1913 Server: local 1914 Namespace: argocd 1915 URL: http://localhost:8080/applications/app-name 1916 Source: 1917 - Repo: test 1918 Target: master 1919 Path: /test 1920 Helm Values: path1,path2 1921 Name Prefix: prefix 1922 SyncWindow: Sync Allowed 1923 Sync Policy: Automated (Prune) 1924 Sync Status: OutOfSync from master 1925 Health Status: Progressing 1926 1927 Operation: Sync 1928 Sync Revision: revision 1929 Phase: 1930 Start: 0001-01-01 00:00:00 +0000 UTC 1931 Finished: 2020-11-10 23:00:00 +0000 UTC 1932 Duration: 2333448h16m18.871345152s 1933 Message: test 1934 1935 GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE 1936 Service default service-name1 Synced Healthy 1937 apps Deployment default test Synced Healthy 1938 ` 1939 expectation = fmt.Sprintf(expectation, timeStr, timeStr) 1940 expectationParts := strings.Split(expectation, "\n") 1941 slices.Sort(expectationParts) 1942 expectationSorted := strings.Join(expectationParts, "\n") 1943 outputParts := strings.Split(output, "\n") 1944 slices.Sort(outputParts) 1945 outputSorted := strings.Join(outputParts, "\n") 1946 // Need to compare sorted since map entries may not keep a specific order during serialization, leading to flakiness. 1947 assert.Equalf(t, expectationSorted, outputSorted, "Incorrect output %q, should be %q (items order doesn't matter)", output, expectation) 1948 } 1949 1950 func TestWaitOnApplicationStatus_JSON_YAML_WideOutput_With_Timeout(t *testing.T) { 1951 acdClient := &customAcdClient{&fakeAcdClient{simulateTimeout: 15}} 1952 ctx := t.Context() 1953 var selectResource []*v1alpha1.SyncOperationResource 1954 watch := watchOpts{ 1955 sync: false, 1956 health: false, 1957 operation: true, 1958 suspended: false, 1959 } 1960 watch = getWatchOpts(watch) 1961 1962 output, _ := captureOutput(func() error { 1963 _, _, _ = waitOnApplicationStatus(ctx, acdClient, "app-name", 5, watch, selectResource, "") 1964 return nil 1965 }) 1966 timeStr := time.Now().Format("2006-01-02T15:04:05-07:00") 1967 1968 expectation := `TIMESTAMP GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE 1969 %s Service default service-name1 Synced Healthy 1970 %s apps Deployment default test Synced Healthy 1971 1972 The command timed out waiting for the conditions to be met. 1973 1974 This is the state of the app after wait timed out: 1975 1976 Name: argocd/test 1977 Project: default 1978 Server: local 1979 Namespace: argocd 1980 URL: http://localhost:8080/applications/app-name 1981 Source: 1982 - Repo: test 1983 Target: master 1984 Path: /test 1985 Helm Values: path1,path2 1986 Name Prefix: prefix 1987 SyncWindow: Sync Allowed 1988 Sync Policy: Automated (Prune) 1989 Sync Status: OutOfSync from master 1990 Health Status: Progressing 1991 1992 Operation: Sync 1993 Sync Revision: revision 1994 Phase: 1995 Start: 0001-01-01 00:00:00 +0000 UTC 1996 Finished: 2020-11-10 23:00:00 +0000 UTC 1997 Duration: 2333448h16m18.871345152s 1998 Message: test 1999 2000 GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE 2001 Service default service-name1 Synced Healthy 2002 apps Deployment default test Synced Healthy 2003 ` 2004 expectation = fmt.Sprintf(expectation, timeStr, timeStr) 2005 expectationParts := strings.Split(expectation, "\n") 2006 slices.Sort(expectationParts) 2007 expectationSorted := strings.Join(expectationParts, "\n") 2008 outputParts := strings.Split(output, "\n") 2009 slices.Sort(outputParts) 2010 outputSorted := strings.Join(outputParts, "\n") 2011 // Need to compare sorted since map entries may not keep a specific order during serialization, leading to flakiness. 2012 assert.Equalf(t, expectationSorted, outputSorted, "Incorrect output %q, should be %q (items order doesn't matter)", output, expectation) 2013 } 2014 2015 type customAcdClient struct { 2016 *fakeAcdClient 2017 } 2018 2019 func (c *customAcdClient) WatchApplicationWithRetry(ctx context.Context, _ string, _ string) chan *v1alpha1.ApplicationWatchEvent { 2020 appEventsCh := make(chan *v1alpha1.ApplicationWatchEvent) 2021 _, appClient := c.NewApplicationClientOrDie() 2022 app, _ := appClient.Get(ctx, &applicationpkg.ApplicationQuery{}) 2023 2024 newApp := v1alpha1.Application{ 2025 TypeMeta: app.TypeMeta, 2026 ObjectMeta: app.ObjectMeta, 2027 Spec: app.Spec, 2028 Status: app.Status, 2029 Operation: app.Operation, 2030 } 2031 2032 go func() { 2033 time.Sleep(time.Duration(c.simulateTimeout) * time.Second) 2034 appEventsCh <- &v1alpha1.ApplicationWatchEvent{ 2035 Type: watch.Bookmark, 2036 Application: newApp, 2037 } 2038 close(appEventsCh) 2039 }() 2040 2041 return appEventsCh 2042 } 2043 2044 func (c *customAcdClient) NewApplicationClientOrDie() (io.Closer, applicationpkg.ApplicationServiceClient) { 2045 return &fakeConnection{}, &fakeAppServiceClient{} 2046 } 2047 2048 func (c *customAcdClient) NewSettingsClientOrDie() (io.Closer, settingspkg.SettingsServiceClient) { 2049 return &fakeConnection{}, &fakeSettingsServiceClient{} 2050 } 2051 2052 type fakeConnection struct{} 2053 2054 func (c *fakeConnection) Close() error { 2055 return nil 2056 } 2057 2058 type fakeSettingsServiceClient struct{} 2059 2060 func (f fakeSettingsServiceClient) Get(_ context.Context, _ *settingspkg.SettingsQuery, _ ...grpc.CallOption) (*settingspkg.Settings, error) { 2061 return &settingspkg.Settings{ 2062 URL: "http://localhost:8080", 2063 }, nil 2064 } 2065 2066 func (f fakeSettingsServiceClient) GetPlugins(_ context.Context, _ *settingspkg.SettingsQuery, _ ...grpc.CallOption) (*settingspkg.SettingsPluginsResponse, error) { 2067 return nil, nil 2068 } 2069 2070 type fakeAppServiceClient struct{} 2071 2072 func (c *fakeAppServiceClient) Get(_ context.Context, _ *applicationpkg.ApplicationQuery, _ ...grpc.CallOption) (*v1alpha1.Application, error) { 2073 time := metav1.Date(2020, time.November, 10, 23, 0, 0, 0, time.UTC) 2074 return &v1alpha1.Application{ 2075 ObjectMeta: metav1.ObjectMeta{ 2076 Name: "test", 2077 Namespace: "argocd", 2078 }, 2079 Spec: v1alpha1.ApplicationSpec{ 2080 SyncPolicy: &v1alpha1.SyncPolicy{ 2081 Automated: &v1alpha1.SyncPolicyAutomated{ 2082 Prune: true, 2083 }, 2084 }, 2085 Project: "default", 2086 Destination: v1alpha1.ApplicationDestination{Server: "local", Namespace: "argocd"}, 2087 Source: &v1alpha1.ApplicationSource{ 2088 RepoURL: "test", 2089 TargetRevision: "master", 2090 Path: "/test", 2091 Helm: &v1alpha1.ApplicationSourceHelm{ 2092 ValueFiles: []string{"path1", "path2"}, 2093 }, 2094 Kustomize: &v1alpha1.ApplicationSourceKustomize{NamePrefix: "prefix"}, 2095 }, 2096 }, 2097 Status: v1alpha1.ApplicationStatus{ 2098 Resources: []v1alpha1.ResourceStatus{ 2099 { 2100 Group: "", 2101 Kind: "Service", 2102 Namespace: "default", 2103 Name: "service-name1", 2104 Status: "Synced", 2105 Health: &v1alpha1.HealthStatus{ 2106 Status: health.HealthStatusHealthy, 2107 Message: "health-message", 2108 }, 2109 }, 2110 { 2111 Group: "apps", 2112 Kind: "Deployment", 2113 Namespace: "default", 2114 Name: "test", 2115 Status: "Synced", 2116 Health: &v1alpha1.HealthStatus{ 2117 Status: health.HealthStatusHealthy, 2118 Message: "health-message", 2119 }, 2120 }, 2121 }, 2122 OperationState: &v1alpha1.OperationState{ 2123 SyncResult: &v1alpha1.SyncOperationResult{ 2124 Revision: "revision", 2125 }, 2126 FinishedAt: &time, 2127 Message: "test", 2128 }, 2129 Sync: v1alpha1.SyncStatus{ 2130 Status: v1alpha1.SyncStatusCodeOutOfSync, 2131 }, 2132 Health: v1alpha1.AppHealthStatus{ 2133 Status: health.HealthStatusProgressing, 2134 }, 2135 }, 2136 }, nil 2137 } 2138 2139 func (c *fakeAppServiceClient) List(_ context.Context, _ *applicationpkg.ApplicationQuery, _ ...grpc.CallOption) (*v1alpha1.ApplicationList, error) { 2140 return nil, nil 2141 } 2142 2143 func (c *fakeAppServiceClient) ListResourceEvents(_ context.Context, _ *applicationpkg.ApplicationResourceEventsQuery, _ ...grpc.CallOption) (*corev1.EventList, error) { 2144 return nil, nil 2145 } 2146 2147 func (c *fakeAppServiceClient) Watch(_ context.Context, _ *applicationpkg.ApplicationQuery, _ ...grpc.CallOption) (applicationpkg.ApplicationService_WatchClient, error) { 2148 return nil, nil 2149 } 2150 2151 func (c *fakeAppServiceClient) Create(_ context.Context, _ *applicationpkg.ApplicationCreateRequest, _ ...grpc.CallOption) (*v1alpha1.Application, error) { 2152 return nil, nil 2153 } 2154 2155 func (c *fakeAppServiceClient) GetApplicationSyncWindows(_ context.Context, _ *applicationpkg.ApplicationSyncWindowsQuery, _ ...grpc.CallOption) (*applicationpkg.ApplicationSyncWindowsResponse, error) { 2156 return nil, nil 2157 } 2158 2159 func (c *fakeAppServiceClient) GetOCIMetadata(_ context.Context, _ *applicationpkg.RevisionMetadataQuery, _ ...grpc.CallOption) (*v1alpha1.OCIMetadata, error) { 2160 return nil, nil 2161 } 2162 2163 func (c *fakeAppServiceClient) RevisionMetadata(_ context.Context, _ *applicationpkg.RevisionMetadataQuery, _ ...grpc.CallOption) (*v1alpha1.RevisionMetadata, error) { 2164 return nil, nil 2165 } 2166 2167 func (c *fakeAppServiceClient) RevisionChartDetails(_ context.Context, _ *applicationpkg.RevisionMetadataQuery, _ ...grpc.CallOption) (*v1alpha1.ChartDetails, error) { 2168 return nil, nil 2169 } 2170 2171 func (c *fakeAppServiceClient) GetManifests(_ context.Context, _ *applicationpkg.ApplicationManifestQuery, _ ...grpc.CallOption) (*apiclient.ManifestResponse, error) { 2172 return nil, nil 2173 } 2174 2175 func (c *fakeAppServiceClient) GetManifestsWithFiles(_ context.Context, _ ...grpc.CallOption) (applicationpkg.ApplicationService_GetManifestsWithFilesClient, error) { 2176 return nil, nil 2177 } 2178 2179 func (c *fakeAppServiceClient) Update(_ context.Context, _ *applicationpkg.ApplicationUpdateRequest, _ ...grpc.CallOption) (*v1alpha1.Application, error) { 2180 return nil, nil 2181 } 2182 2183 func (c *fakeAppServiceClient) UpdateSpec(_ context.Context, _ *applicationpkg.ApplicationUpdateSpecRequest, _ ...grpc.CallOption) (*v1alpha1.ApplicationSpec, error) { 2184 return nil, nil 2185 } 2186 2187 func (c *fakeAppServiceClient) Patch(_ context.Context, _ *applicationpkg.ApplicationPatchRequest, _ ...grpc.CallOption) (*v1alpha1.Application, error) { 2188 return nil, nil 2189 } 2190 2191 func (c *fakeAppServiceClient) Delete(_ context.Context, _ *applicationpkg.ApplicationDeleteRequest, _ ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { 2192 return nil, nil 2193 } 2194 2195 func (c *fakeAppServiceClient) Sync(_ context.Context, _ *applicationpkg.ApplicationSyncRequest, _ ...grpc.CallOption) (*v1alpha1.Application, error) { 2196 return nil, nil 2197 } 2198 2199 func (c *fakeAppServiceClient) ManagedResources(_ context.Context, _ *applicationpkg.ResourcesQuery, _ ...grpc.CallOption) (*applicationpkg.ManagedResourcesResponse, error) { 2200 return nil, nil 2201 } 2202 2203 func (c *fakeAppServiceClient) ResourceTree(_ context.Context, _ *applicationpkg.ResourcesQuery, _ ...grpc.CallOption) (*v1alpha1.ApplicationTree, error) { 2204 return nil, nil 2205 } 2206 2207 func (c *fakeAppServiceClient) WatchResourceTree(_ context.Context, _ *applicationpkg.ResourcesQuery, _ ...grpc.CallOption) (applicationpkg.ApplicationService_WatchResourceTreeClient, error) { 2208 return nil, nil 2209 } 2210 2211 func (c *fakeAppServiceClient) Rollback(_ context.Context, _ *applicationpkg.ApplicationRollbackRequest, _ ...grpc.CallOption) (*v1alpha1.Application, error) { 2212 return nil, nil 2213 } 2214 2215 func (c *fakeAppServiceClient) TerminateOperation(_ context.Context, _ *applicationpkg.OperationTerminateRequest, _ ...grpc.CallOption) (*applicationpkg.OperationTerminateResponse, error) { 2216 return nil, nil 2217 } 2218 2219 func (c *fakeAppServiceClient) GetResource(_ context.Context, _ *applicationpkg.ApplicationResourceRequest, _ ...grpc.CallOption) (*applicationpkg.ApplicationResourceResponse, error) { 2220 return nil, nil 2221 } 2222 2223 func (c *fakeAppServiceClient) PatchResource(_ context.Context, _ *applicationpkg.ApplicationResourcePatchRequest, _ ...grpc.CallOption) (*applicationpkg.ApplicationResourceResponse, error) { 2224 return nil, nil 2225 } 2226 2227 func (c *fakeAppServiceClient) ListResourceActions(_ context.Context, _ *applicationpkg.ApplicationResourceRequest, _ ...grpc.CallOption) (*applicationpkg.ResourceActionsListResponse, error) { 2228 return nil, nil 2229 } 2230 2231 // nolint:staticcheck // ResourceActionRunRequest is deprecated, but we still need to implement it to satisfy the server interface. 2232 func (c *fakeAppServiceClient) RunResourceAction(_ context.Context, _ *applicationpkg.ResourceActionRunRequest, _ ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { 2233 return nil, nil 2234 } 2235 2236 func (c *fakeAppServiceClient) RunResourceActionV2(_ context.Context, _ *applicationpkg.ResourceActionRunRequestV2, _ ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { 2237 return nil, nil 2238 } 2239 2240 func (c *fakeAppServiceClient) DeleteResource(_ context.Context, _ *applicationpkg.ApplicationResourceDeleteRequest, _ ...grpc.CallOption) (*applicationpkg.ApplicationResponse, error) { 2241 return nil, nil 2242 } 2243 2244 func (c *fakeAppServiceClient) PodLogs(_ context.Context, _ *applicationpkg.ApplicationPodLogsQuery, _ ...grpc.CallOption) (applicationpkg.ApplicationService_PodLogsClient, error) { 2245 return nil, nil 2246 } 2247 2248 func (c *fakeAppServiceClient) ListLinks(_ context.Context, _ *applicationpkg.ListAppLinksRequest, _ ...grpc.CallOption) (*applicationpkg.LinksResponse, error) { 2249 return nil, nil 2250 } 2251 2252 func (c *fakeAppServiceClient) ListResourceLinks(_ context.Context, _ *applicationpkg.ApplicationResourceRequest, _ ...grpc.CallOption) (*applicationpkg.LinksResponse, error) { 2253 return nil, nil 2254 } 2255 2256 func (c *fakeAppServiceClient) ServerSideDiff(_ context.Context, _ *applicationpkg.ApplicationServerSideDiffQuery, _ ...grpc.CallOption) (*applicationpkg.ApplicationServerSideDiffResponse, error) { 2257 return nil, nil 2258 } 2259 2260 type fakeAcdClient struct { 2261 simulateTimeout uint 2262 } 2263 2264 func (c *fakeAcdClient) ClientOptions() argocdclient.ClientOptions { 2265 return argocdclient.ClientOptions{} 2266 } 2267 func (c *fakeAcdClient) HTTPClient() (*http.Client, error) { return nil, nil } 2268 func (c *fakeAcdClient) OIDCConfig(context.Context, *settingspkg.Settings) (*oauth2.Config, *oidc.Provider, error) { 2269 return nil, nil, nil 2270 } 2271 2272 func (c *fakeAcdClient) NewRepoClient() (io.Closer, repositorypkg.RepositoryServiceClient, error) { 2273 return nil, nil, nil 2274 } 2275 2276 func (c *fakeAcdClient) NewRepoClientOrDie() (io.Closer, repositorypkg.RepositoryServiceClient) { 2277 return nil, nil 2278 } 2279 2280 func (c *fakeAcdClient) NewRepoCredsClient() (io.Closer, repocredspkg.RepoCredsServiceClient, error) { 2281 return nil, nil, nil 2282 } 2283 2284 func (c *fakeAcdClient) NewRepoCredsClientOrDie() (io.Closer, repocredspkg.RepoCredsServiceClient) { 2285 return nil, nil 2286 } 2287 2288 func (c *fakeAcdClient) NewCertClient() (io.Closer, certificatepkg.CertificateServiceClient, error) { 2289 return nil, nil, nil 2290 } 2291 2292 func (c *fakeAcdClient) NewCertClientOrDie() (io.Closer, certificatepkg.CertificateServiceClient) { 2293 return nil, nil 2294 } 2295 2296 func (c *fakeAcdClient) NewClusterClient() (io.Closer, clusterpkg.ClusterServiceClient, error) { 2297 return nil, nil, nil 2298 } 2299 2300 func (c *fakeAcdClient) NewClusterClientOrDie() (io.Closer, clusterpkg.ClusterServiceClient) { 2301 return nil, nil 2302 } 2303 2304 func (c *fakeAcdClient) NewGPGKeyClient() (io.Closer, gpgkeypkg.GPGKeyServiceClient, error) { 2305 return nil, nil, nil 2306 } 2307 2308 func (c *fakeAcdClient) NewGPGKeyClientOrDie() (io.Closer, gpgkeypkg.GPGKeyServiceClient) { 2309 return nil, nil 2310 } 2311 2312 func (c *fakeAcdClient) NewApplicationClient() (io.Closer, applicationpkg.ApplicationServiceClient, error) { 2313 return nil, nil, nil 2314 } 2315 2316 func (c *fakeAcdClient) NewApplicationSetClient() (io.Closer, applicationsetpkg.ApplicationSetServiceClient, error) { 2317 return nil, nil, nil 2318 } 2319 2320 func (c *fakeAcdClient) NewApplicationClientOrDie() (io.Closer, applicationpkg.ApplicationServiceClient) { 2321 return nil, nil 2322 } 2323 2324 func (c *fakeAcdClient) NewApplicationSetClientOrDie() (io.Closer, applicationsetpkg.ApplicationSetServiceClient) { 2325 return nil, nil 2326 } 2327 2328 func (c *fakeAcdClient) NewNotificationClient() (io.Closer, notificationpkg.NotificationServiceClient, error) { 2329 return nil, nil, nil 2330 } 2331 2332 func (c *fakeAcdClient) NewNotificationClientOrDie() (io.Closer, notificationpkg.NotificationServiceClient) { 2333 return nil, nil 2334 } 2335 2336 func (c *fakeAcdClient) NewSessionClient() (io.Closer, sessionpkg.SessionServiceClient, error) { 2337 return nil, nil, nil 2338 } 2339 2340 func (c *fakeAcdClient) NewSessionClientOrDie() (io.Closer, sessionpkg.SessionServiceClient) { 2341 return nil, nil 2342 } 2343 2344 func (c *fakeAcdClient) NewSettingsClient() (io.Closer, settingspkg.SettingsServiceClient, error) { 2345 return nil, nil, nil 2346 } 2347 2348 func (c *fakeAcdClient) NewSettingsClientOrDie() (io.Closer, settingspkg.SettingsServiceClient) { 2349 return nil, nil 2350 } 2351 2352 func (c *fakeAcdClient) NewVersionClient() (io.Closer, versionpkg.VersionServiceClient, error) { 2353 return nil, nil, nil 2354 } 2355 2356 func (c *fakeAcdClient) NewVersionClientOrDie() (io.Closer, versionpkg.VersionServiceClient) { 2357 return nil, nil 2358 } 2359 2360 func (c *fakeAcdClient) NewProjectClient() (io.Closer, projectpkg.ProjectServiceClient, error) { 2361 return nil, nil, nil 2362 } 2363 2364 func (c *fakeAcdClient) NewProjectClientOrDie() (io.Closer, projectpkg.ProjectServiceClient) { 2365 return nil, nil 2366 } 2367 2368 func (c *fakeAcdClient) NewAccountClient() (io.Closer, accountpkg.AccountServiceClient, error) { 2369 return nil, nil, nil 2370 } 2371 2372 func (c *fakeAcdClient) NewAccountClientOrDie() (io.Closer, accountpkg.AccountServiceClient) { 2373 return nil, nil 2374 } 2375 2376 func (c *fakeAcdClient) WatchApplicationWithRetry(_ context.Context, _ string, _ string) chan *v1alpha1.ApplicationWatchEvent { 2377 appEventsCh := make(chan *v1alpha1.ApplicationWatchEvent) 2378 2379 go func() { 2380 modifiedEvent := new(v1alpha1.ApplicationWatchEvent) 2381 modifiedEvent.Type = watch.Modified 2382 appEventsCh <- modifiedEvent 2383 deletedEvent := new(v1alpha1.ApplicationWatchEvent) 2384 deletedEvent.Type = watch.Deleted 2385 appEventsCh <- deletedEvent 2386 }() 2387 return appEventsCh 2388 }