github.com/argoproj/argo-cd/v2@v2.10.9/controller/cache/info_test.go (about) 1 package cache 2 3 import ( 4 "sort" 5 "strings" 6 "testing" 7 8 "k8s.io/apimachinery/pkg/api/resource" 9 10 "github.com/argoproj/gitops-engine/pkg/utils/kube" 11 "github.com/argoproj/pkg/errors" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 v1 "k8s.io/api/core/v1" 15 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 16 "sigs.k8s.io/yaml" 17 18 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 19 "github.com/argoproj/argo-cd/v2/util/argo/normalizers" 20 ) 21 22 func strToUnstructured(jsonStr string) *unstructured.Unstructured { 23 obj := make(map[string]interface{}) 24 err := yaml.Unmarshal([]byte(jsonStr), &obj) 25 errors.CheckError(err) 26 return &unstructured.Unstructured{Object: obj} 27 } 28 29 var ( 30 testService = strToUnstructured(` 31 apiVersion: v1 32 kind: Service 33 metadata: 34 name: helm-guestbook 35 namespace: default 36 resourceVersion: "123" 37 uid: "4" 38 spec: 39 selector: 40 app: guestbook 41 type: LoadBalancer 42 status: 43 loadBalancer: 44 ingress: 45 - hostname: localhost`) 46 47 testLinkAnnotatedService = strToUnstructured(` 48 apiVersion: v1 49 kind: Service 50 metadata: 51 name: helm-guestbook 52 namespace: default 53 resourceVersion: "123" 54 uid: "4" 55 annotations: 56 link.argocd.argoproj.io/external-link: http://my-grafana.example.com/pre-generated-link 57 spec: 58 selector: 59 app: guestbook 60 type: LoadBalancer 61 status: 62 loadBalancer: 63 ingress: 64 - hostname: localhost`) 65 66 testIngress = strToUnstructured(` 67 apiVersion: extensions/v1beta1 68 kind: Ingress 69 metadata: 70 name: helm-guestbook 71 namespace: default 72 uid: "4" 73 spec: 74 backend: 75 serviceName: not-found-service 76 servicePort: 443 77 rules: 78 - host: helm-guestbook.example.com 79 http: 80 paths: 81 - backend: 82 serviceName: helm-guestbook 83 servicePort: 443 84 path: / 85 - backend: 86 serviceName: helm-guestbook 87 servicePort: https 88 path: / 89 tls: 90 - host: helm-guestbook.example.com 91 secretName: my-tls-secret 92 status: 93 loadBalancer: 94 ingress: 95 - ip: 107.178.210.11`) 96 97 testLinkAnnotatedIngress = strToUnstructured(` 98 apiVersion: extensions/v1beta1 99 kind: Ingress 100 metadata: 101 name: helm-guestbook 102 namespace: default 103 uid: "4" 104 annotations: 105 link.argocd.argoproj.io/external-link: http://my-grafana.example.com/ingress-link 106 spec: 107 backend: 108 serviceName: not-found-service 109 servicePort: 443 110 rules: 111 - host: helm-guestbook.example.com 112 http: 113 paths: 114 - backend: 115 serviceName: helm-guestbook 116 servicePort: 443 117 path: / 118 - backend: 119 serviceName: helm-guestbook 120 servicePort: https 121 path: / 122 tls: 123 - host: helm-guestbook.example.com 124 secretName: my-tls-secret 125 status: 126 loadBalancer: 127 ingress: 128 - ip: 107.178.210.11`) 129 130 testIngressWildCardPath = strToUnstructured(` 131 apiVersion: extensions/v1beta1 132 kind: Ingress 133 metadata: 134 name: helm-guestbook 135 namespace: default 136 uid: "4" 137 spec: 138 backend: 139 serviceName: not-found-service 140 servicePort: 443 141 rules: 142 - host: helm-guestbook.example.com 143 http: 144 paths: 145 - backend: 146 serviceName: helm-guestbook 147 servicePort: 443 148 path: /* 149 - backend: 150 serviceName: helm-guestbook 151 servicePort: https 152 path: /* 153 tls: 154 - host: helm-guestbook.example.com 155 secretName: my-tls-secret 156 status: 157 loadBalancer: 158 ingress: 159 - ip: 107.178.210.11`) 160 161 testIngressWithoutTls = strToUnstructured(` 162 apiVersion: extensions/v1beta1 163 kind: Ingress 164 metadata: 165 name: helm-guestbook 166 namespace: default 167 uid: "4" 168 spec: 169 backend: 170 serviceName: not-found-service 171 servicePort: 443 172 rules: 173 - host: helm-guestbook.example.com 174 http: 175 paths: 176 - backend: 177 serviceName: helm-guestbook 178 servicePort: 443 179 path: / 180 - backend: 181 serviceName: helm-guestbook 182 servicePort: https 183 path: / 184 status: 185 loadBalancer: 186 ingress: 187 - ip: 107.178.210.11`) 188 189 testIngressNetworkingV1 = strToUnstructured(` 190 apiVersion: networking.k8s.io/v1 191 kind: Ingress 192 metadata: 193 name: helm-guestbook 194 namespace: default 195 uid: "4" 196 spec: 197 backend: 198 service: 199 name: not-found-service 200 port: 201 number: 443 202 rules: 203 - host: helm-guestbook.example.com 204 http: 205 paths: 206 - backend: 207 service: 208 name: helm-guestbook 209 port: 210 number: 443 211 path: / 212 - backend: 213 service: 214 name: helm-guestbook 215 port: 216 name: https 217 path: / 218 tls: 219 - host: helm-guestbook.example.com 220 secretName: my-tls-secret 221 status: 222 loadBalancer: 223 ingress: 224 - ip: 107.178.210.11`) 225 226 testIstioVirtualService = strToUnstructured(` 227 apiVersion: networking.istio.io/v1alpha3 228 kind: VirtualService 229 metadata: 230 name: hello-world 231 namespace: demo 232 spec: 233 http: 234 - match: 235 - uri: 236 prefix: "/1" 237 route: 238 - destination: 239 host: service_full.demo.svc.cluster.local 240 - destination: 241 host: service_namespace.namespace 242 - match: 243 - uri: 244 prefix: "/2" 245 route: 246 - destination: 247 host: service 248 `) 249 ) 250 251 func TestGetPodInfo(t *testing.T) { 252 pod := strToUnstructured(` 253 apiVersion: v1 254 kind: Pod 255 metadata: 256 name: helm-guestbook-pod 257 namespace: default 258 ownerReferences: 259 - apiVersion: extensions/v1beta1 260 kind: ReplicaSet 261 name: helm-guestbook-rs 262 resourceVersion: "123" 263 labels: 264 app: guestbook 265 spec: 266 nodeName: minikube 267 containers: 268 - image: bar 269 resources: 270 requests: 271 memory: 128Mi 272 `) 273 274 info := &ResourceInfo{} 275 populateNodeInfo(pod, info, []string{}) 276 assert.Equal(t, []v1alpha1.InfoItem{ 277 {Name: "Node", Value: "minikube"}, 278 {Name: "Containers", Value: "0/1"}, 279 }, info.Info) 280 assert.Equal(t, []string{"bar"}, info.Images) 281 assert.Equal(t, &PodInfo{ 282 NodeName: "minikube", 283 ResourceRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("128Mi")}, 284 }, info.PodInfo) 285 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{Labels: map[string]string{"app": "guestbook"}}, info.NetworkingInfo) 286 } 287 288 func TestGetNodeInfo(t *testing.T) { 289 node := strToUnstructured(` 290 apiVersion: v1 291 kind: Node 292 metadata: 293 name: minikube 294 spec: {} 295 status: 296 capacity: 297 cpu: "6" 298 memory: 6091320Ki 299 nodeInfo: 300 architecture: amd64 301 operatingSystem: linux 302 osImage: Ubuntu 20.04 LTS 303 `) 304 305 info := &ResourceInfo{} 306 populateNodeInfo(node, info, []string{}) 307 assert.Equal(t, &NodeInfo{ 308 Name: "minikube", 309 Capacity: v1.ResourceList{v1.ResourceMemory: resource.MustParse("6091320Ki"), v1.ResourceCPU: resource.MustParse("6")}, 310 SystemInfo: v1.NodeSystemInfo{Architecture: "amd64", OperatingSystem: "linux", OSImage: "Ubuntu 20.04 LTS"}, 311 }, info.NodeInfo) 312 } 313 314 func TestGetServiceInfo(t *testing.T) { 315 info := &ResourceInfo{} 316 populateNodeInfo(testService, info, []string{}) 317 assert.Equal(t, 0, len(info.Info)) 318 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 319 TargetLabels: map[string]string{"app": "guestbook"}, 320 Ingress: []v1.LoadBalancerIngress{{Hostname: "localhost"}}, 321 }, info.NetworkingInfo) 322 } 323 324 func TestGetLinkAnnotatedServiceInfo(t *testing.T) { 325 info := &ResourceInfo{} 326 populateNodeInfo(testLinkAnnotatedService, info, []string{}) 327 assert.Equal(t, 0, len(info.Info)) 328 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 329 TargetLabels: map[string]string{"app": "guestbook"}, 330 Ingress: []v1.LoadBalancerIngress{{Hostname: "localhost"}}, 331 ExternalURLs: []string{"http://my-grafana.example.com/pre-generated-link"}, 332 }, info.NetworkingInfo) 333 } 334 335 func TestGetIstioVirtualServiceInfo(t *testing.T) { 336 info := &ResourceInfo{} 337 populateNodeInfo(testIstioVirtualService, info, []string{}) 338 assert.Equal(t, 0, len(info.Info)) 339 require.NotNil(t, info.NetworkingInfo) 340 require.NotNil(t, info.NetworkingInfo.TargetRefs) 341 assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{ 342 Kind: kube.ServiceKind, 343 Name: "service_full", 344 Namespace: "demo", 345 }) 346 assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{ 347 Kind: kube.ServiceKind, 348 Name: "service_namespace", 349 Namespace: "namespace", 350 }) 351 assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{ 352 Kind: kube.ServiceKind, 353 Name: "service", 354 Namespace: "demo", 355 }) 356 } 357 358 func TestGetIngressInfo(t *testing.T) { 359 var tests = []struct { 360 Ingress *unstructured.Unstructured 361 }{ 362 {testIngress}, 363 {testIngressNetworkingV1}, 364 } 365 for _, tc := range tests { 366 info := &ResourceInfo{} 367 populateNodeInfo(tc.Ingress, info, []string{}) 368 assert.Equal(t, 0, len(info.Info)) 369 sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { 370 return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 371 }) 372 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 373 Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, 374 TargetRefs: []v1alpha1.ResourceRef{{ 375 Namespace: "default", 376 Group: "", 377 Kind: kube.ServiceKind, 378 Name: "not-found-service", 379 }, { 380 Namespace: "default", 381 Group: "", 382 Kind: kube.ServiceKind, 383 Name: "helm-guestbook", 384 }}, 385 ExternalURLs: []string{"https://helm-guestbook.example.com/"}, 386 }, info.NetworkingInfo) 387 } 388 } 389 390 func TestGetLinkAnnotatedIngressInfo(t *testing.T) { 391 info := &ResourceInfo{} 392 populateNodeInfo(testLinkAnnotatedIngress, info, []string{}) 393 assert.Equal(t, 0, len(info.Info)) 394 sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { 395 return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 396 }) 397 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 398 Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, 399 TargetRefs: []v1alpha1.ResourceRef{{ 400 Namespace: "default", 401 Group: "", 402 Kind: kube.ServiceKind, 403 Name: "not-found-service", 404 }, { 405 Namespace: "default", 406 Group: "", 407 Kind: kube.ServiceKind, 408 Name: "helm-guestbook", 409 }}, 410 ExternalURLs: []string{"http://my-grafana.example.com/ingress-link", "https://helm-guestbook.example.com/"}, 411 }, info.NetworkingInfo) 412 } 413 414 func TestGetIngressInfoWildCardPath(t *testing.T) { 415 info := &ResourceInfo{} 416 populateNodeInfo(testIngressWildCardPath, info, []string{}) 417 assert.Equal(t, 0, len(info.Info)) 418 sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { 419 return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 420 }) 421 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 422 Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, 423 TargetRefs: []v1alpha1.ResourceRef{{ 424 Namespace: "default", 425 Group: "", 426 Kind: kube.ServiceKind, 427 Name: "not-found-service", 428 }, { 429 Namespace: "default", 430 Group: "", 431 Kind: kube.ServiceKind, 432 Name: "helm-guestbook", 433 }}, 434 ExternalURLs: []string{"https://helm-guestbook.example.com/"}, 435 }, info.NetworkingInfo) 436 } 437 438 func TestGetIngressInfoWithoutTls(t *testing.T) { 439 info := &ResourceInfo{} 440 populateNodeInfo(testIngressWithoutTls, info, []string{}) 441 assert.Equal(t, 0, len(info.Info)) 442 sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool { 443 return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0 444 }) 445 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 446 Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, 447 TargetRefs: []v1alpha1.ResourceRef{{ 448 Namespace: "default", 449 Group: "", 450 Kind: kube.ServiceKind, 451 Name: "not-found-service", 452 }, { 453 Namespace: "default", 454 Group: "", 455 Kind: kube.ServiceKind, 456 Name: "helm-guestbook", 457 }}, 458 ExternalURLs: []string{"http://helm-guestbook.example.com/"}, 459 }, info.NetworkingInfo) 460 } 461 462 func TestGetIngressInfoWithHost(t *testing.T) { 463 ingress := strToUnstructured(` 464 apiVersion: extensions/v1beta1 465 kind: Ingress 466 metadata: 467 name: helm-guestbook 468 namespace: default 469 spec: 470 rules: 471 - http: 472 paths: 473 - backend: 474 serviceName: helm-guestbook 475 servicePort: 443 476 path: / 477 tls: 478 - secretName: my-tls 479 status: 480 loadBalancer: 481 ingress: 482 - ip: 107.178.210.11`) 483 484 info := &ResourceInfo{} 485 populateNodeInfo(ingress, info, []string{}) 486 487 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 488 Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}}, 489 TargetRefs: []v1alpha1.ResourceRef{{ 490 Namespace: "default", 491 Group: "", 492 Kind: kube.ServiceKind, 493 Name: "helm-guestbook", 494 }}, 495 ExternalURLs: []string{"https://107.178.210.11/"}, 496 }, info.NetworkingInfo) 497 } 498 func TestGetIngressInfoNoHost(t *testing.T) { 499 ingress := strToUnstructured(` 500 apiVersion: extensions/v1beta1 501 kind: Ingress 502 metadata: 503 name: helm-guestbook 504 namespace: default 505 spec: 506 rules: 507 - http: 508 paths: 509 - backend: 510 serviceName: helm-guestbook 511 servicePort: 443 512 path: / 513 tls: 514 - secretName: my-tls 515 `) 516 517 info := &ResourceInfo{} 518 populateNodeInfo(ingress, info, []string{}) 519 520 assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{ 521 TargetRefs: []v1alpha1.ResourceRef{{ 522 Namespace: "default", 523 Group: "", 524 Kind: kube.ServiceKind, 525 Name: "helm-guestbook", 526 }}, 527 }, info.NetworkingInfo) 528 assert.Equal(t, len(info.NetworkingInfo.ExternalURLs), 0) 529 } 530 func TestExternalUrlWithSubPath(t *testing.T) { 531 ingress := strToUnstructured(` 532 apiVersion: networking.k8s.io/v1 533 kind: Ingress 534 metadata: 535 name: helm-guestbook 536 namespace: default 537 spec: 538 rules: 539 - http: 540 paths: 541 - backend: 542 serviceName: helm-guestbook 543 servicePort: 443 544 path: /my/sub/path/ 545 tls: 546 - secretName: my-tls 547 status: 548 loadBalancer: 549 ingress: 550 - ip: 107.178.210.11`) 551 552 info := &ResourceInfo{} 553 populateNodeInfo(ingress, info, []string{}) 554 555 expectedExternalUrls := []string{"https://107.178.210.11/my/sub/path/"} 556 assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs) 557 } 558 func TestExternalUrlWithMultipleSubPaths(t *testing.T) { 559 ingress := strToUnstructured(` 560 apiVersion: networking.k8s.io/v1 561 kind: Ingress 562 metadata: 563 name: helm-guestbook 564 namespace: default 565 spec: 566 rules: 567 - host: helm-guestbook.example.com 568 http: 569 paths: 570 - backend: 571 serviceName: helm-guestbook 572 servicePort: 443 573 path: /my/sub/path/ 574 - backend: 575 serviceName: helm-guestbook-2 576 servicePort: 443 577 path: /my/sub/path/2 578 - backend: 579 serviceName: helm-guestbook-3 580 servicePort: 443 581 tls: 582 - secretName: my-tls 583 status: 584 loadBalancer: 585 ingress: 586 - ip: 107.178.210.11`) 587 588 info := &ResourceInfo{} 589 populateNodeInfo(ingress, info, []string{}) 590 591 expectedExternalUrls := []string{"https://helm-guestbook.example.com/my/sub/path/", "https://helm-guestbook.example.com/my/sub/path/2", "https://helm-guestbook.example.com"} 592 actualURLs := info.NetworkingInfo.ExternalURLs 593 sort.Strings(expectedExternalUrls) 594 sort.Strings(actualURLs) 595 assert.Equal(t, expectedExternalUrls, actualURLs) 596 } 597 func TestExternalUrlWithNoSubPath(t *testing.T) { 598 ingress := strToUnstructured(` 599 apiVersion: networking.k8s.io/v1 600 kind: Ingress 601 metadata: 602 name: helm-guestbook 603 namespace: default 604 spec: 605 rules: 606 - http: 607 paths: 608 - backend: 609 serviceName: helm-guestbook 610 servicePort: 443 611 tls: 612 - secretName: my-tls 613 status: 614 loadBalancer: 615 ingress: 616 - ip: 107.178.210.11`) 617 618 info := &ResourceInfo{} 619 populateNodeInfo(ingress, info, []string{}) 620 621 expectedExternalUrls := []string{"https://107.178.210.11"} 622 assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs) 623 } 624 625 func TestExternalUrlWithNetworkingApi(t *testing.T) { 626 ingress := strToUnstructured(` 627 apiVersion: networking.k8s.io/v1beta1 628 kind: Ingress 629 metadata: 630 name: helm-guestbook 631 namespace: default 632 spec: 633 rules: 634 - http: 635 paths: 636 - backend: 637 serviceName: helm-guestbook 638 servicePort: 443 639 tls: 640 - secretName: my-tls 641 status: 642 loadBalancer: 643 ingress: 644 - ip: 107.178.210.11`) 645 646 info := &ResourceInfo{} 647 populateNodeInfo(ingress, info, []string{}) 648 649 expectedExternalUrls := []string{"https://107.178.210.11"} 650 assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs) 651 } 652 653 func TestCustomLabel(t *testing.T) { 654 configmap := strToUnstructured(` 655 apiVersion: v1 656 kind: ConfigMap 657 metadata: 658 name: cm`) 659 660 info := &ResourceInfo{} 661 populateNodeInfo(configmap, info, []string{"my-label"}) 662 663 assert.Equal(t, 0, len(info.Info)) 664 665 configmap = strToUnstructured(` 666 apiVersion: v1 667 kind: ConfigMap 668 metadata: 669 name: cm 670 labels: 671 my-label: value`) 672 673 info = &ResourceInfo{} 674 populateNodeInfo(configmap, info, []string{"my-label", "other-label"}) 675 676 assert.Equal(t, 1, len(info.Info)) 677 assert.Equal(t, "my-label", info.Info[0].Name) 678 assert.Equal(t, "value", info.Info[0].Value) 679 680 configmap = strToUnstructured(` 681 apiVersion: v1 682 kind: ConfigMap 683 metadata: 684 name: cm 685 labels: 686 my-label: value 687 other-label: value2`) 688 689 info = &ResourceInfo{} 690 populateNodeInfo(configmap, info, []string{"my-label", "other-label"}) 691 692 assert.Equal(t, 2, len(info.Info)) 693 assert.Equal(t, "my-label", info.Info[0].Name) 694 assert.Equal(t, "value", info.Info[0].Value) 695 assert.Equal(t, "other-label", info.Info[1].Name) 696 assert.Equal(t, "value2", info.Info[1].Value) 697 } 698 699 func TestManifestHash(t *testing.T) { 700 manifest := strToUnstructured(` 701 apiVersion: v1 702 kind: Pod 703 metadata: 704 name: helm-guestbook-pod 705 namespace: default 706 ownerReferences: 707 - apiVersion: extensions/v1beta1 708 kind: ReplicaSet 709 name: helm-guestbook-rs 710 resourceVersion: "123" 711 labels: 712 app: guestbook 713 spec: 714 nodeName: minikube 715 containers: 716 - image: bar 717 resources: 718 requests: 719 memory: 128Mi 720 `) 721 722 ignores := []v1alpha1.ResourceIgnoreDifferences{ 723 { 724 Group: "*", 725 Kind: "*", 726 JSONPointers: []string{"/metadata/resourceVersion"}, 727 }, 728 } 729 730 data, _ := strToUnstructured(` 731 apiVersion: v1 732 kind: Pod 733 metadata: 734 name: helm-guestbook-pod 735 namespace: default 736 ownerReferences: 737 - apiVersion: extensions/v1beta1 738 kind: ReplicaSet 739 name: helm-guestbook-rs 740 labels: 741 app: guestbook 742 spec: 743 nodeName: minikube 744 containers: 745 - image: bar 746 resources: 747 requests: 748 memory: 128Mi 749 `).MarshalJSON() 750 751 expected := hash(data) 752 753 hash, err := generateManifestHash(manifest, ignores, nil, normalizers.IgnoreNormalizerOpts{}) 754 assert.Equal(t, expected, hash) 755 assert.Nil(t, err) 756 }