github.com/argoproj/argo-cd/v3@v3.2.1/controller/cache/info_test.go (about)

     1  package cache
     2  
     3  import (
     4  	"sort"
     5  	"testing"
     6  
     7  	"github.com/argoproj/gitops-engine/pkg/utils/kube"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  	corev1 "k8s.io/api/core/v1"
    11  	"k8s.io/apimachinery/pkg/api/resource"
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"sigs.k8s.io/yaml"
    14  
    15  	"github.com/argoproj/argo-cd/v3/common"
    16  	"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    17  	"github.com/argoproj/argo-cd/v3/util/argo/normalizers"
    18  	"github.com/argoproj/argo-cd/v3/util/errors"
    19  )
    20  
    21  func strToUnstructured(jsonStr string) *unstructured.Unstructured {
    22  	obj := make(map[string]any)
    23  	err := yaml.Unmarshal([]byte(jsonStr), &obj)
    24  	errors.CheckError(err)
    25  	return &unstructured.Unstructured{Object: obj}
    26  }
    27  
    28  var (
    29  	testService = strToUnstructured(`
    30    apiVersion: v1
    31    kind: Service
    32    metadata:
    33      name: helm-guestbook
    34      namespace: default
    35      resourceVersion: "123"
    36      uid: "4"
    37    spec:
    38      selector:
    39        app: guestbook
    40      type: LoadBalancer
    41    status:
    42      loadBalancer:
    43        ingress:
    44        - hostname: localhost`)
    45  
    46  	testLinkAnnotatedService = strToUnstructured(`
    47    apiVersion: v1
    48    kind: Service
    49    metadata:
    50      name: helm-guestbook
    51      namespace: default
    52      resourceVersion: "123"
    53      uid: "4"
    54      annotations:
    55        link.argocd.argoproj.io/external-link: http://my-grafana.example.com/pre-generated-link
    56    spec:
    57      selector:
    58        app: guestbook
    59      type: LoadBalancer
    60    status:
    61      loadBalancer:
    62        ingress:
    63        - hostname: localhost`)
    64  
    65  	testIngress = strToUnstructured(`
    66    apiVersion: extensions/v1beta1
    67    kind: Ingress
    68    metadata:
    69      name: helm-guestbook
    70      namespace: default
    71      uid: "4"
    72    spec:
    73      backend:
    74        serviceName: not-found-service
    75        servicePort: 443
    76      rules:
    77      - host: helm-guestbook.example.com
    78        http:
    79          paths:
    80          - backend:
    81              serviceName: helm-guestbook
    82              servicePort: 443
    83            path: /
    84          - backend:
    85              serviceName: helm-guestbook
    86              servicePort: https
    87            path: /
    88      tls:
    89      - host: helm-guestbook.example.com
    90      secretName: my-tls-secret
    91    status:
    92      loadBalancer:
    93        ingress:
    94        - ip: 107.178.210.11`)
    95  
    96  	testLinkAnnotatedIngress = strToUnstructured(`
    97    apiVersion: extensions/v1beta1
    98    kind: Ingress
    99    metadata:
   100      name: helm-guestbook
   101      namespace: default
   102      uid: "4"
   103      annotations:
   104        link.argocd.argoproj.io/external-link: http://my-grafana.example.com/ingress-link
   105    spec:
   106      backend:
   107        serviceName: not-found-service
   108        servicePort: 443
   109      rules:
   110      - host: helm-guestbook.example.com
   111        http:
   112          paths:
   113          - backend:
   114              serviceName: helm-guestbook
   115              servicePort: 443
   116            path: /
   117          - backend:
   118              serviceName: helm-guestbook
   119              servicePort: https
   120            path: /
   121      tls:
   122      - host: helm-guestbook.example.com
   123      secretName: my-tls-secret
   124    status:
   125      loadBalancer:
   126        ingress:
   127        - ip: 107.178.210.11`)
   128  
   129  	testIngressWildCardPath = strToUnstructured(`
   130    apiVersion: extensions/v1beta1
   131    kind: Ingress
   132    metadata:
   133      name: helm-guestbook
   134      namespace: default
   135      uid: "4"
   136    spec:
   137      backend:
   138        serviceName: not-found-service
   139        servicePort: 443
   140      rules:
   141      - host: helm-guestbook.example.com
   142        http:
   143          paths:
   144          - backend:
   145              serviceName: helm-guestbook
   146              servicePort: 443
   147            path: /*
   148          - backend:
   149              serviceName: helm-guestbook
   150              servicePort: https
   151            path: /*
   152      tls:
   153      - host: helm-guestbook.example.com
   154      secretName: my-tls-secret
   155    status:
   156      loadBalancer:
   157        ingress:
   158        - ip: 107.178.210.11`)
   159  
   160  	testIngressWithoutTLS = strToUnstructured(`
   161    apiVersion: extensions/v1beta1
   162    kind: Ingress
   163    metadata:
   164      name: helm-guestbook
   165      namespace: default
   166      uid: "4"
   167    spec:
   168      backend:
   169        serviceName: not-found-service
   170        servicePort: 443
   171      rules:
   172      - host: helm-guestbook.example.com
   173        http:
   174          paths:
   175          - backend:
   176              serviceName: helm-guestbook
   177              servicePort: 443
   178            path: /
   179          - backend:
   180              serviceName: helm-guestbook
   181              servicePort: https
   182            path: /
   183    status:
   184      loadBalancer:
   185        ingress:
   186        - ip: 107.178.210.11`)
   187  
   188  	testIngressNetworkingV1 = strToUnstructured(`
   189    apiVersion: networking.k8s.io/v1
   190    kind: Ingress
   191    metadata:
   192      name: helm-guestbook
   193      namespace: default
   194      uid: "4"
   195    spec:
   196      backend:
   197        service:
   198          name: not-found-service
   199          port:
   200            number: 443
   201      rules:
   202      - host: helm-guestbook.example.com
   203        http:
   204          paths:
   205          - backend:
   206              service:
   207                name: helm-guestbook
   208                port:
   209                  number: 443
   210            path: /
   211          - backend:
   212              service:
   213                name: helm-guestbook
   214                port:
   215                  name: https
   216            path: /
   217      tls:
   218      - host: helm-guestbook.example.com
   219      secretName: my-tls-secret
   220    status:
   221      loadBalancer:
   222        ingress:
   223        - ip: 107.178.210.11`)
   224  
   225  	testIstioVirtualService = strToUnstructured(`
   226  apiVersion: networking.istio.io/v1alpha3
   227  kind: VirtualService
   228  metadata:
   229    name: hello-world
   230    namespace: demo
   231  spec:
   232    http:
   233      - match:
   234          - uri:
   235              prefix: "/1"
   236        route:
   237          - destination:
   238              host: service_full.demo.svc.cluster.local
   239          - destination:
   240              host: service_namespace.namespace
   241      - match:
   242          - uri:
   243              prefix: "/2"
   244        route:
   245          - destination:
   246              host: service
   247  `)
   248  
   249  	testIstioServiceEntry = strToUnstructured(`
   250  apiVersion: networking.istio.io/v1beta1
   251  kind: ServiceEntry
   252  metadata:
   253    name: echo
   254  spec:
   255    exportTo:
   256    - '*'
   257    hosts:
   258    - echo.internal
   259    location: MESH_INTERNAL
   260    ports:
   261    - name: http
   262      number: 80
   263      protocol: HTTP
   264      targetPort: 5678 
   265    resolution: DNS
   266  
   267    workloadSelector:
   268      labels:
   269        app.kubernetes.io/name: echo-2
   270  `)
   271  )
   272  
   273  // These tests are equivalent to tests in ui/src/app/applications/components/utils.test.tsx. If you update tests here,
   274  // please make sure to update the equivalent tests in the UI.
   275  func TestGetPodInfo(t *testing.T) {
   276  	t.Parallel()
   277  
   278  	t.Run("TestGetPodInfo", func(t *testing.T) {
   279  		t.Parallel()
   280  
   281  		pod := strToUnstructured(`
   282    apiVersion: v1
   283    kind: Pod
   284    metadata:
   285      name: helm-guestbook-pod
   286      namespace: default
   287      ownerReferences:
   288      - apiVersion: extensions/v1beta1
   289        kind: ReplicaSet
   290        name: helm-guestbook-rs
   291      resourceVersion: "123"
   292      labels:
   293        app: guestbook
   294    spec:
   295      nodeName: minikube
   296      containers:
   297      - image: bar
   298        resources:
   299          requests:
   300            memory: 128Mi
   301  `)
   302  
   303  		info := &ResourceInfo{}
   304  		populateNodeInfo(pod, info, []string{})
   305  		assert.Equal(t, []v1alpha1.InfoItem{
   306  			{Name: "Node", Value: "minikube"},
   307  			{Name: "Containers", Value: "0/1"},
   308  			{Name: common.PodRequestsCPU, Value: "0"}, // strings imported from common
   309  			{Name: common.PodRequestsMEM, Value: "134217728000"},
   310  		}, info.Info)
   311  		assert.Equal(t, []string{"bar"}, info.Images)
   312  		assert.Equal(t, &PodInfo{
   313  			NodeName:         "minikube",
   314  			ResourceRequests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("128Mi")},
   315  		}, info.PodInfo)
   316  		assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{Labels: map[string]string{"app": "guestbook"}}, info.NetworkingInfo)
   317  	})
   318  
   319  	t.Run("TestGetPodWithInitialContainerInfo", func(t *testing.T) {
   320  		pod := strToUnstructured(`
   321    apiVersion: "v1"
   322    kind: "Pod"
   323    metadata: 
   324      labels: 
   325        app: "app-with-initial-container"
   326      name: "app-with-initial-container-5f46976fdb-vd6rv"
   327      namespace: "default"
   328      ownerReferences: 
   329      - apiVersion: "apps/v1"
   330        kind: "ReplicaSet"
   331        name: "app-with-initial-container-5f46976fdb"
   332    spec: 
   333      containers: 
   334      - image: "alpine:latest"
   335        imagePullPolicy: "Always"
   336        name: "app-with-initial-container"
   337      initContainers: 
   338      - image: "alpine:latest"
   339        imagePullPolicy: "Always"
   340        name: "app-with-initial-container-logshipper"
   341      nodeName: "minikube"
   342    status: 
   343      containerStatuses: 
   344      - image: "alpine:latest"
   345        name: "app-with-initial-container"
   346        ready: true
   347        restartCount: 0
   348        started: true
   349        state: 
   350          running: 
   351            startedAt: "2024-10-08T08:44:25Z"
   352      initContainerStatuses: 
   353      - image: "alpine:latest"
   354        name: "app-with-initial-container-logshipper"
   355        ready: true
   356        restartCount: 0
   357        started: false
   358        state: 
   359          terminated: 
   360            exitCode: 0
   361            reason: "Completed"
   362      phase: "Running"
   363  `)
   364  
   365  		info := &ResourceInfo{}
   366  		populateNodeInfo(pod, info, []string{})
   367  		assert.Equal(t, []v1alpha1.InfoItem{
   368  			{Name: "Status Reason", Value: "Running"},
   369  			{Name: "Node", Value: "minikube"},
   370  			{Name: "Containers", Value: "1/1"},
   371  			{Name: common.PodRequestsCPU, Value: "0"},
   372  			{Name: common.PodRequestsMEM, Value: "0"},
   373  		}, info.Info)
   374  	})
   375  
   376  	t.Run("TestGetPodWithInitialContainerInfoWithResources", func(t *testing.T) {
   377  		pod := strToUnstructured(`
   378          apiVersion: "v1"
   379          kind: "Pod"
   380          metadata:
   381              labels:
   382                  app: "app-with-initial-container"
   383              name: "app-with-initial-container-5f46976fdb-vd6rv"
   384              namespace: "default"
   385              ownerReferences:
   386              - apiVersion: "apps/v1"
   387                kind: "ReplicaSet"
   388                name: "app-with-initial-container-5f46976fdb"
   389          spec:
   390              containers:
   391              - image: "alpine:latest"
   392                imagePullPolicy: "Always"
   393                name: "app-with-initial-container"
   394                resources:
   395                  requests:
   396                    cpu: "100m"
   397                    memory: "128Mi"
   398                  limits:
   399                    cpu: "500m"
   400                    memory: "512Mi"
   401              initContainers:
   402              - image: "alpine:latest"
   403                imagePullPolicy: "Always"
   404                name: "app-with-initial-container-logshipper"
   405                resources:
   406                  requests:
   407                    cpu: "50m"
   408                    memory: "64Mi"
   409                  limits:
   410                    cpu: "250m"
   411                    memory: "256Mi"
   412              nodeName: "minikube"
   413          status:
   414              containerStatuses:
   415              - image: "alpine:latest"
   416                name: "app-with-initial-container"
   417                ready: true
   418                restartCount: 0
   419                started: true
   420                state:
   421                  running:
   422                    startedAt: "2024-10-08T08:44:25Z"
   423              initContainerStatuses:
   424              - image: "alpine:latest"
   425                name: "app-with-initial-container-logshipper"
   426                ready: true
   427                restartCount: 0
   428                started: false
   429                state:
   430                  terminated:
   431                    exitCode: 0
   432                    reason: "Completed"
   433              phase: "Running"
   434      `)
   435  
   436  		info := &ResourceInfo{}
   437  		populateNodeInfo(pod, info, []string{})
   438  		assert.Equal(t, []v1alpha1.InfoItem{
   439  			{Name: "Status Reason", Value: "Running"},
   440  			{Name: "Node", Value: "minikube"},
   441  			{Name: "Containers", Value: "1/1"},
   442  			{Name: common.PodRequestsCPU, Value: "100"},
   443  			{Name: common.PodRequestsMEM, Value: "134217728000"},
   444  		}, info.Info)
   445  	})
   446  	t.Run("TestGetPodInfoWithSidecar", func(t *testing.T) {
   447  		t.Parallel()
   448  
   449  		pod := strToUnstructured(`
   450    apiVersion: v1
   451    kind: Pod
   452    metadata:
   453      labels:
   454        app: app-with-sidecar
   455      name: app-with-sidecar-6664cc788c-lqlrp
   456      namespace: default
   457      ownerReferences:
   458        - apiVersion: apps/v1
   459          kind: ReplicaSet
   460          name: app-with-sidecar-6664cc788c
   461    spec:
   462      containers:
   463      - image: 'docker.m.daocloud.io/library/alpine:latest'
   464        imagePullPolicy: Always
   465        name: app-with-sidecar
   466      initContainers:
   467      - image: 'docker.m.daocloud.io/library/alpine:latest'
   468        imagePullPolicy: Always
   469        name: logshipper
   470        restartPolicy: Always
   471      nodeName: minikube
   472    status:
   473      containerStatuses:
   474      - image: 'docker.m.daocloud.io/library/alpine:latest'
   475        name: app-with-sidecar
   476        ready: true
   477        restartCount: 0
   478        started: true
   479        state:
   480          running:
   481            startedAt: '2024-10-08T08:39:43Z'
   482      initContainerStatuses:
   483      - image: 'docker.m.daocloud.io/library/alpine:latest'
   484        name: logshipper
   485        ready: true
   486        restartCount: 0
   487        started: true
   488        state:
   489          running:
   490            startedAt: '2024-10-08T08:39:40Z'
   491      phase: Running
   492  `)
   493  
   494  		info := &ResourceInfo{}
   495  		populateNodeInfo(pod, info, []string{})
   496  		assert.Equal(t, []v1alpha1.InfoItem{
   497  			{Name: "Status Reason", Value: "Running"},
   498  			{Name: "Node", Value: "minikube"},
   499  			{Name: "Containers", Value: "2/2"},
   500  			{Name: common.PodRequestsCPU, Value: "0"},
   501  			{Name: common.PodRequestsMEM, Value: "0"},
   502  		}, info.Info)
   503  	})
   504  
   505  	t.Run("TestGetPodInfoWithInitialContainer", func(t *testing.T) {
   506  		t.Parallel()
   507  
   508  		pod := strToUnstructured(`
   509    apiVersion: v1
   510    kind: Pod
   511    metadata:
   512      generateName: myapp-long-exist-56b7d8794d-
   513      labels:
   514        app: myapp-long-exist
   515      name: myapp-long-exist-56b7d8794d-pbgrd
   516      namespace: linghao
   517      ownerReferences:
   518        - apiVersion: apps/v1
   519          kind: ReplicaSet
   520          name: myapp-long-exist-56b7d8794d
   521    spec:
   522      containers:
   523        - image: alpine:latest
   524          imagePullPolicy: Always
   525          name: myapp-long-exist
   526      initContainers:
   527        - image: alpine:latest
   528          imagePullPolicy: Always
   529          name: myapp-long-exist-logshipper
   530      nodeName: minikube
   531    status:
   532      containerStatuses:
   533        - image: alpine:latest
   534          name: myapp-long-exist
   535          ready: false
   536          restartCount: 0
   537          started: false
   538          state:
   539            waiting:
   540              reason: PodInitializing
   541      initContainerStatuses:
   542        - image: alpine:latest
   543          name: myapp-long-exist-logshipper
   544          ready: false
   545          restartCount: 0
   546          started: true
   547          state:
   548            running:
   549              startedAt: '2024-10-09T08:03:45Z'
   550      phase: Pending
   551      startTime: '2024-10-09T08:02:39Z'
   552  `)
   553  
   554  		info := &ResourceInfo{}
   555  		populateNodeInfo(pod, info, []string{})
   556  		assert.Equal(t, []v1alpha1.InfoItem{
   557  			{Name: "Status Reason", Value: "Init:0/1"},
   558  			{Name: "Node", Value: "minikube"},
   559  			{Name: "Containers", Value: "0/1"},
   560  			{Name: common.PodRequestsCPU, Value: "0"},
   561  			{Name: common.PodRequestsMEM, Value: "0"},
   562  		}, info.Info)
   563  	})
   564  
   565  	// Test pod has 2 restartable init containers, the first one running but not started.
   566  	t.Run("TestGetPodInfoWithRestartableInitContainer", func(t *testing.T) {
   567  		t.Parallel()
   568  
   569  		pod := strToUnstructured(`
   570    apiVersion: v1
   571    kind: Pod
   572    metadata:
   573      name: test1
   574    spec:
   575      initContainers:
   576        - name: restartable-init-1
   577          restartPolicy: Always
   578        - name: restartable-init-2
   579          restartPolicy: Always
   580      containers:
   581        - name: container
   582      nodeName: minikube
   583    status:
   584      phase: Pending
   585      initContainerStatuses:
   586        - name: restartable-init-1
   587          ready: false
   588          restartCount: 3
   589          state:
   590            running: {}
   591          started: false
   592          lastTerminationState:
   593            terminated:
   594              finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
   595        - name: restartable-init-2
   596          ready: false
   597          state:
   598            waiting: {}
   599          started: false
   600      containerStatuses:
   601        - ready: false
   602          restartCount: 0
   603          state:
   604            waiting: {}
   605      conditions:
   606        - type: ContainersReady
   607          status: "False"
   608        - type: Initialized
   609          status: "False"
   610  `)
   611  
   612  		info := &ResourceInfo{}
   613  		populateNodeInfo(pod, info, []string{})
   614  		assert.Equal(t, []v1alpha1.InfoItem{
   615  			{Name: "Status Reason", Value: "Init:0/2"},
   616  			{Name: "Node", Value: "minikube"},
   617  			{Name: "Containers", Value: "0/3"},
   618  			{Name: "Restart Count", Value: "3"},
   619  			{Name: common.PodRequestsCPU, Value: "0"},
   620  			{Name: common.PodRequestsMEM, Value: "0"},
   621  		}, info.Info)
   622  	})
   623  
   624  	// Test pod has 2 restartable init containers, the first one started and the second one running but not started.
   625  	t.Run("TestGetPodInfoWithPartiallyStartedInitContainers", func(t *testing.T) {
   626  		t.Parallel()
   627  
   628  		pod := strToUnstructured(`
   629    apiVersion: v1
   630    kind: Pod
   631    metadata:
   632      name: test1
   633    spec:
   634      initContainers:
   635        - name: restartable-init-1
   636          restartPolicy: Always
   637        - name: restartable-init-2
   638          restartPolicy: Always
   639      containers:
   640        - name: container
   641      nodeName: minikube
   642    status:
   643      phase: Pending
   644      initContainerStatuses:
   645        - name: restartable-init-1
   646          ready: false
   647          restartCount: 3
   648          state:
   649            running: {}
   650          started: true
   651          lastTerminationState:
   652            terminated:
   653              finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
   654        - name: restartable-init-2
   655          ready: false
   656          state:
   657            running: {}
   658          started: false
   659      containerStatuses:
   660        - ready: false
   661          restartCount: 0
   662          state:
   663            waiting: {}
   664      conditions:
   665        - type: ContainersReady
   666          status: "False"
   667        - type: Initialized
   668          status: "False"
   669  `)
   670  
   671  		info := &ResourceInfo{}
   672  		populateNodeInfo(pod, info, []string{})
   673  		assert.Equal(t, []v1alpha1.InfoItem{
   674  			{Name: "Status Reason", Value: "Init:1/2"},
   675  			{Name: "Node", Value: "minikube"},
   676  			{Name: "Containers", Value: "0/3"},
   677  			{Name: "Restart Count", Value: "3"},
   678  			{Name: common.PodRequestsCPU, Value: "0"},
   679  			{Name: common.PodRequestsMEM, Value: "0"},
   680  		}, info.Info)
   681  	})
   682  
   683  	// Test pod has 2 restartable init containers started and 1 container running
   684  	t.Run("TestGetPodInfoWithStartedInitContainers", func(t *testing.T) {
   685  		t.Parallel()
   686  
   687  		pod := strToUnstructured(`
   688    apiVersion: v1
   689    kind: Pod
   690    metadata:
   691      name: test2
   692    spec:
   693      initContainers:
   694        - name: restartable-init-1
   695          restartPolicy: Always
   696        - name: restartable-init-2
   697          restartPolicy: Always
   698      containers:
   699        - name: container
   700      nodeName: minikube
   701    status:
   702      phase: Running
   703      initContainerStatuses:
   704        - name: restartable-init-1
   705          ready: false
   706          restartCount: 3
   707          state:
   708            running: {}
   709          started: true
   710          lastTerminationState:
   711            terminated:
   712              finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
   713        - name: restartable-init-2
   714          ready: false
   715          state:
   716            running: {}
   717          started: true
   718      containerStatuses:
   719        - ready: true
   720          restartCount: 4
   721          state:
   722            running: {}
   723          lastTerminationState:
   724            terminated:
   725              finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
   726      conditions:
   727        - type: ContainersReady
   728          status: "False"
   729        - type: Initialized
   730          status: "True"
   731  `)
   732  
   733  		info := &ResourceInfo{}
   734  		populateNodeInfo(pod, info, []string{})
   735  		assert.Equal(t, []v1alpha1.InfoItem{
   736  			{Name: "Status Reason", Value: "Running"},
   737  			{Name: "Node", Value: "minikube"},
   738  			{Name: "Containers", Value: "1/3"},
   739  			{Name: "Restart Count", Value: "7"},
   740  			{Name: common.PodRequestsCPU, Value: "0"},
   741  			{Name: common.PodRequestsMEM, Value: "0"},
   742  		}, info.Info)
   743  	})
   744  
   745  	// Test pod has 1 init container restarting and 1 container not running
   746  	t.Run("TestGetPodInfoWithNormalInitContainer", func(t *testing.T) {
   747  		t.Parallel()
   748  
   749  		pod := strToUnstructured(`
   750    apiVersion: v1
   751    kind: Pod
   752    metadata:
   753      name: test7
   754    spec:
   755      initContainers:
   756        - name: init-container
   757      containers:
   758        - name: main-container
   759      nodeName: minikube
   760    status:
   761      phase: podPhase
   762      initContainerStatuses:
   763        - ready: false
   764          restartCount: 3
   765          state:
   766            running: {}
   767          lastTerminationState:
   768            terminated:
   769              finishedAt: "2023-10-01T00:00:00Z" # Replace with the actual time
   770      containerStatuses:
   771        - ready: false
   772          restartCount: 0
   773          state:
   774            waiting: {}
   775  `)
   776  
   777  		info := &ResourceInfo{}
   778  		populateNodeInfo(pod, info, []string{})
   779  		assert.Equal(t, []v1alpha1.InfoItem{
   780  			{Name: "Status Reason", Value: "Init:0/1"},
   781  			{Name: "Node", Value: "minikube"},
   782  			{Name: "Containers", Value: "0/1"},
   783  			{Name: "Restart Count", Value: "3"},
   784  			{Name: common.PodRequestsCPU, Value: "0"},
   785  			{Name: common.PodRequestsMEM, Value: "0"},
   786  		}, info.Info)
   787  	})
   788  
   789  	// Test pod condition succeed
   790  	t.Run("TestPodConditionSucceeded", func(t *testing.T) {
   791  		t.Parallel()
   792  
   793  		pod := strToUnstructured(`
   794    apiVersion: v1
   795    kind: Pod
   796    metadata:
   797      name: test8
   798    spec:
   799      nodeName: minikube
   800      containers:
   801        - name: container
   802    status:
   803      phase: Succeeded
   804      containerStatuses:
   805        - ready: false
   806          restartCount: 0
   807          state:
   808            terminated:
   809              reason: Completed
   810              exitCode: 0
   811  `)
   812  		info := &ResourceInfo{}
   813  		populateNodeInfo(pod, info, []string{})
   814  		assert.Equal(t, []v1alpha1.InfoItem{
   815  			{Name: "Status Reason", Value: "Completed"},
   816  			{Name: "Node", Value: "minikube"},
   817  			{Name: "Containers", Value: "0/1"},
   818  		}, info.Info)
   819  	})
   820  
   821  	// Test pod condition succeed which had some allocated resources
   822  	t.Run("TestPodConditionSucceededWithResources", func(t *testing.T) {
   823  		t.Parallel()
   824  
   825  		pod := strToUnstructured(`
   826    apiVersion: v1
   827    kind: Pod
   828    metadata:
   829      name: test8
   830    spec:
   831      nodeName: minikube
   832      containers:
   833        - name: container
   834          resources:
   835            requests:
   836              cpu: "50m"
   837              memory: "64Mi"
   838            limits:
   839              cpu: "250m"
   840              memory: "256Mi"
   841    status:
   842      phase: Succeeded
   843      containerStatuses:
   844        - ready: false
   845          restartCount: 0
   846          state:
   847            terminated:
   848              reason: Completed
   849              exitCode: 0
   850  `)
   851  		info := &ResourceInfo{}
   852  		populateNodeInfo(pod, info, []string{})
   853  		assert.Equal(t, []v1alpha1.InfoItem{
   854  			{Name: "Status Reason", Value: "Completed"},
   855  			{Name: "Node", Value: "minikube"},
   856  			{Name: "Containers", Value: "0/1"},
   857  		}, info.Info)
   858  	})
   859  
   860  	// Test pod condition failed
   861  	t.Run("TestPodConditionFailed", func(t *testing.T) {
   862  		t.Parallel()
   863  
   864  		pod := strToUnstructured(`
   865    apiVersion: v1
   866    kind: Pod
   867    metadata:
   868      name: test9
   869    spec:
   870      nodeName: minikube
   871      containers:
   872        - name: container
   873    status:
   874      phase: Failed
   875      containerStatuses:
   876        - ready: false
   877          restartCount: 0
   878          state:
   879            terminated:
   880              reason: Error
   881              exitCode: 1
   882  `)
   883  		info := &ResourceInfo{}
   884  		populateNodeInfo(pod, info, []string{})
   885  		assert.Equal(t, []v1alpha1.InfoItem{
   886  			{Name: "Status Reason", Value: "Error"},
   887  			{Name: "Node", Value: "minikube"},
   888  			{Name: "Containers", Value: "0/1"},
   889  		}, info.Info)
   890  	})
   891  
   892  	// Test pod condition failed with allocated resources
   893  
   894  	t.Run("TestPodConditionFailedWithResources", func(t *testing.T) {
   895  		t.Parallel()
   896  
   897  		pod := strToUnstructured(`
   898    apiVersion: v1
   899    kind: Pod
   900    metadata:
   901      name: test9
   902    spec:
   903      nodeName: minikube
   904      containers:
   905        - name: container
   906          resources:
   907            requests:
   908              cpu: "50m"
   909              memory: "64Mi"
   910            limits:
   911              cpu: "250m"
   912              memory: "256Mi"
   913    status:
   914      phase: Failed
   915      containerStatuses:
   916        - ready: false
   917          restartCount: 0
   918          state:
   919            terminated:
   920              reason: Error
   921              exitCode: 1
   922  `)
   923  		info := &ResourceInfo{}
   924  		populateNodeInfo(pod, info, []string{})
   925  		assert.Equal(t, []v1alpha1.InfoItem{
   926  			{Name: "Status Reason", Value: "Error"},
   927  			{Name: "Node", Value: "minikube"},
   928  			{Name: "Containers", Value: "0/1"},
   929  		}, info.Info)
   930  	})
   931  
   932  	// Test pod condition succeed with deletion
   933  	t.Run("TestPodConditionSucceededWithDeletion", func(t *testing.T) {
   934  		t.Parallel()
   935  
   936  		pod := strToUnstructured(`
   937    apiVersion: v1
   938    kind: Pod
   939    metadata:
   940      name: test10
   941      deletionTimestamp: "2023-10-01T00:00:00Z"
   942    spec:
   943      nodeName: minikube
   944      containers:
   945        - name: container
   946    status:
   947      phase: Succeeded
   948      containerStatuses:
   949        - ready: false
   950          restartCount: 0
   951          state:
   952            terminated:
   953              reason: Completed
   954              exitCode: 0
   955  `)
   956  		info := &ResourceInfo{}
   957  		populateNodeInfo(pod, info, []string{})
   958  		assert.Equal(t, []v1alpha1.InfoItem{
   959  			{Name: "Status Reason", Value: "Completed"},
   960  			{Name: "Node", Value: "minikube"},
   961  			{Name: "Containers", Value: "0/1"},
   962  		}, info.Info)
   963  	})
   964  
   965  	// Test pod condition running with deletion
   966  	t.Run("TestPodConditionRunningWithDeletion", func(t *testing.T) {
   967  		t.Parallel()
   968  
   969  		pod := strToUnstructured(`
   970    apiVersion: v1
   971    kind: Pod
   972    metadata:
   973      name: test11
   974      deletionTimestamp: "2023-10-01T00:00:00Z"
   975    spec:
   976      nodeName: minikube
   977      containers:
   978        - name: container
   979    status:
   980      phase: Running
   981      containerStatuses:
   982        - ready: false
   983          restartCount: 0
   984          state:
   985            running: {}
   986  `)
   987  		info := &ResourceInfo{}
   988  		populateNodeInfo(pod, info, []string{})
   989  		assert.Equal(t, []v1alpha1.InfoItem{
   990  			{Name: "Status Reason", Value: "Terminating"},
   991  			{Name: "Node", Value: "minikube"},
   992  			{Name: "Containers", Value: "0/1"},
   993  			{Name: common.PodRequestsCPU, Value: "0"},
   994  			{Name: common.PodRequestsMEM, Value: "0"},
   995  		}, info.Info)
   996  	})
   997  
   998  	// Test pod condition pending with deletion
   999  	t.Run("TestPodConditionPendingWithDeletion", func(t *testing.T) {
  1000  		t.Parallel()
  1001  
  1002  		pod := strToUnstructured(`
  1003    apiVersion: v1
  1004    kind: Pod
  1005    metadata:
  1006      name: test12
  1007      deletionTimestamp: "2023-10-01T00:00:00Z"
  1008    spec:
  1009      nodeName: minikube
  1010      containers:
  1011        - name: container
  1012    status:
  1013      phase: Pending
  1014  `)
  1015  		info := &ResourceInfo{}
  1016  		populateNodeInfo(pod, info, []string{})
  1017  		assert.Equal(t, []v1alpha1.InfoItem{
  1018  			{Name: "Status Reason", Value: "Terminating"},
  1019  			{Name: "Node", Value: "minikube"},
  1020  			{Name: "Containers", Value: "0/1"},
  1021  			{Name: common.PodRequestsCPU, Value: "0"},
  1022  			{Name: common.PodRequestsMEM, Value: "0"},
  1023  		}, info.Info)
  1024  	})
  1025  
  1026  	// Test PodScheduled condition with reason SchedulingGated
  1027  	t.Run("TestPodScheduledWithSchedulingGated", func(t *testing.T) {
  1028  		t.Parallel()
  1029  
  1030  		pod := strToUnstructured(`
  1031    apiVersion: v1
  1032    kind: Pod
  1033    metadata:
  1034      name: test13
  1035    spec:
  1036      nodeName: minikube
  1037      containers:
  1038        - name: container1
  1039        - name: container2
  1040    status:
  1041      phase: podPhase
  1042      conditions:
  1043        - type: PodScheduled
  1044          status: "False"
  1045          reason: SchedulingGated
  1046  `)
  1047  		info := &ResourceInfo{}
  1048  		populateNodeInfo(pod, info, []string{})
  1049  		assert.Equal(t, []v1alpha1.InfoItem{
  1050  			{Name: "Status Reason", Value: "SchedulingGated"},
  1051  			{Name: "Node", Value: "minikube"},
  1052  			{Name: "Containers", Value: "0/2"},
  1053  			{Name: common.PodRequestsCPU, Value: "0"},
  1054  			{Name: common.PodRequestsMEM, Value: "0"},
  1055  		}, info.Info)
  1056  	})
  1057  }
  1058  
  1059  func TestGetNodeInfo(t *testing.T) {
  1060  	node := strToUnstructured(`
  1061  apiVersion: v1
  1062  kind: Node
  1063  metadata:
  1064    name: minikube
  1065    labels:
  1066      foo: bar
  1067  spec: {}
  1068  status:
  1069    capacity:
  1070      cpu: "6"
  1071      memory: 6091320Ki
  1072    nodeInfo:
  1073      architecture: amd64
  1074      operatingSystem: linux
  1075      osImage: Ubuntu 20.04 LTS
  1076  `)
  1077  
  1078  	info := &ResourceInfo{}
  1079  	populateNodeInfo(node, info, []string{})
  1080  	assert.Equal(t, &NodeInfo{
  1081  		Name:       "minikube",
  1082  		Capacity:   corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("6091320Ki"), corev1.ResourceCPU: resource.MustParse("6")},
  1083  		SystemInfo: corev1.NodeSystemInfo{Architecture: "amd64", OperatingSystem: "linux", OSImage: "Ubuntu 20.04 LTS"},
  1084  		Labels:     map[string]string{"foo": "bar"},
  1085  	}, info.NodeInfo)
  1086  }
  1087  
  1088  func TestGetServiceInfo(t *testing.T) {
  1089  	info := &ResourceInfo{}
  1090  	populateNodeInfo(testService, info, []string{})
  1091  	assert.Empty(t, info.Info)
  1092  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1093  		TargetLabels: map[string]string{"app": "guestbook"},
  1094  		Ingress:      []corev1.LoadBalancerIngress{{Hostname: "localhost"}},
  1095  	}, info.NetworkingInfo)
  1096  }
  1097  
  1098  func TestGetLinkAnnotatedServiceInfo(t *testing.T) {
  1099  	info := &ResourceInfo{}
  1100  	populateNodeInfo(testLinkAnnotatedService, info, []string{})
  1101  	assert.Empty(t, info.Info)
  1102  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1103  		TargetLabels: map[string]string{"app": "guestbook"},
  1104  		Ingress:      []corev1.LoadBalancerIngress{{Hostname: "localhost"}},
  1105  		ExternalURLs: []string{"http://my-grafana.example.com/pre-generated-link"},
  1106  	}, info.NetworkingInfo)
  1107  }
  1108  
  1109  func TestGetIstioVirtualServiceInfo(t *testing.T) {
  1110  	info := &ResourceInfo{}
  1111  	populateNodeInfo(testIstioVirtualService, info, []string{})
  1112  	assert.Empty(t, info.Info)
  1113  	require.NotNil(t, info.NetworkingInfo)
  1114  	require.NotNil(t, info.NetworkingInfo.TargetRefs)
  1115  	assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
  1116  		Kind:      kube.ServiceKind,
  1117  		Name:      "service_full",
  1118  		Namespace: "demo",
  1119  	})
  1120  	assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
  1121  		Kind:      kube.ServiceKind,
  1122  		Name:      "service_namespace",
  1123  		Namespace: "namespace",
  1124  	})
  1125  	assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
  1126  		Kind:      kube.ServiceKind,
  1127  		Name:      "service",
  1128  		Namespace: "demo",
  1129  	})
  1130  }
  1131  
  1132  func TestGetIstioServiceEntryInfo(t *testing.T) {
  1133  	info := &ResourceInfo{}
  1134  	populateNodeInfo(testIstioServiceEntry, info, []string{})
  1135  	assert.Empty(t, info.Info)
  1136  	require.NotNil(t, info.NetworkingInfo)
  1137  	require.NotNil(t, info.NetworkingInfo.TargetRefs)
  1138  	assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
  1139  		Kind: kube.PodKind,
  1140  	})
  1141  
  1142  	assert.Equal(t, map[string]string{
  1143  		"app.kubernetes.io/name": "echo-2",
  1144  	}, info.NetworkingInfo.TargetLabels)
  1145  }
  1146  
  1147  func TestGetIngressInfo(t *testing.T) {
  1148  	tests := []struct {
  1149  		Ingress *unstructured.Unstructured
  1150  	}{
  1151  		{testIngress},
  1152  		{testIngressNetworkingV1},
  1153  	}
  1154  	for _, tc := range tests {
  1155  		info := &ResourceInfo{}
  1156  		populateNodeInfo(tc.Ingress, info, []string{})
  1157  		assert.Empty(t, info.Info)
  1158  		sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
  1159  			return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
  1160  		})
  1161  		assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1162  			Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
  1163  			TargetRefs: []v1alpha1.ResourceRef{{
  1164  				Namespace: "default",
  1165  				Group:     "",
  1166  				Kind:      kube.ServiceKind,
  1167  				Name:      "helm-guestbook",
  1168  			}, {
  1169  				Namespace: "default",
  1170  				Group:     "",
  1171  				Kind:      kube.ServiceKind,
  1172  				Name:      "not-found-service",
  1173  			}},
  1174  			ExternalURLs: []string{"https://helm-guestbook.example.com/"},
  1175  		}, info.NetworkingInfo)
  1176  	}
  1177  }
  1178  
  1179  func TestGetLinkAnnotatedIngressInfo(t *testing.T) {
  1180  	info := &ResourceInfo{}
  1181  	populateNodeInfo(testLinkAnnotatedIngress, info, []string{})
  1182  	assert.Empty(t, info.Info)
  1183  	sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
  1184  		return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
  1185  	})
  1186  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1187  		Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
  1188  		TargetRefs: []v1alpha1.ResourceRef{{
  1189  			Namespace: "default",
  1190  			Group:     "",
  1191  			Kind:      kube.ServiceKind,
  1192  			Name:      "helm-guestbook",
  1193  		}, {
  1194  			Namespace: "default",
  1195  			Group:     "",
  1196  			Kind:      kube.ServiceKind,
  1197  			Name:      "not-found-service",
  1198  		}},
  1199  		ExternalURLs: []string{"http://my-grafana.example.com/ingress-link", "https://helm-guestbook.example.com/"},
  1200  	}, info.NetworkingInfo)
  1201  }
  1202  
  1203  func TestGetIngressInfoWildCardPath(t *testing.T) {
  1204  	info := &ResourceInfo{}
  1205  	populateNodeInfo(testIngressWildCardPath, info, []string{})
  1206  	assert.Empty(t, info.Info)
  1207  	sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
  1208  		return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
  1209  	})
  1210  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1211  		Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
  1212  		TargetRefs: []v1alpha1.ResourceRef{{
  1213  			Namespace: "default",
  1214  			Group:     "",
  1215  			Kind:      kube.ServiceKind,
  1216  			Name:      "helm-guestbook",
  1217  		}, {
  1218  			Namespace: "default",
  1219  			Group:     "",
  1220  			Kind:      kube.ServiceKind,
  1221  			Name:      "not-found-service",
  1222  		}},
  1223  		ExternalURLs: []string{"https://helm-guestbook.example.com/"},
  1224  	}, info.NetworkingInfo)
  1225  }
  1226  
  1227  func TestGetIngressInfoWithoutTls(t *testing.T) {
  1228  	info := &ResourceInfo{}
  1229  	populateNodeInfo(testIngressWithoutTLS, info, []string{})
  1230  	assert.Empty(t, info.Info)
  1231  	sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
  1232  		return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
  1233  	})
  1234  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1235  		Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
  1236  		TargetRefs: []v1alpha1.ResourceRef{{
  1237  			Namespace: "default",
  1238  			Group:     "",
  1239  			Kind:      kube.ServiceKind,
  1240  			Name:      "helm-guestbook",
  1241  		}, {
  1242  			Namespace: "default",
  1243  			Group:     "",
  1244  			Kind:      kube.ServiceKind,
  1245  			Name:      "not-found-service",
  1246  		}},
  1247  		ExternalURLs: []string{"http://helm-guestbook.example.com/"},
  1248  	}, info.NetworkingInfo)
  1249  }
  1250  
  1251  func TestGetIngressInfoWithHost(t *testing.T) {
  1252  	ingress := strToUnstructured(`
  1253    apiVersion: extensions/v1beta1
  1254    kind: Ingress
  1255    metadata:
  1256      name: helm-guestbook
  1257      namespace: default
  1258    spec:
  1259      rules:
  1260      - http:
  1261          paths:
  1262          - backend:
  1263              serviceName: helm-guestbook
  1264              servicePort: 443
  1265            path: /
  1266      tls:
  1267      - secretName: my-tls
  1268    status:
  1269      loadBalancer:
  1270        ingress:
  1271        - ip: 107.178.210.11`)
  1272  
  1273  	info := &ResourceInfo{}
  1274  	populateNodeInfo(ingress, info, []string{})
  1275  
  1276  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1277  		Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
  1278  		TargetRefs: []v1alpha1.ResourceRef{{
  1279  			Namespace: "default",
  1280  			Group:     "",
  1281  			Kind:      kube.ServiceKind,
  1282  			Name:      "helm-guestbook",
  1283  		}},
  1284  		ExternalURLs: []string{"https://107.178.210.11/"},
  1285  	}, info.NetworkingInfo)
  1286  }
  1287  
  1288  func TestGetIngressInfoNoHost(t *testing.T) {
  1289  	ingress := strToUnstructured(`
  1290    apiVersion: extensions/v1beta1
  1291    kind: Ingress
  1292    metadata:
  1293      name: helm-guestbook
  1294      namespace: default
  1295    spec:
  1296      rules:
  1297      - http:
  1298          paths:
  1299          - backend:
  1300              serviceName: helm-guestbook
  1301              servicePort: 443
  1302            path: /
  1303      tls:
  1304      - secretName: my-tls
  1305        `)
  1306  
  1307  	info := &ResourceInfo{}
  1308  	populateNodeInfo(ingress, info, []string{})
  1309  
  1310  	assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
  1311  		TargetRefs: []v1alpha1.ResourceRef{{
  1312  			Namespace: "default",
  1313  			Group:     "",
  1314  			Kind:      kube.ServiceKind,
  1315  			Name:      "helm-guestbook",
  1316  		}},
  1317  	}, info.NetworkingInfo)
  1318  	assert.Empty(t, info.NetworkingInfo.ExternalURLs)
  1319  }
  1320  
  1321  func TestExternalUrlWithSubPath(t *testing.T) {
  1322  	ingress := strToUnstructured(`
  1323    apiVersion: networking.k8s.io/v1
  1324    kind: Ingress
  1325    metadata:
  1326      name: helm-guestbook
  1327      namespace: default
  1328    spec:
  1329      rules:
  1330      - http:
  1331          paths:
  1332          - backend:
  1333              serviceName: helm-guestbook
  1334              servicePort: 443
  1335            path: /my/sub/path/
  1336      tls:
  1337      - secretName: my-tls
  1338    status:
  1339      loadBalancer:
  1340        ingress:
  1341        - ip: 107.178.210.11`)
  1342  
  1343  	info := &ResourceInfo{}
  1344  	populateNodeInfo(ingress, info, []string{})
  1345  
  1346  	expectedExternalUrls := []string{"https://107.178.210.11/my/sub/path/"}
  1347  	assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
  1348  }
  1349  
  1350  func TestExternalUrlWithMultipleSubPaths(t *testing.T) {
  1351  	ingress := strToUnstructured(`
  1352    apiVersion: networking.k8s.io/v1
  1353    kind: Ingress
  1354    metadata:
  1355      name: helm-guestbook
  1356      namespace: default
  1357    spec:
  1358      rules:
  1359      - host: helm-guestbook.example.com
  1360        http:
  1361          paths:
  1362          - backend:
  1363              serviceName: helm-guestbook
  1364              servicePort: 443
  1365            path: /my/sub/path/
  1366          - backend:
  1367              serviceName: helm-guestbook-2
  1368              servicePort: 443
  1369            path: /my/sub/path/2
  1370          - backend:
  1371              serviceName: helm-guestbook-3
  1372              servicePort: 443
  1373      tls:
  1374      - secretName: my-tls
  1375    status:
  1376      loadBalancer:
  1377        ingress:
  1378        - ip: 107.178.210.11`)
  1379  
  1380  	info := &ResourceInfo{}
  1381  	populateNodeInfo(ingress, info, []string{})
  1382  
  1383  	expectedExternalUrls := []string{"https://helm-guestbook.example.com/my/sub/path/", "https://helm-guestbook.example.com/my/sub/path/2", "https://helm-guestbook.example.com"}
  1384  	actualURLs := info.NetworkingInfo.ExternalURLs
  1385  	sort.Strings(expectedExternalUrls)
  1386  	sort.Strings(actualURLs)
  1387  	assert.Equal(t, expectedExternalUrls, actualURLs)
  1388  }
  1389  
  1390  func TestExternalUrlWithNoSubPath(t *testing.T) {
  1391  	ingress := strToUnstructured(`
  1392    apiVersion: networking.k8s.io/v1
  1393    kind: Ingress
  1394    metadata:
  1395      name: helm-guestbook
  1396      namespace: default
  1397    spec:
  1398      rules:
  1399      - http:
  1400          paths:
  1401          - backend:
  1402              serviceName: helm-guestbook
  1403              servicePort: 443
  1404      tls:
  1405      - secretName: my-tls
  1406    status:
  1407      loadBalancer:
  1408        ingress:
  1409        - ip: 107.178.210.11`)
  1410  
  1411  	info := &ResourceInfo{}
  1412  	populateNodeInfo(ingress, info, []string{})
  1413  
  1414  	expectedExternalUrls := []string{"https://107.178.210.11"}
  1415  	assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
  1416  }
  1417  
  1418  func TestExternalUrlWithNetworkingApi(t *testing.T) {
  1419  	ingress := strToUnstructured(`
  1420    apiVersion: networking.k8s.io/v1beta1
  1421    kind: Ingress
  1422    metadata:
  1423      name: helm-guestbook
  1424      namespace: default
  1425    spec:
  1426      rules:
  1427      - http:
  1428          paths:
  1429          - backend:
  1430              serviceName: helm-guestbook
  1431              servicePort: 443
  1432      tls:
  1433      - secretName: my-tls
  1434    status:
  1435      loadBalancer:
  1436        ingress:
  1437        - ip: 107.178.210.11`)
  1438  
  1439  	info := &ResourceInfo{}
  1440  	populateNodeInfo(ingress, info, []string{})
  1441  
  1442  	expectedExternalUrls := []string{"https://107.178.210.11"}
  1443  	assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
  1444  }
  1445  
  1446  func TestCustomLabel(t *testing.T) {
  1447  	configmap := strToUnstructured(`
  1448    apiVersion: v1
  1449    kind: ConfigMap
  1450    metadata:
  1451      name: cm`)
  1452  
  1453  	info := &ResourceInfo{}
  1454  	populateNodeInfo(configmap, info, []string{"my-label"})
  1455  
  1456  	assert.Empty(t, info.Info)
  1457  
  1458  	configmap = strToUnstructured(`
  1459    apiVersion: v1
  1460    kind: ConfigMap
  1461    metadata:
  1462      name: cm
  1463      labels:
  1464        my-label: value`)
  1465  
  1466  	info = &ResourceInfo{}
  1467  	populateNodeInfo(configmap, info, []string{"my-label", "other-label"})
  1468  
  1469  	assert.Len(t, info.Info, 1)
  1470  	assert.Equal(t, "my-label", info.Info[0].Name)
  1471  	assert.Equal(t, "value", info.Info[0].Value)
  1472  
  1473  	configmap = strToUnstructured(`
  1474    apiVersion: v1
  1475    kind: ConfigMap
  1476    metadata:
  1477      name: cm
  1478      labels:
  1479        my-label: value
  1480        other-label: value2`)
  1481  
  1482  	info = &ResourceInfo{}
  1483  	populateNodeInfo(configmap, info, []string{"my-label", "other-label"})
  1484  
  1485  	assert.Len(t, info.Info, 2)
  1486  	assert.Equal(t, "my-label", info.Info[0].Name)
  1487  	assert.Equal(t, "value", info.Info[0].Value)
  1488  	assert.Equal(t, "other-label", info.Info[1].Name)
  1489  	assert.Equal(t, "value2", info.Info[1].Value)
  1490  }
  1491  
  1492  func TestManifestHash(t *testing.T) {
  1493  	manifest := strToUnstructured(`
  1494    apiVersion: v1
  1495    kind: Pod
  1496    metadata:
  1497      name: helm-guestbook-pod
  1498      namespace: default
  1499      ownerReferences:
  1500      - apiVersion: extensions/v1beta1
  1501        kind: ReplicaSet
  1502        name: helm-guestbook-rs
  1503      resourceVersion: "123"
  1504      labels:
  1505        app: guestbook
  1506    spec:
  1507      nodeName: minikube
  1508      containers:
  1509      - image: bar
  1510        resources:
  1511          requests:
  1512            memory: 128Mi
  1513  `)
  1514  
  1515  	ignores := []v1alpha1.ResourceIgnoreDifferences{
  1516  		{
  1517  			Group:        "*",
  1518  			Kind:         "*",
  1519  			JSONPointers: []string{"/metadata/resourceVersion"},
  1520  		},
  1521  	}
  1522  
  1523  	data, _ := strToUnstructured(`
  1524    apiVersion: v1
  1525    kind: Pod
  1526    metadata:
  1527      name: helm-guestbook-pod
  1528      namespace: default
  1529      ownerReferences:
  1530      - apiVersion: extensions/v1beta1
  1531        kind: ReplicaSet
  1532        name: helm-guestbook-rs
  1533      labels:
  1534        app: guestbook
  1535    spec:
  1536      nodeName: minikube
  1537      containers:
  1538      - image: bar
  1539        resources:
  1540          requests:
  1541            memory: 128Mi
  1542  `).MarshalJSON()
  1543  
  1544  	expected := hash(data)
  1545  
  1546  	hash, err := generateManifestHash(manifest, ignores, nil, normalizers.IgnoreNormalizerOpts{})
  1547  	assert.Equal(t, expected, hash)
  1548  	assert.NoError(t, err)
  1549  }